OSDN Git Service

Merge "Fix crash due to reverse selection." into mnc-dev
authorSeigo Nonaka <nona@google.com>
Mon, 29 Jun 2015 03:02:42 +0000 (03:02 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Mon, 29 Jun 2015 03:02:45 +0000 (03:02 +0000)
147 files changed:
api/current.txt
api/system-current.txt
cmds/pm/src/com/android/commands/pm/Pm.java
core/java/android/app/assist/AssistStructure.java
core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
core/java/android/content/Intent.java
core/java/android/content/IntentFilter.java
core/java/android/content/pm/IPackageManager.aidl
core/java/android/hardware/camera2/CameraCharacteristics.java
core/java/android/hardware/camera2/CameraDevice.java
core/java/android/hardware/camera2/CameraMetadata.java
core/java/android/hardware/camera2/CaptureRequest.java
core/java/android/hardware/camera2/CaptureResult.java
core/java/android/hardware/fingerprint/IFingerprintService.aidl
core/java/android/inputmethodservice/InputMethodService.java
core/java/android/os/Build.java
core/java/android/os/Process.java
core/java/android/os/storage/IMountService.java
core/java/android/os/storage/StorageManager.java
core/java/android/provider/MediaStore.java
core/java/android/service/notification/NotificationListenerService.java
core/java/android/text/StaticLayout.java
core/java/android/text/format/Formatter.java
core/java/android/text/method/WordIterator.java
core/java/android/transition/Transition.java
core/java/android/transition/TransitionManager.java
core/java/android/util/LocalLog.java
core/java/android/view/View.java
core/java/android/view/ViewGroup.java
core/java/android/view/ViewStructure.java
core/java/android/widget/AbsListView.java
core/java/android/widget/Editor.java
core/java/android/widget/FastScroller.java
core/java/android/widget/FrameLayout.java
core/java/android/widget/LinearLayout.java
core/java/android/widget/ListPopupWindow.java
core/java/android/widget/Switch.java
core/java/android/widget/TextView.java
core/java/com/android/internal/os/Zygote.java
core/java/com/android/internal/os/ZygoteConnection.java
core/java/com/android/internal/os/storage/ExternalStorageFormatter.java
core/java/com/android/internal/view/FloatingActionMode.java
core/java/com/android/internal/widget/FloatingToolbar.java
core/jni/android_util_Binder.cpp
core/jni/com_android_internal_content_NativeLibraryHelper.cpp
core/jni/com_android_internal_os_Zygote.cpp
core/res/AndroidManifest.xml
core/res/res/layout/floating_popup_menu_button.xml
core/res/res/values/attrs.xml
core/res/res/values/config.xml
core/res/res/values/strings.xml
core/res/res/values/symbols.xml
core/res/res/values/themes.xml
core/res/res/xml/sms_short_codes.xml
core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
core/tests/coretests/src/android/text/format/FormatterTest.java [new file with mode: 0644]
data/etc/platform.xml
docs/html/guide/topics/security/permissions.jd
docs/html/images/tools/eclipse-notepad-pre-import--structure.png [new file with mode: 0644]
docs/html/images/tools/studio-import-destination-dir.png [new file with mode: 0644]
docs/html/images/tools/studio-import-options.png [new file with mode: 0644]
docs/html/images/tools/studio-import-project-structure-android.png [new file with mode: 0644]
docs/html/images/tools/studio-import-project-structure-project.png [new file with mode: 0644]
docs/html/images/tools/studio-import-summary.png [new file with mode: 0644]
docs/html/images/tools/studio-select-project-forimport.png [new file with mode: 0644]
docs/html/sdk/installing/installing-adt.jd
docs/html/sdk/installing/migrate.jd
docs/html/tools/building/building-cmdline-ant.jd
docs/html/tools/building/manifest-merge.jd [new file with mode: 0644]
docs/html/tools/help/adt.jd
docs/html/tools/studio/eclipse-transition-guide.jd [new file with mode: 0644]
docs/html/tools/studio/index.jd
docs/html/tools/tools_toc.cs
docs/html/training/volley/requestqueue.jd
include/androidfw/ZipFileRO.h
keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
libs/androidfw/ZipFileRO.cpp
libs/hwui/Outline.h
libs/hwui/RenderProperties.h
libs/hwui/ShadowTessellator.cpp
media/java/android/media/AudioManager.java
media/java/android/media/MediaCodecInfo.java
packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
packages/SystemUI/res/values/dimens.xml
packages/SystemUI/src/com/android/systemui/Prefs.java
packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
packages/SystemUI/src/com/android/systemui/recents/Recents.java
packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHostView.java
packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
services/core/java/com/android/server/AlarmManagerService.java
services/core/java/com/android/server/ConnectivityService.java
services/core/java/com/android/server/MasterClearReceiver.java
services/core/java/com/android/server/MountService.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/ActivityStackSupervisor.java
services/core/java/com/android/server/am/BatteryStatsService.java
services/core/java/com/android/server/connectivity/NetworkMonitor.java
services/core/java/com/android/server/fingerprint/FingerprintService.java
services/core/java/com/android/server/location/GpsLocationProvider.java
services/core/java/com/android/server/net/NetworkPolicyManagerService.java
services/core/java/com/android/server/notification/NotificationManagerService.java
services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
services/core/java/com/android/server/pm/PackageManagerService.java
services/core/java/com/android/server/pm/PermissionsState.java
services/core/java/com/android/server/pm/Settings.java
services/core/java/com/android/server/power/PowerManagerService.java
services/core/java/com/android/server/wm/WindowAnimator.java
services/core/jni/com_android_server_am_BatteryStatsService.cpp
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
telephony/java/android/telephony/CarrierConfigManager.java
telephony/java/android/telephony/TelephonyManager.java
telephony/java/com/android/internal/telephony/CellNetworkScanResult.java
tests/VoiceInteraction/res/layout/voice_interaction_session.xml
tests/VoiceInteraction/res/values/strings.xml
tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
tests/WebViewTests/Android.mk [deleted file]
tests/WebViewTests/AndroidManifest.xml [deleted file]
tests/WebViewTests/res/layout/webview_layout.xml [deleted file]
tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java [deleted file]
tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java [deleted file]
tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java [deleted file]
tests/WebViewTests/src/com/android/webviewtests/JavaBridgeChildFrameTest.java [deleted file]
tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java [deleted file]
tests/WebViewTests/src/com/android/webviewtests/JavaBridgeFieldsTest.java [deleted file]
tests/WebViewTests/src/com/android/webviewtests/JavaBridgeReturnValuesTest.java [deleted file]
tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java [deleted file]
tests/WebViewTests/src/com/android/webviewtests/WebViewStubActivity.java [deleted file]
wifi/java/android/net/wifi/WifiInfo.java

index d951591..b609b83 100644 (file)
@@ -10,10 +10,8 @@ package android {
     field public static final java.lang.String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
     field public static final java.lang.String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
     field public static final java.lang.String ACCESS_LOCATION_EXTRA_COMMANDS = "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS";
-    field public static final java.lang.String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
     field public static final java.lang.String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE";
     field public static final java.lang.String ACCESS_NOTIFICATION_POLICY = "android.permission.ACCESS_NOTIFICATION_POLICY";
-    field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
     field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
     field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
     field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL";
@@ -42,7 +40,6 @@ package android {
     field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN";
     field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED";
     field public static final java.lang.String BODY_SENSORS = "android.permission.BODY_SENSORS";
-    field public static final java.lang.String BRICK = "android.permission.BRICK";
     field public static final java.lang.String BROADCAST_PACKAGE_REMOVED = "android.permission.BROADCAST_PACKAGE_REMOVED";
     field public static final java.lang.String BROADCAST_SMS = "android.permission.BROADCAST_SMS";
     field public static final java.lang.String BROADCAST_STICKY = "android.permission.BROADCAST_STICKY";
@@ -59,33 +56,25 @@ package android {
     field public static final java.lang.String CHANGE_WIFI_MULTICAST_STATE = "android.permission.CHANGE_WIFI_MULTICAST_STATE";
     field public static final java.lang.String CHANGE_WIFI_STATE = "android.permission.CHANGE_WIFI_STATE";
     field public static final java.lang.String CLEAR_APP_CACHE = "android.permission.CLEAR_APP_CACHE";
-    field public static final java.lang.String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
     field public static final java.lang.String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES";
     field public static final java.lang.String DELETE_CACHE_FILES = "android.permission.DELETE_CACHE_FILES";
     field public static final java.lang.String DELETE_PACKAGES = "android.permission.DELETE_PACKAGES";
-    field public static final java.lang.String DEVICE_POWER = "android.permission.DEVICE_POWER";
     field public static final java.lang.String DIAGNOSTIC = "android.permission.DIAGNOSTIC";
     field public static final java.lang.String DISABLE_KEYGUARD = "android.permission.DISABLE_KEYGUARD";
     field public static final java.lang.String DUMP = "android.permission.DUMP";
     field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
     field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
     field public static final java.lang.String FLASHLIGHT = "android.permission.FLASHLIGHT";
-    field public static final java.lang.String FORCE_BACK = "android.permission.FORCE_BACK";
     field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
     field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
     field public static final deprecated java.lang.String GET_TASKS = "android.permission.GET_TASKS";
-    field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
     field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
-    field public static final java.lang.String HARDWARE_TEST = "android.permission.HARDWARE_TEST";
-    field public static final java.lang.String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
     field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
     field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
     field public static final java.lang.String INSTALL_SHORTCUT = "com.android.launcher.permission.INSTALL_SHORTCUT";
-    field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
     field public static final java.lang.String INTERNET = "android.permission.INTERNET";
     field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
     field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
-    field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
     field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
     field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
     field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
@@ -120,13 +109,10 @@ package android {
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
     field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
-    field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
     field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
     field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
     field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
     field public static final java.lang.String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
-    field public static final java.lang.String SET_ORIENTATION = "android.permission.SET_ORIENTATION";
-    field public static final java.lang.String SET_POINTER_SPEED = "android.permission.SET_POINTER_SPEED";
     field public static final deprecated java.lang.String SET_PREFERRED_APPLICATIONS = "android.permission.SET_PREFERRED_APPLICATIONS";
     field public static final java.lang.String SET_PROCESS_LIMIT = "android.permission.SET_PROCESS_LIMIT";
     field public static final java.lang.String SET_TIME = "android.permission.SET_TIME";
@@ -5846,10 +5832,12 @@ package android.app.assist {
   }
 
   public static class AssistStructure.ViewNode {
+    method public float getAlpha();
     method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
     method public int getChildCount();
     method public java.lang.String getClassName();
     method public java.lang.CharSequence getContentDescription();
+    method public float getElevation();
     method public android.os.Bundle getExtras();
     method public int getHeight();
     method public java.lang.String getHint();
@@ -5868,6 +5856,7 @@ package android.app.assist {
     method public float getTextSize();
     method public int getTextStyle();
     method public int getTop();
+    method public android.graphics.Matrix getTransformation();
     method public int getVisibility();
     method public int getWidth();
     method public boolean isAccessibilityFocused();
@@ -22711,7 +22700,7 @@ package android.os {
     field public static final int KITKAT_WATCH = 20; // 0x14
     field public static final int LOLLIPOP = 21; // 0x15
     field public static final int LOLLIPOP_MR1 = 22; // 0x16
-    field public static final int MNC = 10000; // 0x2710
+    field public static final int MNC = 23; // 0x17
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -30543,8 +30532,8 @@ package android.telephony {
     field public static final java.lang.String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
     field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
     field public static final java.lang.String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
-    field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
     field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
+    field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
     field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
     field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
@@ -30553,6 +30542,7 @@ package android.telephony {
     field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
+    field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
     field public static final java.lang.String KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY = "gsm_roaming_networks_string_array";
     field public static final java.lang.String KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL = "has_in_call_noise_suppression_bool";
@@ -31090,7 +31080,7 @@ package android.telephony {
     method public java.lang.String getLine1Number();
     method public java.lang.String getMmsUAProfUrl();
     method public java.lang.String getMmsUserAgent();
-    method public java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
+    method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
     method public java.lang.String getNetworkCountryIso();
     method public java.lang.String getNetworkOperator();
     method public java.lang.String getNetworkOperatorName();
@@ -36961,6 +36951,7 @@ package android.view {
     method public abstract android.view.ViewStructure newChild(int);
     method public abstract void setAccessibilityFocused(boolean);
     method public abstract void setActivated(boolean);
+    method public abstract void setAlpha(float);
     method public abstract void setCheckable(boolean);
     method public abstract void setChecked(boolean);
     method public abstract void setChildCount(int);
@@ -36969,6 +36960,7 @@ package android.view {
     method public abstract void setContentDescription(java.lang.CharSequence);
     method public abstract void setContextClickable(boolean);
     method public abstract void setDimens(int, int, int, int, int, int);
+    method public abstract void setElevation(float);
     method public abstract void setEnabled(boolean);
     method public abstract void setFocusable(boolean);
     method public abstract void setFocused(boolean);
@@ -36979,6 +36971,7 @@ package android.view {
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
     method public abstract void setTextStyle(float, int, int, int);
+    method public abstract void setTransformation(android.graphics.Matrix);
     method public abstract void setVisibility(int);
   }
 
index 78e0a16..7e07e48 100644 (file)
@@ -5964,10 +5964,12 @@ package android.app.assist {
   }
 
   public static class AssistStructure.ViewNode {
+    method public float getAlpha();
     method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
     method public int getChildCount();
     method public java.lang.String getClassName();
     method public java.lang.CharSequence getContentDescription();
+    method public float getElevation();
     method public android.os.Bundle getExtras();
     method public int getHeight();
     method public java.lang.String getHint();
@@ -5986,6 +5988,7 @@ package android.app.assist {
     method public float getTextSize();
     method public int getTextStyle();
     method public int getTop();
+    method public android.graphics.Matrix getTransformation();
     method public int getVisibility();
     method public int getWidth();
     method public boolean isAccessibilityFocused();
@@ -24644,7 +24647,7 @@ package android.os {
     field public static final int KITKAT_WATCH = 20; // 0x14
     field public static final int LOLLIPOP = 21; // 0x15
     field public static final int LOLLIPOP_MR1 = 22; // 0x16
-    field public static final int MNC = 10000; // 0x2710
+    field public static final int MNC = 23; // 0x17
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -32766,8 +32769,8 @@ package android.telephony {
     field public static final java.lang.String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
     field public static final java.lang.String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool";
     field public static final java.lang.String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool";
-    field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
     field public static final java.lang.String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
+    field public static final java.lang.String KEY_CARRIER_VVM_PACKAGE_NAME_STRING = "carrier_vvm_package_name_string";
     field public static final java.lang.String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
     field public static final java.lang.String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
     field public static final java.lang.String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
@@ -32776,6 +32779,7 @@ package android.telephony {
     field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
+    field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
     field public static final java.lang.String KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY = "gsm_roaming_networks_string_array";
     field public static final java.lang.String KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL = "has_in_call_noise_suppression_bool";
@@ -33332,7 +33336,7 @@ package android.telephony {
     method public java.lang.String getLine1Number();
     method public java.lang.String getMmsUAProfUrl();
     method public java.lang.String getMmsUserAgent();
-    method public java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
+    method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
     method public java.lang.String getNetworkCountryIso();
     method public java.lang.String getNetworkOperator();
     method public java.lang.String getNetworkOperatorName();
@@ -39242,6 +39246,7 @@ package android.view {
     method public abstract android.view.ViewStructure newChild(int);
     method public abstract void setAccessibilityFocused(boolean);
     method public abstract void setActivated(boolean);
+    method public abstract void setAlpha(float);
     method public abstract void setCheckable(boolean);
     method public abstract void setChecked(boolean);
     method public abstract void setChildCount(int);
@@ -39250,6 +39255,7 @@ package android.view {
     method public abstract void setContentDescription(java.lang.CharSequence);
     method public abstract void setContextClickable(boolean);
     method public abstract void setDimens(int, int, int, int, int, int);
+    method public abstract void setElevation(float);
     method public abstract void setEnabled(boolean);
     method public abstract void setFocusable(boolean);
     method public abstract void setFocused(boolean);
@@ -39260,6 +39266,7 @@ package android.view {
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
     method public abstract void setTextStyle(float, int, int, int);
+    method public abstract void setTransformation(android.graphics.Matrix);
     method public abstract void setVisibility(int);
   }
 
index 2be44bc..ce83caa 100644 (file)
@@ -204,6 +204,10 @@ public final class Pm {
             return runGrantRevokePermission(false);
         }
 
+        if ("reset-permissions".equals(op)) {
+            return runResetPermissions();
+        }
+
         if ("set-permission-enforced".equals(op)) {
             return runSetPermissionEnforced();
         }
@@ -1636,6 +1640,24 @@ public final class Pm {
         }
     }
 
+    private int runResetPermissions() {
+        try {
+            mPm.resetRuntimePermissions();
+            return 0;
+        } catch (RemoteException e) {
+            System.err.println(e.toString());
+            System.err.println(PM_NOT_RUNNING_ERR);
+            return 1;
+        } catch (IllegalArgumentException e) {
+            System.err.println("Bad argument: " + e.toString());
+            showUsage();
+            return 1;
+        } catch (SecurityException e) {
+            System.err.println("Operation not allowed: " + e.toString());
+            return 1;
+        }
+    }
+
     private int runSetPermissionEnforced() {
         final String permission = nextArg();
         if (permission == null) {
@@ -1911,6 +1933,7 @@ public final class Pm {
         System.err.println("       pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT");
         System.err.println("       pm grant [--user USER_ID] PACKAGE PERMISSION");
         System.err.println("       pm revoke [--user USER_ID] PACKAGE PERMISSION");
+        System.err.println("       pm reset-permissions");
         System.err.println("       pm set-install-location [0/auto] [1/internal] [2/external]");
         System.err.println("       pm get-install-location");
         System.err.println("       pm set-permission-enforced PERMISSION [true|false]");
@@ -1988,6 +2011,8 @@ public final class Pm {
         System.err.println("    manifest, be runtime permissions (protection level dangerous),");
         System.err.println("    and the app targeting SDK greater than Lollipop MR1.");
         System.err.println("");
+        System.err.println("pm reset-permissions: revert all runtime permissions to their default state.");
+        System.err.println("");
         System.err.println("pm get-install-location: returns the current install location.");
         System.err.println("    0 [auto]: Let system decide the best location");
         System.err.println("    1 [internal]: Install on internal device storage");
index 284dfd6..3429b6e 100644 (file)
@@ -2,6 +2,7 @@ package android.app.assist;
 
 import android.app.Activity;
 import android.content.ComponentName;
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
@@ -128,24 +129,24 @@ public class AssistStructure implements Parcelable {
             view.dispatchProvideStructure(builder);
         }
 
-        WindowNode(Parcel in, PooledStringReader preader) {
+        WindowNode(Parcel in, PooledStringReader preader, float[] tmpMatrix) {
             mX = in.readInt();
             mY = in.readInt();
             mWidth = in.readInt();
             mHeight = in.readInt();
             mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             mDisplayId = in.readInt();
-            mRoot = new ViewNode(in, preader);
+            mRoot = new ViewNode(in, preader, tmpMatrix);
         }
 
-        void writeToParcel(Parcel out, PooledStringWriter pwriter) {
+        int writeToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
             out.writeInt(mX);
             out.writeInt(mY);
             out.writeInt(mWidth);
             out.writeInt(mHeight);
             TextUtils.writeToParcel(mTitle, out, 0);
             out.writeInt(mDisplayId);
-            mRoot.writeToParcel(out, pwriter);
+            return mRoot.writeToParcel(out, pwriter, tmpMatrix);
         }
 
         /**
@@ -216,7 +217,7 @@ public class AssistStructure implements Parcelable {
         public static final int TEXT_STYLE_UNDERLINE = 1<<2;
         public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
 
-        int mId;
+        int mId = View.NO_ID;
         String mIdPackage;
         String mIdType;
         String mIdEntry;
@@ -226,20 +227,35 @@ public class AssistStructure implements Parcelable {
         int mScrollY;
         int mWidth;
         int mHeight;
+        Matrix mMatrix;
+        float mElevation;
+        float mAlpha = 1.0f;
 
         static final int FLAGS_DISABLED = 0x00000001;
         static final int FLAGS_VISIBILITY_MASK = View.VISIBLE|View.INVISIBLE|View.GONE;
         static final int FLAGS_FOCUSABLE = 0x00000010;
         static final int FLAGS_FOCUSED = 0x00000020;
-        static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x04000000;
         static final int FLAGS_SELECTED = 0x00000040;
         static final int FLAGS_ASSIST_BLOCKED = 0x00000080;
-        static final int FLAGS_ACTIVATED = 0x40000000;
         static final int FLAGS_CHECKABLE = 0x00000100;
         static final int FLAGS_CHECKED = 0x00000200;
-        static final int FLAGS_CLICKABLE = 0x00004000;
-        static final int FLAGS_LONG_CLICKABLE = 0x00200000;
-        static final int FLAGS_CONTEXT_CLICKABLE = 0x00400000;
+        static final int FLAGS_CLICKABLE = 0x00000400;
+        static final int FLAGS_LONG_CLICKABLE = 0x00000800;
+        static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x00001000;
+        static final int FLAGS_ACTIVATED = 0x00002000;
+        static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
+
+        static final int FLAGS_HAS_MATRIX = 0x40000000;
+        static final int FLAGS_HAS_ALPHA = 0x20000000;
+        static final int FLAGS_HAS_ELEVATION = 0x10000000;
+        static final int FLAGS_HAS_SCROLL = 0x08000000;
+        static final int FLAGS_HAS_LARGE_COORDS = 0x04000000;
+        static final int FLAGS_HAS_CONTENT_DESCRIPTION = 0x02000000;
+        static final int FLAGS_HAS_TEXT = 0x01000000;
+        static final int FLAGS_HAS_EXTRAS = 0x00800000;
+        static final int FLAGS_HAS_ID = 0x00400000;
+        static final int FLAGS_HAS_CHILDREN = 0x00200000;
+        static final int FLAGS_ALL_CONTROL = 0xfff00000;
 
         int mFlags;
 
@@ -254,79 +270,153 @@ public class AssistStructure implements Parcelable {
         ViewNode() {
         }
 
-        ViewNode(Parcel in, PooledStringReader preader) {
-            mId = in.readInt();
-            if (mId != 0) {
-                mIdEntry = preader.readString();
-                if (mIdEntry != null) {
-                    mIdType = preader.readString();
-                    mIdPackage = preader.readString();
-                } else {
-                    mIdPackage = mIdType = null;
+        ViewNode(Parcel in, PooledStringReader preader, float[] tmpMatrix) {
+            mClassName = preader.readString();
+            mFlags = in.readInt();
+            final int flags = mFlags;
+            if ((flags&FLAGS_HAS_ID) != 0) {
+                mId = in.readInt();
+                if (mId != 0) {
+                    mIdEntry = preader.readString();
+                    if (mIdEntry != null) {
+                        mIdType = preader.readString();
+                        mIdPackage = preader.readString();
+                    }
                 }
+            }
+            if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
+                mX = in.readInt();
+                mY = in.readInt();
+                mWidth = in.readInt();
+                mHeight = in.readInt();
             } else {
-                mIdPackage = mIdType = mIdEntry = null;
+                int val = in.readInt();
+                mX = val&0x7fff;
+                mY = (val>>16)&0x7fff;
+                val = in.readInt();
+                mWidth = val&0x7fff;
+                mHeight = (val>>16)&0x7fff;
             }
-            mX = in.readInt();
-            mY = in.readInt();
-            mScrollX = in.readInt();
-            mScrollY = in.readInt();
-            mWidth = in.readInt();
-            mHeight = in.readInt();
-            mFlags = in.readInt();
-            mClassName = preader.readString();
-            mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-            if (in.readInt() != 0) {
+            if ((flags&FLAGS_HAS_SCROLL) != 0) {
+                mScrollX = in.readInt();
+                mScrollY = in.readInt();
+            }
+            if ((flags&FLAGS_HAS_MATRIX) != 0) {
+                mMatrix = new Matrix();
+                in.readFloatArray(tmpMatrix);
+                mMatrix.setValues(tmpMatrix);
+            }
+            if ((flags&FLAGS_HAS_ELEVATION) != 0) {
+                mElevation = in.readFloat();
+            }
+            if ((flags&FLAGS_HAS_ALPHA) != 0) {
+                mAlpha = in.readFloat();
+            }
+            if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
+                mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            }
+            if ((flags&FLAGS_HAS_TEXT) != 0) {
                 mText = new ViewNodeText(in);
-            } else {
-                mText = null;
             }
-            mExtras = in.readBundle();
-            final int NCHILDREN = in.readInt();
-            if (NCHILDREN > 0) {
+            if ((flags&FLAGS_HAS_EXTRAS) != 0) {
+                mExtras = in.readBundle();
+            }
+            if ((flags&FLAGS_HAS_CHILDREN) != 0) {
+                final int NCHILDREN = in.readInt();
                 mChildren = new ViewNode[NCHILDREN];
                 for (int i=0; i<NCHILDREN; i++) {
-                    mChildren[i] = new ViewNode(in, preader);
+                    mChildren[i] = new ViewNode(in, preader, tmpMatrix);
                 }
-            } else {
-                mChildren = null;
             }
         }
 
-        void writeToParcel(Parcel out, PooledStringWriter pwriter) {
-            out.writeInt(mId);
-            if (mId != 0) {
-                pwriter.writeString(mIdEntry);
-                if (mIdEntry != null) {
-                    pwriter.writeString(mIdType);
-                    pwriter.writeString(mIdPackage);
-                }
+        int writeToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
+            int flags = mFlags & ~FLAGS_ALL_CONTROL;
+            if (mId != View.NO_ID) {
+                flags |= FLAGS_HAS_ID;
+            }
+            if ((mX&~0x7fff) != 0 || (mY&~0x7fff) != 0
+                    || (mWidth&~0x7fff) != 0 | (mHeight&~0x7fff) != 0) {
+                flags |= FLAGS_HAS_LARGE_COORDS;
+            }
+            if (mScrollX != 0 || mScrollY != 0) {
+                flags |= FLAGS_HAS_SCROLL;
+            }
+            if (mMatrix != null) {
+                flags |= FLAGS_HAS_MATRIX;
+            }
+            if (mElevation != 0) {
+                flags |= FLAGS_HAS_ELEVATION;
+            }
+            if (mAlpha != 1.0f) {
+                flags |= FLAGS_HAS_ALPHA;
+            }
+            if (mContentDescription != null) {
+                flags |= FLAGS_HAS_CONTENT_DESCRIPTION;
             }
-            out.writeInt(mX);
-            out.writeInt(mY);
-            out.writeInt(mScrollX);
-            out.writeInt(mScrollY);
-            out.writeInt(mWidth);
-            out.writeInt(mHeight);
-            out.writeInt(mFlags);
-            pwriter.writeString(mClassName);
-            TextUtils.writeToParcel(mContentDescription, out, 0);
             if (mText != null) {
-                out.writeInt(1);
-                mText.writeToParcel(out);
-            } else {
-                out.writeInt(0);
+                flags |= FLAGS_HAS_TEXT;
+            }
+            if (mExtras != null) {
+                flags |= FLAGS_HAS_EXTRAS;
             }
-            out.writeBundle(mExtras);
             if (mChildren != null) {
+                flags |= FLAGS_HAS_CHILDREN;
+            }
+
+            pwriter.writeString(mClassName);
+            out.writeInt(flags);
+            if ((flags&FLAGS_HAS_ID) != 0) {
+                out.writeInt(mId);
+                if (mId != 0) {
+                    pwriter.writeString(mIdEntry);
+                    if (mIdEntry != null) {
+                        pwriter.writeString(mIdType);
+                        pwriter.writeString(mIdPackage);
+                    }
+                }
+            }
+            if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
+                out.writeInt(mX);
+                out.writeInt(mY);
+                out.writeInt(mWidth);
+                out.writeInt(mHeight);
+            } else {
+                out.writeInt((mY<<16) | mX);
+                out.writeInt((mHeight<<16) | mWidth);
+            }
+            if ((flags&FLAGS_HAS_SCROLL) != 0) {
+                out.writeInt(mScrollX);
+                out.writeInt(mScrollY);
+            }
+            if ((flags&FLAGS_HAS_MATRIX) != 0) {
+                mMatrix.getValues(tmpMatrix);
+                out.writeFloatArray(tmpMatrix);
+            }
+            if ((flags&FLAGS_HAS_ELEVATION) != 0) {
+                out.writeFloat(mElevation);
+            }
+            if ((flags&FLAGS_HAS_ALPHA) != 0) {
+                out.writeFloat(mAlpha);
+            }
+            if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
+                TextUtils.writeToParcel(mContentDescription, out, 0);
+            }
+            if ((flags&FLAGS_HAS_TEXT) != 0) {
+                mText.writeToParcel(out);
+            }
+            if ((flags&FLAGS_HAS_EXTRAS) != 0) {
+                out.writeBundle(mExtras);
+            }
+            int N = 1;
+            if ((flags&FLAGS_HAS_CHILDREN) != 0) {
                 final int NCHILDREN = mChildren.length;
                 out.writeInt(NCHILDREN);
                 for (int i=0; i<NCHILDREN; i++) {
-                    mChildren[i].writeToParcel(out, pwriter);
+                    N += mChildren[i].writeToParcel(out, pwriter, tmpMatrix);
                 }
-            } else {
-                out.writeInt(0);
             }
+            return N;
         }
 
         /**
@@ -408,6 +498,33 @@ public class AssistStructure implements Parcelable {
         }
 
         /**
+         * Returns the transformation that has been applied to this view, such as a translation
+         * or scaling.  The returned Matrix object is owned by ViewNode; do not modify it.
+         * Returns null if there is no transformation applied to the view.
+         */
+        public Matrix getTransformation() {
+            return mMatrix;
+        }
+
+        /**
+         * Returns the visual elevation of the view, used for shadowing and other visual
+         * characterstics, as set by {@link ViewStructure#setElevation
+         * ViewStructure.setElevation(float)}.
+         */
+        public float getElevation() {
+            return mElevation;
+        }
+
+        /**
+         * Returns the alpha transformation of the view, used to reduce the overall opacity
+         * of the view's contents, as set by {@link ViewStructure#setAlpha
+         * ViewStructure.setAlpha(float)}.
+         */
+        public float getAlpha() {
+            return mAlpha;
+        }
+
+        /**
          * Returns the visibility mode of this view, as per
          * {@link android.view.View#getVisibility() View.getVisibility()}.
          */
@@ -646,6 +763,25 @@ public class AssistStructure implements Parcelable {
         }
 
         @Override
+        public void setTransformation(Matrix matrix) {
+            if (matrix == null) {
+                mNode.mMatrix = null;
+            } else {
+                mNode.mMatrix = new Matrix(matrix);
+            }
+        }
+
+        @Override
+        public void setElevation(float elevation) {
+            mNode.mElevation = elevation;
+        }
+
+        @Override
+        public void setAlpha(float alpha) {
+            mNode.mAlpha = alpha;
+        }
+
+        @Override
         public void setVisibility(int visibility) {
             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility;
         }
@@ -919,6 +1055,18 @@ public class AssistStructure implements Parcelable {
         if (scrollX != 0 || scrollY != 0) {
             Log.i(TAG, prefix + "  Scroll: " + scrollX + "," + scrollY);
         }
+        Matrix matrix = node.getTransformation();
+        if (matrix != null) {
+            Log.i(TAG, prefix + "  Transformation: " + matrix);
+        }
+        float elevation = node.getElevation();
+        if (elevation != 0) {
+            Log.i(TAG, prefix + "  Elevation: " + elevation);
+        }
+        float alpha = node.getAlpha();
+        if (alpha != 0) {
+            Log.i(TAG, prefix + "  Alpha: " + elevation);
+        }
         CharSequence contentDescription = node.getContentDescription();
         if (contentDescription != null) {
             Log.i(TAG, prefix + "  Content description: " + contentDescription);
@@ -1010,27 +1158,33 @@ public class AssistStructure implements Parcelable {
             }
             if (mPendingAsyncChildren.size() > 0) {
                 // We waited too long, assume none of the assist structure is valid.
+                Log.w(TAG, "Skipping assist structure, waiting too long for async children (have "
+                        + mPendingAsyncChildren.size() + " remaining");
                 skipStructure = true;
             }
         }
         int start = out.dataPosition();
         PooledStringWriter pwriter = new PooledStringWriter(out);
+        float[] tmpMatrix = new float[9];
         ComponentName.writeToParcel(mActivityComponent, out);
         final int N = skipStructure ? 0 : mWindowNodes.size();
         out.writeInt(N);
+        int NV = 0;
         for (int i=0; i<N; i++) {
-            mWindowNodes.get(i).writeToParcel(out, pwriter);
+            NV += mWindowNodes.get(i).writeToParcel(out, pwriter, tmpMatrix);
         }
         pwriter.finish();
-        Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes");
+        Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes, containing "
+                + N + " windows, " + NV + " views");
     }
 
     void readContentFromParcel(Parcel in) {
         PooledStringReader preader = new PooledStringReader(in);
+        float[] tmpMatrix = new float[9];
         mActivityComponent = ComponentName.readFromParcel(in);
         final int N = in.readInt();
         for (int i=0; i<N; i++) {
-            mWindowNodes.add(new WindowNode(in, preader));
+            mWindowNodes.add(new WindowNode(in, preader, tmpMatrix));
         }
         //dump();
     }
index 67d9de5..eaf20d8 100644 (file)
@@ -152,7 +152,6 @@ public final class BluetoothLeAdvertiser {
      */
     public void stopAdvertising(final AdvertiseCallback callback) {
         synchronized (mLeAdvertisers) {
-            BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
             if (callback == null) {
                 throw new IllegalArgumentException("callback cannot be null");
             }
index 5190037..b1d80f0 100644 (file)
@@ -1020,6 +1020,11 @@ public class Intent implements Parcelable, Cloneable {
      * <p>Note: this Intent <strong>cannot</strong> be used to call emergency
      * numbers.  Applications can <strong>dial</strong> emergency numbers using
      * {@link #ACTION_DIAL}, however.
+     *
+     * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#MNC MNC}
+     * and above and declares as using the {@link android.Manifest.permission#CALL_PHONE}
+     * permission which is not granted, then atempting to use this action will
+     * result in a {@link java.lang.SecurityException}.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_CALL = "android.intent.action.CALL";
@@ -3779,6 +3784,9 @@ public class Intent implements Parcelable, Cloneable {
     /** {@hide} */
     public static final String EXTRA_REASON = "android.intent.extra.REASON";
 
+    /** {@hide} */
+    public static final String EXTRA_WIPE_EXTERNAL_STORAGE = "android.intent.extra.WIPE_EXTERNAL_STORAGE";
+
     /**
      * Optional {@link android.app.PendingIntent} extra used to deliver the result of the SIM
      * activation request.
index e267b52..19329ce 100644 (file)
@@ -518,7 +518,8 @@ public class IntentFilter implements Parcelable {
     }
 
     /**
-     * Return if this filter handle all HTTP or HTTPS data URI or not.
+     * Return if this filter handle all HTTP or HTTPS data URI or not.  This is the
+     * core check for whether a given activity qualifies as a "browser".
      *
      * @return True if the filter handle all HTTP or HTTPS data URI. False otherwise.
      *
@@ -533,23 +534,26 @@ public class IntentFilter implements Parcelable {
      */
     public final boolean handleAllWebDataURI() {
         return hasCategory(Intent.CATEGORY_APP_BROWSER) ||
-                (hasOnlyWebDataURI() && countDataAuthorities() == 0);
+                (handlesWebUris(false) && countDataAuthorities() == 0);
     }
 
     /**
-     * Return if this filter handles only HTTP or HTTPS data URIs.
+     * Return if this filter handles HTTP or HTTPS data URIs.
      *
      * @return True if the filter handles ACTION_VIEW/CATEGORY_BROWSABLE,
-     * has at least one HTTP or HTTPS data URI pattern defined, and does not
-     * define any non-http/https data URI patterns.
+     * has at least one HTTP or HTTPS data URI pattern defined, and optionally
+     * does not define any non-http/https data URI patterns.
      *
      * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
      * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
      * data scheme is "http" or "https".
      *
+     * @param onlyWebSchemes When true, requires that the intent filter declare
+     *     that it handles *only* http: or https: schemes.  This is a requirement for
+     *     the intent filter's domain linkage being verifiable.
      * @hide
      */
-    public final boolean hasOnlyWebDataURI() {
+    public final boolean handlesWebUris(boolean onlyWebSchemes) {
         // Require ACTION_VIEW, CATEGORY_BROWSEABLE, and at least one scheme
         if (!hasAction(Intent.ACTION_VIEW)
             || !hasCategory(Intent.CATEGORY_BROWSABLE)
@@ -562,13 +566,28 @@ public class IntentFilter implements Parcelable {
         final int N = mDataSchemes.size();
         for (int i = 0; i < N; i++) {
             final String scheme = mDataSchemes.get(i);
-            if (!SCHEME_HTTP.equals(scheme) && !SCHEME_HTTPS.equals(scheme)) {
-                return false;
+            final boolean isWebScheme =
+                    SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme);
+            if (onlyWebSchemes) {
+                // If we're specifically trying to ensure that there are no non-web schemes
+                // declared in this filter, then if we ever see a non-http/https scheme then
+                // we know it's a failure.
+                if (!isWebScheme) {
+                    return false;
+                }
+            } else {
+                // If we see any http/https scheme declaration in this case then the
+                // filter matches what we're looking for.
+                if (isWebScheme) {
+                    return true;
+                }
             }
         }
 
-        // Everything passed, so it's an only-web-URIs filter
-        return true;
+        // We get here if:
+        //   1) onlyWebSchemes and no non-web schemes were found, i.e success; or
+        //   2) !onlyWebSchemes and no http/https schemes were found, i.e. failure.
+        return onlyWebSchemes;
     }
 
     /**
@@ -585,7 +604,7 @@ public class IntentFilter implements Parcelable {
      * @hide
      */
     public final boolean needsVerification() {
-        return getAutoVerify() && hasOnlyWebDataURI();
+        return getAutoVerify() && handlesWebUris(true);
     }
 
     /**
index cb68d74..cea6e99 100644 (file)
@@ -102,6 +102,8 @@ interface IPackageManager {
 
     void revokeRuntimePermission(String packageName, String permissionName, int userId);
 
+    void resetRuntimePermissions();
+
     int getPermissionFlags(String permissionName, String packageName, int userId);
 
     void updatePermissionFlags(String permissionName, String packageName, int flagMask,
@@ -504,4 +506,5 @@ interface IPackageManager {
 
     void grantDefaultPermissions(int userId);
     void setCarrierAppPackagesProvider(in IPackagesProvider provider);
+    int getMountExternalMode(int uid);
 }
index a88b71c..c47498d 100644 (file)
@@ -2738,35 +2738,13 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
     /**
      * <p>The maximum number of frames that can occur after a request
      * (different than the previous) has been submitted, and before the
-     * result's state becomes synchronized (by setting
-     * android.sync.frameNumber to a non-negative value).</p>
+     * result's state becomes synchronized.</p>
      * <p>This defines the maximum distance (in number of metadata results),
-     * between android.sync.frameNumber and the equivalent
-     * frame number for that result.</p>
+     * between the frame number of the request that has new controls to apply
+     * and the frame number of the result that has all the controls applied.</p>
      * <p>In other words this acts as an upper boundary for how many frames
      * must occur before the camera device knows for a fact that the new
      * submitted camera settings have been applied in outgoing frames.</p>
-     * <p>For example if the distance was 2,</p>
-     * <pre><code>initial request = X (repeating)
-     * request1 = X
-     * request2 = Y
-     * request3 = Y
-     * request4 = Y
-     *
-     * where requestN has frameNumber N, and the first of the repeating
-     * initial request's has frameNumber F (and F &lt; 1).
-     *
-     * initial result = X' + { android.sync.frameNumber == F }
-     * result1 = X' + { android.sync.frameNumber == F }
-     * result2 = X' + { android.sync.frameNumber == CONVERGING }
-     * result3 = X' + { android.sync.frameNumber == CONVERGING }
-     * result4 = X' + { android.sync.frameNumber == 2 }
-     *
-     * where resultN has frameNumber N.
-     * </code></pre>
-     * <p>Since <code>result4</code> has a <code>frameNumber == 4</code> and
-     * <code>android.sync.frameNumber == 2</code>, the distance is clearly
-     * <code>4 - 2 = 2</code>.</p>
      * <p><b>Units</b>: Frame counts</p>
      * <p><b>Possible values:</b>
      * <ul>
index 006030c..639c8b1 100644 (file)
@@ -236,13 +236,16 @@ public abstract class CameraDevice implements AutoCloseable {
      * {@link CameraCaptureSession.StateCallback}'s
      * {@link CameraCaptureSession.StateCallback#onConfigured} callback will be called.</p>
      *
-     * <p>If a prior CameraCaptureSession already exists when a new one is created, the previous
-     * session is closed. Any in-progress capture requests made on the prior session will be
-     * completed before the new session is configured and is able to start capturing its own
-     * requests. To minimize the transition time, the {@link CameraCaptureSession#abortCaptures}
-     * call can be used to discard the remaining requests for the prior capture session before a new
-     * one is created. Note that once the new session is created, the old one can no longer have its
-     * captures aborted.</p>
+     * <p>If a prior CameraCaptureSession already exists when this method is called, the previous
+     * session will no longer be able to accept new capture requests and will be closed. Any
+     * in-progress capture requests made on the prior session will be completed before it's closed.
+     * {@link CameraCaptureSession.StateListener#onConfigured} for the new session may be invoked
+     * before {@link CameraCaptureSession.StateListener#onClosed} is invoked for the prior
+     * session. Once the new session is {@link CameraCaptureSession.StateListener#onConfigured
+     * configured}, it is able to start capturing its own requests. To minimize the transition time,
+     * the {@link CameraCaptureSession#abortCaptures} call can be used to discard the remaining
+     * requests for the prior capture session before a new one is created. Note that once the new
+     * session is created, the old one can no longer have its captures aborted.</p>
      *
      * <p>Using larger resolution outputs, or more outputs, can result in slower
      * output rate from the device.</p>
index 5a80585..e8dbc5b 100644 (file)
@@ -955,8 +955,6 @@ public abstract class CameraMetadata<TKey> {
 
     /**
      * <p>Every frame has the requests immediately applied.</p>
-     * <p>Furthermore for all results,
-     * <code>android.sync.frameNumber == {@link android.hardware.camera2.CaptureResult#getFrameNumber }</code></p>
      * <p>Changing controls over multiple requests one after another will
      * produce results that have those controls applied atomically
      * each frame.</p>
index 75289f7..33cc962 100644 (file)
@@ -1062,6 +1062,15 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
      * capturing a high-resolution JPEG image will automatically trigger a
      * precapture sequence before the high-resolution capture, including
      * potentially firing a pre-capture flash.</p>
+     * <p>Using the precapture trigger and the auto-focus trigger {@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}
+     * simultaneously is allowed. However, since these triggers often require cooperation between
+     * the auto-focus and auto-exposure routines (for example, the may need to be enabled for a
+     * focus sweep), the camera device may delay acting on a later trigger until the previous
+     * trigger has been fully handled. This may lead to longer intervals between the trigger and
+     * changes to {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} indicating the start of the precapture sequence, for
+     * example.</p>
+     * <p>If both the precapture and the auto-focus trigger are activated on the same request, then
+     * the camera device will complete them in the optimal order for that device.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE IDLE}</li>
@@ -1075,6 +1084,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
      *
      * @see CaptureRequest#CONTROL_AE_LOCK
      * @see CaptureResult#CONTROL_AE_STATE
+     * @see CaptureRequest#CONTROL_AF_TRIGGER
      * @see CaptureRequest#CONTROL_CAPTURE_INTENT
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
@@ -1179,6 +1189,12 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
      * START for multiple captures in a row means restarting the AF operation over
      * and over again.</p>
      * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what the trigger means for each AF mode.</p>
+     * <p>Using the autofocus trigger and the precapture trigger {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}
+     * simultaneously is allowed. However, since these triggers often require cooperation between
+     * the auto-focus and auto-exposure routines (for example, the may need to be enabled for a
+     * focus sweep), the camera device may delay acting on a later trigger until the previous
+     * trigger has been fully handled. This may lead to longer intervals between the trigger and
+     * changes to {@link CaptureResult#CONTROL_AF_STATE android.control.afState}, for example.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #CONTROL_AF_TRIGGER_IDLE IDLE}</li>
@@ -1187,6 +1203,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
      * @see CaptureResult#CONTROL_AF_STATE
      * @see #CONTROL_AF_TRIGGER_IDLE
      * @see #CONTROL_AF_TRIGGER_START
index 1d31109..9dee045 100644 (file)
@@ -779,6 +779,15 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
      * capturing a high-resolution JPEG image will automatically trigger a
      * precapture sequence before the high-resolution capture, including
      * potentially firing a pre-capture flash.</p>
+     * <p>Using the precapture trigger and the auto-focus trigger {@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}
+     * simultaneously is allowed. However, since these triggers often require cooperation between
+     * the auto-focus and auto-exposure routines (for example, the may need to be enabled for a
+     * focus sweep), the camera device may delay acting on a later trigger until the previous
+     * trigger has been fully handled. This may lead to longer intervals between the trigger and
+     * changes to {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} indicating the start of the precapture sequence, for
+     * example.</p>
+     * <p>If both the precapture and the auto-focus trigger are activated on the same request, then
+     * the camera device will complete them in the optimal order for that device.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE IDLE}</li>
@@ -792,6 +801,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
      *
      * @see CaptureRequest#CONTROL_AE_LOCK
      * @see CaptureResult#CONTROL_AE_STATE
+     * @see CaptureRequest#CONTROL_AF_TRIGGER
      * @see CaptureRequest#CONTROL_CAPTURE_INTENT
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
@@ -1139,6 +1149,12 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
      * START for multiple captures in a row means restarting the AF operation over
      * and over again.</p>
      * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what the trigger means for each AF mode.</p>
+     * <p>Using the autofocus trigger and the precapture trigger {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}
+     * simultaneously is allowed. However, since these triggers often require cooperation between
+     * the auto-focus and auto-exposure routines (for example, the may need to be enabled for a
+     * focus sweep), the camera device may delay acting on a later trigger until the previous
+     * trigger has been fully handled. This may lead to longer intervals between the trigger and
+     * changes to {@link CaptureResult#CONTROL_AF_STATE android.control.afState}, for example.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #CONTROL_AF_TRIGGER_IDLE IDLE}</li>
@@ -1147,6 +1163,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
+     * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
      * @see CaptureResult#CONTROL_AF_STATE
      * @see #CONTROL_AF_TRIGGER_IDLE
      * @see #CONTROL_AF_TRIGGER_START
index 0484806..f596c93 100644 (file)
@@ -26,7 +26,7 @@ import java.util.List;
  */
 interface IFingerprintService {
     // Authenticate the given sessionId with a fingerprint
-    void authenticate(IBinder token, long sessionId, int groupId,
+    void authenticate(IBinder token, long sessionId, int userId,
             IFingerprintServiceReceiver receiver, int flags, String opPackageName);
 
     // Cancel authentication for the given sessionId
index ae74b9a..ff7a300 100644 (file)
@@ -1819,7 +1819,18 @@ public class InputMethodService extends AbstractInputMethodService {
         }
         return false;
     }
-    
+
+    /**
+     * @return {#link ExtractEditText} if it is considered to be visible and active. Otherwise
+     * {@code null} is returned.
+     */
+    private ExtractEditText getExtractEditTextIfVisible() {
+        if (!isExtractViewShown() || !isInputViewShown()) {
+            return null;
+        }
+        return mExtractEditText;
+    }
+
     /**
      * Override this to intercept key down events before they are processed by the
      * application.  If you return true, the application will not 
@@ -1835,6 +1846,10 @@ public class InputMethodService extends AbstractInputMethodService {
      */
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+            final ExtractEditText eet = getExtractEditTextIfVisible();
+            if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
+                return true;
+            }
             if (handleBack(false)) {
                 event.startTracking();
                 return true;
@@ -1882,11 +1897,15 @@ public class InputMethodService extends AbstractInputMethodService {
      * them to perform navigation in the underlying application.
      */
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isTracking()
-                && !event.isCanceled()) {
-            return handleBack(true);
+        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+            final ExtractEditText eet = getExtractEditTextIfVisible();
+            if (eet != null && eet.handleBackInTextActionModeIfNeeded(event)) {
+                return true;
+            }
+            if (event.isTracking() && !event.isCanceled()) {
+                return handleBack(true);
+            }
         }
-        
         return doMovementKey(keyCode, event, MOVEMENT_UP);
     }
 
@@ -1952,10 +1971,10 @@ public class InputMethodService extends AbstractInputMethodService {
         }
         onExtractedCursorMovement(dx, dy);
     }
-    
+
     boolean doMovementKey(int keyCode, KeyEvent event, int count) {
-        final ExtractEditText eet = mExtractEditText;
-        if (isExtractViewShown() && isInputViewShown() && eet != null) {
+        final ExtractEditText eet = getExtractEditTextIfVisible();
+        if (eet != null) {
             // If we are in fullscreen mode, the cursor will move around
             // the extract edit text, but should NOT cause focus to move
             // to other fields.
@@ -2006,7 +2025,7 @@ public class InputMethodService extends AbstractInputMethodService {
                     return true;
             }
         }
-        
+
         return false;
     }
     
index 50eed3e..b2ced7f 100644 (file)
@@ -622,7 +622,7 @@ public class Build {
         /**
          * M comes after L.
          */
-        public static final int MNC = CUR_DEVELOPMENT;
+        public static final int MNC = 23;
     }
 
     /** The type of build, like "user" or "eng". */
index f9c50f3..7234e98 100644 (file)
@@ -643,6 +643,10 @@ public class Process {
             }
             if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
                 argsForZygote.add("--mount-external-default");
+            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
+                argsForZygote.add("--mount-external-read");
+            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
+                argsForZygote.add("--mount-external-write");
             }
             argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
 
@@ -802,7 +806,12 @@ public class Process {
      * @hide
      */
     public static final boolean isIsolated() {
-        int uid = UserHandle.getAppId(myUid());
+        return isIsolated(myUid());
+    }
+
+    /** {@hide} */
+    public static final boolean isIsolated(int uid) {
+        uid = UserHandle.getAppId(uid);
         return uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID;
     }
 
index e55ae99..84a879c 100644 (file)
@@ -1177,6 +1177,21 @@ public interface IMountService extends IInterface {
                     _data.recycle();
                 }
             }
+
+            @Override
+            public void remountUid(int uid) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(uid);
+                    mRemote.transact(Stub.TRANSACTION_remountUid, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
         }
 
         private static final String DESCRIPTOR = "IMountService";
@@ -1292,6 +1307,8 @@ public interface IMountService extends IInterface {
         static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59;
         static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60;
 
+        static final int TRANSACTION_remountUid = IBinder.FIRST_CALL_TRANSACTION + 61;
+
         /**
          * Cast an IBinder object into an IMountService interface, generating a
          * proxy if needed.
@@ -1845,6 +1862,13 @@ public interface IMountService extends IInterface {
                     reply.writeNoException();
                     return true;
                 }
+                case TRANSACTION_remountUid: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int uid = data.readInt();
+                    remountUid(uid);
+                    reply.writeNoException();
+                    return true;
+                }
             }
             return super.onTransact(code, data, reply, flags);
         }
@@ -2154,4 +2178,6 @@ public interface IMountService extends IInterface {
     public String getPrimaryStorageUuid() throws RemoteException;
     public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
             throws RemoteException;
+
+    public void remountUid(int uid) throws RemoteException;
 }
index 9b26f24..aab68e9 100644 (file)
@@ -871,6 +871,15 @@ public class StorageManager {
     }
 
     /** {@hide} */
+    public void remountUid(int uid) {
+        try {
+            mMountService.remountUid(uid);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** {@hide} */
     private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
     private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
     private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
index 51dbdee..e63fb04 100644 (file)
@@ -283,7 +283,13 @@ public final class MediaStore {
      * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
      * If you don't set a ClipData, it will be copied there for you when calling
      * {@link Context#startActivity(Intent)}.
-     * @see #EXTRA_OUTPUT
+     *
+     * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#MNC MNC} and above
+     * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
+     * is not granted, then atempting to use this action will result in a {@link
+     * java.lang.SecurityException}.
+     *
+     *  @see #EXTRA_OUTPUT
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
@@ -331,6 +337,12 @@ public final class MediaStore {
      * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
      * If you don't set a ClipData, it will be copied there for you when calling
      * {@link Context#startActivity(Intent)}.
+     *
+     * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#MNC MNC} and above
+     * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
+     * is not granted, then atempting to use this action will result in a {@link
+     * java.lang.SecurityException}.
+     *
      * @see #EXTRA_OUTPUT
      * @see #EXTRA_VIDEO_QUALITY
      * @see #EXTRA_SIZE_LIMIT
index 0309d24..d424546 100644 (file)
@@ -369,7 +369,9 @@ public abstract class NotificationListenerService extends Service {
 
     /**
      * Inform the notification manager that these notifications have been viewed by the
-     * user.
+     * user. This should only be called when there is sufficient confidence that the user is
+     * looking at the notifications, such as when the notifications appear on the screen due to
+     * an explicit user interaction.
      * @param keys Notifications to mark as seen.
      */
     public final void setNotificationsShown(String[] keys) {
index 464710b..b6fa4e4 100644 (file)
@@ -714,6 +714,27 @@ public class StaticLayout extends Layout {
             float[] lineWidths = lineBreaks.widths;
             int[] flags = lineBreaks.flags;
 
+            final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
+            final boolean ellipsisMayBeApplied = ellipsize != null
+                    && (ellipsize == TextUtils.TruncateAt.END
+                        || (mMaximumVisibleLineCount == 1
+                                && ellipsize != TextUtils.TruncateAt.MARQUEE));
+            if (remainingLineCount < breakCount && ellipsisMayBeApplied) {
+                // Treat the last line and overflowed lines as a single line.
+                breaks[remainingLineCount - 1] = breaks[breakCount - 1];
+                // Calculate width and flag.
+                float width = 0;
+                int flag = 0;
+                for (int i = remainingLineCount - 1; i < breakCount; i++) {
+                    width += lineWidths[i];
+                    flag |= flags[i] & TAB_MASK;
+                }
+                lineWidths[remainingLineCount - 1] = width;
+                flags[remainingLineCount - 1] = flag;
+
+                breakCount = remainingLineCount;
+            }
+
             // here is the offset of the starting character of the line we are currently measuring
             int here = paraStart;
 
index 47d5c79..82689b9 100644 (file)
@@ -105,32 +105,45 @@ public final class Formatter {
             mult = TrafficStats.PB_IN_BYTES;
             result = result / 1024;
         }
-        String value;
+        // Note we calculate the rounded long by ourselves, but still let String.format()
+        // compute the rounded value. String.format("%f", 0.1) might not return "0.1" due to
+        // floating point errors.
+        final int roundFactor;
+        final String roundFormat;
         if (result < 1) {
-            value = String.format("%.2f", result);
+            roundFactor = 100;
+            roundFormat = "%.2f";
         } else if (result < 10) {
             if ((flags & FLAG_SHORTER) != 0) {
-                value = String.format("%.1f", result);
+                roundFactor = 10;
+                roundFormat = "%.1f";
             } else {
-                value = String.format("%.2f", result);
+                roundFactor = 100;
+                roundFormat = "%.2f";
             }
         } else if (result < 100) {
             if ((flags & FLAG_SHORTER) != 0) {
-                value = String.format("%.0f", result);
+                roundFactor = 1;
+                roundFormat = "%.0f";
             } else {
-                value = String.format("%.2f", result);
+                roundFactor = 100;
+                roundFormat = "%.2f";
             }
         } else {
-            value = String.format("%.0f", result);
+            roundFactor = 1;
+            roundFormat = "%.0f";
         }
+        final String roundedString = String.format(roundFormat, result);
+
+        // Note this might overflow if result >= Long.MAX_VALUE / 100, but that's like 80PB so
+        // it's okay (for now)...
+        final long roundedBytes =
+                (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0
+                : (((long) Math.round(result * roundFactor)) * mult / roundFactor);
+
         final String units = res.getString(suffix);
-        final long roundedBytes;
-        if ((flags & FLAG_CALCULATE_ROUNDED) != 0) {
-            roundedBytes = (long) (Double.parseDouble(value) * mult);
-        } else {
-            roundedBytes = 0;
-        }
-        return new BytesResult(value, units, roundedBytes);
+
+        return new BytesResult(roundedString, units, roundedBytes);
     }
 
     /**
index 5dda8a7..3688cfa 100644 (file)
@@ -147,11 +147,90 @@ public class WordIterator implements Selection.PositionIterator {
      * @throws IllegalArgumentException is offset is not valid.
      */
     public int getBeginning(int offset) {
+        // TODO: Check if usage of this can be updated to getBeginning(offset, true) if
+        // so this method can be removed.
+        return getBeginning(offset, false);
+    }
+
+    /**
+     * If <code>offset</code> is within a word, returns the index of the last character of that
+     * word plus one, otherwise returns BreakIterator.DONE.
+     *
+     * The offsets that are considered to be part of a word are the indexes of its characters,
+     * <i>as well as</i> the index of its last character plus one.
+     * If offset is the index of a low surrogate character, BreakIterator.DONE will be returned.
+     *
+     * Valid range for offset is [0..textLength] (note the inclusive upper bound).
+     * The returned value is within [offset..textLength] or BreakIterator.DONE.
+     *
+     * @throws IllegalArgumentException is offset is not valid.
+     */
+    public int getEnd(int offset) {
+        // TODO: Check if usage of this can be updated to getEnd(offset, true), if
+        // so this method can be removed.
+        return getEnd(offset, false);
+    }
+
+    /**
+     * If the <code>offset</code> is within a word or on a word boundary that can only be
+     * considered the start of a word (e.g. _word where "_" is any character that would not
+     * be considered part of the word) then this returns the index of the first character of
+     * that word.
+     *
+     * If the offset is on a word boundary that can be considered the start and end of a
+     * word, e.g. AABB (where AA and BB are both words) and the offset is the boundary
+     * between AA and BB, this would return the start of the previous word, AA.
+     *
+     * Returns BreakIterator.DONE if there is no previous boundary.
+     *
+     * @throws IllegalArgumentException is offset is not valid.
+     */
+    public int getPrevWordBeginningOnTwoWordsBoundary(int offset) {
+        return getBeginning(offset, true);
+    }
+
+    /**
+     * If the <code>offset</code> is within a word or on a word boundary that can only be
+     * considered the end of a word (e.g. word_ where "_" is any character that would not
+     * be considered part of the word) then this returns the index of the last character
+     * plus one of that word.
+     *
+     * If the offset is on a word boundary that can be considered the start and end of a
+     * word, e.g. AABB (where AA and BB are both words) and the offset is the boundary
+     * between AA and BB, this would return the end of the next word, BB.
+     *
+     * Returns BreakIterator.DONE if there is no next boundary.
+     *
+     * @throws IllegalArgumentException is offset is not valid.
+     */
+    public int getNextWordEndOnTwoWordBoundary(int offset) {
+        return getEnd(offset, true);
+    }
+
+    /**
+     * If the <code>offset</code> is within a word or on a word boundary that can only be
+     * considered the start of a word (e.g. _word where "_" is any character that would not
+     * be considered part of the word) then this returns the index of the first character of
+     * that word.
+     *
+     * If the offset is on a word boundary that can be considered the start and end of a
+     * word, e.g. AABB (where AA and BB are both words) and the offset is the boundary
+     * between AA and BB, and getPrevWordBeginningOnTwoWordsBoundary is true then this would
+     * return the start of the previous word, AA. Otherwise it would return the current offset,
+     * the start of BB.
+     *
+     * Returns BreakIterator.DONE if there is no previous boundary.
+     *
+     * @throws IllegalArgumentException is offset is not valid.
+     */
+    private int getBeginning(int offset, boolean getPrevWordBeginningOnTwoWordsBoundary) {
         final int shiftedOffset = offset - mOffsetShift;
         checkOffsetIsValid(shiftedOffset);
 
         if (isOnLetterOrDigit(shiftedOffset)) {
-            if (mIterator.isBoundary(shiftedOffset)) {
+            if (mIterator.isBoundary(shiftedOffset)
+                    && (!isAfterLetterOrDigit(shiftedOffset)
+                            || !getPrevWordBeginningOnTwoWordsBoundary)) {
                 return shiftedOffset + mOffsetShift;
             } else {
                 return mIterator.preceding(shiftedOffset) + mOffsetShift;
@@ -164,24 +243,29 @@ public class WordIterator implements Selection.PositionIterator {
         return BreakIterator.DONE;
     }
 
-    /** If <code>offset</code> is within a word, returns the index of the last character of that
-     * word plus one, otherwise returns BreakIterator.DONE.
+    /**
+     * If the <code>offset</code> is within a word or on a word boundary that can only be
+     * considered the end of a word (e.g. word_ where "_" is any character that would not be
+     * considered part of the word) then this returns the index of the last character plus one
+     * of that word.
      *
-     * The offsets that are considered to be part of a word are the indexes of its characters,
-     * <i>as well as</i> the index of its last character plus one.
-     * If offset is the index of a low surrogate character, BreakIterator.DONE will be returned.
+     * If the offset is on a word boundary that can be considered the start and end of a
+     * word, e.g. AABB (where AA and BB are both words) and the offset is the boundary
+     * between AA and BB, and getNextWordEndOnTwoWordBoundary is true then this would return
+     * the end of the next word, BB. Otherwise it would return the current offset, the end
+     * of AA.
      *
-     * Valid range for offset is [0..textLength] (note the inclusive upper bound).
-     * The returned value is within [offset..textLength] or BreakIterator.DONE.
+     * Returns BreakIterator.DONE if there is no next boundary.
      *
      * @throws IllegalArgumentException is offset is not valid.
      */
-    public int getEnd(int offset) {
+    private int getEnd(int offset, boolean getNextWordEndOnTwoWordBoundary) {
         final int shiftedOffset = offset - mOffsetShift;
         checkOffsetIsValid(shiftedOffset);
 
         if (isAfterLetterOrDigit(shiftedOffset)) {
-            if (mIterator.isBoundary(shiftedOffset)) {
+            if (mIterator.isBoundary(shiftedOffset)
+                    && (!isOnLetterOrDigit(shiftedOffset) || !getNextWordEndOnTwoWordBoundary)) {
                 return shiftedOffset + mOffsetShift;
             } else {
                 return mIterator.following(shiftedOffset) + mOffsetShift;
index c61ca4e..e958058 100644 (file)
@@ -528,11 +528,13 @@ public abstract class Transition implements Cloneable {
             ArrayMap<View, TransitionValues> unmatchedEnd) {
         for (int i = unmatchedStart.size() - 1; i >= 0; i--) {
             View view = unmatchedStart.keyAt(i);
-            TransitionValues end = unmatchedEnd.remove(view);
-            if (end != null) {
-                TransitionValues start = unmatchedStart.removeAt(i);
-                mStartValuesList.add(start);
-                mEndValuesList.add(end);
+            if (view != null && isValidTarget(view)) {
+                TransitionValues end = unmatchedEnd.remove(view);
+                if (end != null && end.view != null && isValidTarget(end.view)) {
+                    TransitionValues start = unmatchedStart.removeAt(i);
+                    mStartValuesList.add(start);
+                    mEndValuesList.add(end);
+                }
             }
         }
     }
@@ -548,9 +550,9 @@ public abstract class Transition implements Cloneable {
         int numStartIds = startItemIds.size();
         for (int i = 0; i < numStartIds; i++) {
             View startView = startItemIds.valueAt(i);
-            if (startView != null) {
+            if (startView != null && isValidTarget(startView)) {
                 View endView = endItemIds.get(startItemIds.keyAt(i));
-                if (endView != null) {
+                if (endView != null && isValidTarget(endView)) {
                     TransitionValues startValues = unmatchedStart.get(startView);
                     TransitionValues endValues = unmatchedEnd.get(endView);
                     if (startValues != null && endValues != null) {
@@ -626,14 +628,20 @@ public abstract class Transition implements Cloneable {
             ArrayMap<View, TransitionValues> unmatchedEnd) {
         // Views that only exist in the start Scene
         for (int i = 0; i < unmatchedStart.size(); i++) {
-            mStartValuesList.add(unmatchedStart.valueAt(i));
-            mEndValuesList.add(null);
+            final TransitionValues start = unmatchedStart.valueAt(i);
+            if (isValidTarget(start.view)) {
+                mStartValuesList.add(start);
+                mEndValuesList.add(null);
+            }
         }
 
         // Views that only exist in the end Scene
         for (int i = 0; i < unmatchedEnd.size(); i++) {
-            mEndValuesList.add(unmatchedEnd.valueAt(i));
-            mStartValuesList.add(null);
+            final TransitionValues end = unmatchedEnd.valueAt(i);
+            if (isValidTarget(end.view)) {
+                mEndValuesList.add(end);
+                mStartValuesList.add(null);
+            }
         }
     }
 
@@ -1046,7 +1054,7 @@ public abstract class Transition implements Cloneable {
      */
     public Transition removeTarget(int targetId) {
         if (targetId > 0) {
-            mTargetIds.remove(targetId);
+            mTargetIds.remove((Integer)targetId);
         }
         return this;
     }
index 5209f90..71c8099 100644 (file)
@@ -435,10 +435,11 @@ public class TransitionManager {
         sPendingTransitions.remove(sceneRoot);
 
         final ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
-        if (runningTransitions != null) {
-            final int count = runningTransitions.size();
-            for (int i = 0; i < count; i++) {
-                final Transition transition = runningTransitions.get(i);
+        if (runningTransitions != null && !runningTransitions.isEmpty()) {
+            // Make a copy in case this is called by an onTransitionEnd listener
+            ArrayList<Transition> copy = new ArrayList(runningTransitions);
+            for (int i = copy.size() - 1; i >= 0; i--) {
+                final Transition transition = copy.get(i);
                 transition.end();
             }
         }
index cab5d19..4862f01 100644 (file)
@@ -54,4 +54,18 @@ public final class LocalLog {
             pw.println(itr.next());
         }
     }
+
+    public static class ReadOnlyLocalLog {
+        private final LocalLog mLog;
+        ReadOnlyLocalLog(LocalLog log) {
+            mLog = log;
+        }
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            mLog.dump(fd, pw, args);
+        }
+    }
+
+    public ReadOnlyLocalLog readOnlyLocalLog() {
+        return new ReadOnlyLocalLog(this);
+    }
 }
index 92dae2e..23da6d2 100644 (file)
@@ -6179,6 +6179,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
             structure.setId(id, null, null, null);
         }
         structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop);
+        if (!hasIdentityMatrix()) {
+            structure.setTransformation(getMatrix());
+        }
+        structure.setElevation(getZ());
         structure.setVisibility(getVisibility());
         structure.setEnabled(isEnabled());
         if (isClickable()) {
@@ -6215,11 +6219,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         structure.setContentDescription(getContentDescription());
     }
 
-    /** @hide */
-    public void onProvideAssistStructure(ViewStructure structure) {
-        onProvideStructure(structure);
-    }
-
     /**
      * Called when assist structure is being retrieved from a view as part of
      * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to
@@ -6232,7 +6231,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
         if (provider != null) {
             AccessibilityNodeInfo info = createAccessibilityNodeInfo();
-            Log.i("View", "Provider of " + this + ": children=" + info.getChildCount());
             structure.setChildCount(1);
             ViewStructure root = structure.newChild(0);
             populateVirtualStructure(root, provider, info);
@@ -6240,11 +6238,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         }
     }
 
-    /** @hide */
-    public void onProvideVirtualAssistStructure(ViewStructure structure) {
-        onProvideVirtualStructure(structure);
-    }
-
     private void populateVirtualStructure(ViewStructure structure,
             AccessibilityNodeProvider provider, AccessibilityNodeInfo info) {
         structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
@@ -6284,8 +6277,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         CharSequence cname = info.getClassName();
         structure.setClassName(cname != null ? cname.toString() : null);
         structure.setContentDescription(info.getContentDescription());
-        Log.i("View", "vassist " + cname + " @ " + rect.toShortString()
-                + " text=" + info.getText() + " cd=" + info.getContentDescription());
         if (info.getText() != null || info.getError() != null) {
             structure.setText(info.getText(), info.getTextSelectionStart(),
                     info.getTextSelectionEnd());
@@ -6310,8 +6301,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
      */
     public void dispatchProvideStructure(ViewStructure structure) {
         if (!isAssistBlocked()) {
-            onProvideAssistStructure(structure);
-            onProvideVirtualAssistStructure(structure);
+            onProvideStructure(structure);
+            onProvideVirtualStructure(structure);
         } else {
             structure.setClassName(getAccessibilityClassName().toString());
             structure.setAssistBlocked(true);
@@ -8710,14 +8701,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     }
 
     /**
-     * Adds the children of a given View for accessibility. Since some Views are
-     * not important for accessibility the children for accessibility are not
-     * necessarily direct children of the view, rather they are the first level of
-     * descendants important for accessibility.
+     * Adds the children of this View relevant for accessibility to the given list
+     * as output. Since some Views are not important for accessibility the added
+     * child views are not necessarily direct children of this view, rather they are
+     * the first level of descendants important for accessibility.
      *
-     * @param children The list of children for accessibility.
+     * @param outChildren The output list that will receive children for accessibility.
      */
-    public void addChildrenForAccessibility(ArrayList<View> children) {
+    public void addChildrenForAccessibility(ArrayList<View> outChildren) {
 
     }
 
index 2e2ba88..b53d93c 100644 (file)
@@ -1919,7 +1919,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     }
 
     @Override
-    public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
+    public void addChildrenForAccessibility(ArrayList<View> outChildren) {
         if (getAccessibilityNodeProvider() != null) {
             return;
         }
@@ -1930,9 +1930,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
                 View child = children.getChildAt(i);
                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                     if (child.includeForAccessibility()) {
-                        childrenForAccessibility.add(child);
+                        outChildren.add(child);
                     } else {
-                        child.addChildrenForAccessibility(childrenForAccessibility);
+                        child.addChildrenForAccessibility(outChildren);
                     }
                 }
             }
index 9ab0ace..794622a 100644 (file)
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Bundle;
 
@@ -50,6 +51,28 @@ public abstract class ViewStructure {
             int height);
 
     /**
+     * Set the transformation matrix associated with this view, as per
+     * {@link View#getMatrix View.getMatrix()}, or null if there is none.
+     */
+    public abstract void setTransformation(Matrix matrix);
+
+    /**
+     * Set the visual elevation (shadow) of the view, as per
+     * {@link View#getZ View.getZ()}.  Note this is <em>not</em> related
+     * to the physical Z-ordering of this view relative to its other siblings (that is how
+     * they overlap when drawing), it is only the visual representation for shadowing.
+     */
+    public abstract void setElevation(float elevation);
+
+    /**
+     * Set an alpha transformation that is applied to this view, as per
+     * {@link View#getAlpha View.getAlpha()}.  Value ranges from 0
+     * (completely transparent) to 1 (completely opaque); the default is 1, which means
+     * no transformation.
+     */
+    public abstract void setAlpha(float alpha);
+
+    /**
      * Set the visibility state of this view, as per
      * {@link View#getVisibility View.getVisibility()}.
      */
index a96bf71..8bf6992 100644 (file)
@@ -6605,6 +6605,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
         void addScrapView(View scrap, int position) {
             final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
             if (lp == null) {
+                // Can't recycle, skip the scrap heap.
+                getSkippedScrap().add(scrap);
                 return;
             }
 
@@ -6614,6 +6616,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
             // should otherwise not be recycled.
             final int viewType = lp.viewType;
             if (!shouldRecycleViewType(viewType)) {
+                // Can't recycle, skip the scrap heap.
+                getSkippedScrap().add(scrap);
                 return;
             }
 
@@ -6633,22 +6637,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                     // If the adapter has stable IDs, we can reuse the view for
                     // the same data.
                     if (mTransientStateViewsById == null) {
-                        mTransientStateViewsById = new LongSparseArray<View>();
+                        mTransientStateViewsById = new LongSparseArray<>();
                     }
                     mTransientStateViewsById.put(lp.itemId, scrap);
                 } else if (!mDataChanged) {
                     // If the data hasn't changed, we can reuse the views at
                     // their old positions.
                     if (mTransientStateViews == null) {
-                        mTransientStateViews = new SparseArray<View>();
+                        mTransientStateViews = new SparseArray<>();
                     }
                     mTransientStateViews.put(position, scrap);
                 } else {
                     // Otherwise, we'll have to remove the view and start over.
-                    if (mSkippedScrap == null) {
-                        mSkippedScrap = new ArrayList<View>();
-                    }
-                    mSkippedScrap.add(scrap);
+                    getSkippedScrap().add(scrap);
                 }
             } else {
                 if (mViewTypeCount == 1) {
@@ -6663,6 +6664,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
             }
         }
 
+        private ArrayList<View> getSkippedScrap() {
+            if (mSkippedScrap == null) {
+                mSkippedScrap = new ArrayList<>();
+            }
+            return mSkippedScrap;
+        }
+
         /**
          * Finish the removal of any views that skipped the scrap heap.
          */
index 48e69a1..84e7db4 100644 (file)
@@ -123,6 +123,7 @@ public class Editor {
     private static final float[] TEMP_POSITION = new float[2];
     private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
     private static final float LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS = 0.5f;
+    private static final int UNSET_X_VALUE = -1;
     // Tag used when the Editor maintains its own separate UndoManager.
     private static final String UNDO_OWNER_TAG = "Editor";
 
@@ -735,7 +736,7 @@ public class Editor {
             retOffset = getWordIteratorWithText().getPunctuationBeginning(offset);
         } else {
             // Not on a punctuation boundary, find the word start.
-            retOffset = getWordIteratorWithText().getBeginning(offset);
+            retOffset = getWordIteratorWithText().getPrevWordBeginningOnTwoWordsBoundary(offset);
         }
         if (retOffset == BreakIterator.DONE) {
             return offset;
@@ -750,7 +751,7 @@ public class Editor {
             retOffset = getWordIteratorWithText().getPunctuationEnd(offset);
         } else {
             // Not on a punctuation boundary, find the word end.
-            retOffset = getWordIteratorWithText().getEnd(offset);
+            retOffset = getWordIteratorWithText().getNextWordEndOnTwoWordBoundary(offset);
         }
         if (retOffset == BreakIterator.DONE) {
             return offset;
@@ -4067,6 +4068,10 @@ public class Editor {
         private boolean mInWord = false;
         // Difference between touch position and word boundary position.
         private float mTouchWordDelta;
+        // X value of the previous updatePosition call.
+        private float mPrevX;
+        // Indicates if the handle has moved a boundary between LTR and RTL text.
+        private boolean mLanguageDirectionChanged = false;
 
         public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) {
             super(drawableLtr, drawableRtl);
@@ -4127,13 +4132,56 @@ public class Editor {
             int end = getWordEnd(offset);
             int start = getWordStart(offset);
 
-            if (offset < mPreviousOffset) {
+            if (mPrevX == UNSET_X_VALUE) {
+                mPrevX = x;
+            }
+
+            final int selectionStart = mTextView.getSelectionStart();
+            final boolean selectionStartRtl = layout.isRtlCharAt(selectionStart);
+            final boolean atRtl = layout.isRtlCharAt(offset);
+            final boolean isLvlBoundary = layout.isLevelBoundary(offset);
+            boolean isExpanding;
+
+            // We can't determine if the user is expanding or shrinking the selection if they're
+            // on a bi-di boundary, so until they've moved past the boundary we'll just place
+            // the cursor at the current position.
+            if (isLvlBoundary || (selectionStartRtl && !atRtl) || (!selectionStartRtl && atRtl)) {
+                // We're on a boundary or this is the first direction change -- just update
+                // to the current position.
+                mLanguageDirectionChanged = true;
+                mTouchWordDelta = 0.0f;
+                positionAtCursorOffset(offset, false);
+                return;
+            } else if (mLanguageDirectionChanged && !isLvlBoundary) {
+                // We've just moved past the boundary so update the position. After this we can
+                // figure out if the user is expanding or shrinking to go by word or character.
+                positionAtCursorOffset(offset, false);
+                mTouchWordDelta = 0.0f;
+                mLanguageDirectionChanged = false;
+                return;
+            } else {
+                final float xDiff = x - mPrevX;
+                if (atRtl) {
+                    isExpanding = xDiff > 0 || currLine > mPrevLine;
+                } else {
+                    isExpanding = xDiff < 0 || currLine < mPrevLine;
+                }
+            }
+
+            if (isExpanding) {
                 // User is increasing the selection.
                 if (!mInWord || currLine < mPrevLine) {
-                    // We're not in a word, or we're on a different line so we'll expand by
-                    // word. First ensure the user has at least entered the next word.
-                    int offsetToWord = Math.min((end - start) / 2, 2);
-                    if (offset <= end - offsetToWord || currLine < mPrevLine) {
+                    // Sometimes words can be broken across lines (Chinese, hyphenation).
+                    // We still snap to the start of the word but we only use the letters on the
+                    // current line to determine if the user is far enough into the word to snap.
+                    int wordStartOnCurrLine = start;
+                    if (layout != null && layout.getLineForOffset(start) != currLine) {
+                        wordStartOnCurrLine = layout.getLineStart(currLine);
+                    }
+                    int offsetThresholdToSnap = end - ((end - wordStartOnCurrLine) / 2);
+                    if (offset <= offsetThresholdToSnap || currLine < mPrevLine) {
+                        // User is far enough into the word or on a different
+                        // line so we expand by word.
                         offset = start;
                     } else {
                         offset = mPreviousOffset;
@@ -4182,6 +4230,7 @@ public class Editor {
                 }
                 positionAtCursorOffset(offset, false);
             }
+            mPrevX = x;
         }
 
         @Override
@@ -4196,6 +4245,7 @@ public class Editor {
             if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                 // Reset the touch word offset when the user has lifted their finger.
                 mTouchWordDelta = 0.0f;
+                mPrevX = UNSET_X_VALUE;
             }
             return superResult;
         }
@@ -4206,6 +4256,10 @@ public class Editor {
         private boolean mInWord = false;
         // Difference between touch position and word boundary position.
         private float mTouchWordDelta;
+        // X value of the previous updatePosition call.
+        private float mPrevX;
+        // Indicates if the handle has moved a boundary between LTR and RTL text.
+        private boolean mLanguageDirectionChanged = false;
 
         public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) {
             super(drawableLtr, drawableRtl);
@@ -4266,13 +4320,56 @@ public class Editor {
             int end = getWordEnd(offset);
             int start = getWordStart(offset);
 
-            if (offset > mPreviousOffset) {
+            if (mPrevX == UNSET_X_VALUE) {
+                mPrevX = x;
+            }
+
+            final int selectionEnd = mTextView.getSelectionEnd();
+            final boolean selectionEndRtl = layout.isRtlCharAt(selectionEnd);
+            final boolean atRtl = layout.isRtlCharAt(offset);
+            final boolean isLvlBoundary = layout.isLevelBoundary(offset);
+            boolean isExpanding;
+
+            // We can't determine if the user is expanding or shrinking the selection if they're
+            // on a bi-di boundary, so until they've moved past the boundary we'll just place
+            // the cursor at the current position.
+            if (isLvlBoundary || (selectionEndRtl && !atRtl) || (!selectionEndRtl && atRtl)) {
+                // We're on a boundary or this is the first direction change -- just update
+                // to the current position.
+                mLanguageDirectionChanged = true;
+                mTouchWordDelta = 0.0f;
+                positionAtCursorOffset(offset, false);
+                return;
+            } else if (mLanguageDirectionChanged && !isLvlBoundary) {
+                // We've just moved past the boundary so update the position. After this we can
+                // figure out if the user is expanding or shrinking to go by word or character.
+                positionAtCursorOffset(offset, false);
+                mTouchWordDelta = 0.0f;
+                mLanguageDirectionChanged = false;
+                return;
+            } else {
+                final float xDiff = x - mPrevX;
+                if (atRtl) {
+                    isExpanding = xDiff < 0 || currLine < mPrevLine;
+                } else {
+                    isExpanding = xDiff > 0 || currLine > mPrevLine;
+                }
+            }
+
+            if (isExpanding) {
                 // User is increasing the selection.
                 if (!mInWord || currLine > mPrevLine) {
-                    // We're not in a word, or we're on a different line so we'll expand by
-                    // word. First ensure the user has at least entered the next word.
-                    int midPoint = Math.min((end - start) / 2, 2);
-                    if (offset >= start + midPoint || currLine > mPrevLine) {
+                    // Sometimes words can be broken across lines (Chinese, hyphenation).
+                    // We still snap to the end of the word but we only use the letters on the
+                    // current line to determine if the user is far enough into the word to snap.
+                    int wordEndOnCurrLine = end;
+                    if (layout != null && layout.getLineForOffset(end) != currLine) {
+                        wordEndOnCurrLine = layout.getLineEnd(currLine);
+                    }
+                    final int offsetThresholdToSnap = start + ((wordEndOnCurrLine - start) / 2);
+                    if (offset >= offsetThresholdToSnap || currLine > mPrevLine) {
+                        // User is far enough into the word or on a different
+                        // line so we expand by word.
                         offset = end;
                     } else {
                         offset = mPreviousOffset;
@@ -4321,6 +4418,7 @@ public class Editor {
                 }
                 positionAtCursorOffset(offset, false);
             }
+            mPrevX = x;
         }
 
         @Override
@@ -4335,6 +4433,7 @@ public class Editor {
             if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                 // Reset the touch word offset when the user has lifted their finger.
                 mTouchWordDelta = 0.0f;
+                mPrevX = UNSET_X_VALUE;
             }
             return superResult;
         }
index 3ee273c..c40289e 100644 (file)
@@ -660,10 +660,11 @@ class FastScroller {
             maxWidth = containerWidth - adjacent.getRight();
         }
 
+        final int adjMaxHeight = Math.max(0, container.height());
         final int adjMaxWidth = Math.max(0, maxWidth - marginLeft - marginRight);
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
-        final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(container.height(),
-                MeasureSpec.UNSPECIFIED);
+        final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
+                adjMaxHeight, MeasureSpec.UNSPECIFIED);
         view.measure(widthMeasureSpec, heightMeasureSpec);
 
         // Align to the left or right.
@@ -700,10 +701,11 @@ class FastScroller {
 
         final Rect container = mContainerRect;
         final int containerWidth = container.width();
-        final int adjMaxWidth = containerWidth - marginLeft - marginRight;
+        final int adjMaxHeight = Math.max(0, container.height());
+        final int adjMaxWidth = Math.max(0, containerWidth - marginLeft - marginRight);
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(adjMaxWidth, MeasureSpec.AT_MOST);
-        final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(container.height(),
-                MeasureSpec.UNSPECIFIED);
+        final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
+                adjMaxHeight, MeasureSpec.UNSPECIFIED);
         preview.measure(widthMeasureSpec, heightMeasureSpec);
 
         // Align at the vertical center, 10% from the top.
@@ -766,10 +768,11 @@ class FastScroller {
         final View track = mTrackImage;
         final View thumb = mThumbImage;
         final Rect container = mContainerRect;
-        final int maxWidth = container.width();
+        final int maxWidth = Math.max(0, container.width());
+        final int maxHeight = Math.max(0, container.height());
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
-        final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(container.height(),
-                MeasureSpec.UNSPECIFIED);
+        final int heightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
+                maxHeight, MeasureSpec.UNSPECIFIED);
         track.measure(widthMeasureSpec, heightMeasureSpec);
 
         final int top;
index 7ca450a..280ff15 100644 (file)
@@ -230,28 +230,29 @@ public class FrameLayout extends ViewGroup {
         if (count > 1) {
             for (int i = 0; i < count; i++) {
                 final View child = mMatchParentChildren.get(i);
-
                 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
-                int childWidthMeasureSpec;
-                int childHeightMeasureSpec;
-                
+
+                final int childWidthMeasureSpec;
                 if (lp.width == LayoutParams.MATCH_PARENT) {
-                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
-                            getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
-                            lp.leftMargin - lp.rightMargin,
-                            MeasureSpec.EXACTLY);
+                    final int width = Math.max(0, getMeasuredWidth()
+                            - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
+                            - lp.leftMargin - lp.rightMargin);
+                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            width, MeasureSpec.EXACTLY);
                 } else {
                     childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                             getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                             lp.leftMargin + lp.rightMargin,
                             lp.width);
                 }
-                
+
+                final int childHeightMeasureSpec;
                 if (lp.height == LayoutParams.MATCH_PARENT) {
-                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
-                            getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
-                            lp.topMargin - lp.bottomMargin,
-                            MeasureSpec.EXACTLY);
+                    final int height = Math.max(0, getMeasuredHeight()
+                            - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
+                            - lp.topMargin - lp.bottomMargin);
+                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                            height, MeasureSpec.EXACTLY);
                 } else {
                     childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                             getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
index 056323d..4dcc242 100644 (file)
@@ -359,7 +359,7 @@ public class LinearLayout extends ViewGroup {
         }
 
         if (hasDividerBeforeChildAt(count)) {
-            final View child = getVirtualChildAt(count - 1);
+            final View child = getLastNonGoneChild();
             int bottom = 0;
             if (child == null) {
                 bottom = getHeight() - getPaddingBottom() - mDividerHeight;
@@ -371,6 +371,20 @@ public class LinearLayout extends ViewGroup {
         }
     }
 
+    /**
+     * Finds the last child that is not gone. The last child will be used as the reference for
+     * where the end divider should be drawn.
+     */
+    private View getLastNonGoneChild() {
+        for (int i = getVirtualChildCount() - 1; i >= 0; i--) {
+            View child = getVirtualChildAt(i);
+            if (child != null && child.getVisibility() != GONE) {
+                return child;
+            }
+        }
+        return null;
+    }
+
     void drawDividersHorizontal(Canvas canvas) {
         final int count = getVirtualChildCount();
         final boolean isLayoutRtl = isLayoutRtl();
@@ -392,7 +406,7 @@ public class LinearLayout extends ViewGroup {
         }
 
         if (hasDividerBeforeChildAt(count)) {
-            final View child = getVirtualChildAt(count - 1);
+            final View child = getLastNonGoneChild();
             int position;
             if (child == null) {
                 if (isLayoutRtl) {
@@ -627,21 +641,29 @@ public class LinearLayout extends ViewGroup {
      * @hide Pending API consideration. Currently only used internally by the system.
      */
     protected boolean hasDividerBeforeChildAt(int childIndex) {
-        if (childIndex == 0) {
-            return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
-        } else if (childIndex == getChildCount()) {
+        if (childIndex == getVirtualChildCount()) {
+            // Check whether the end divider should draw.
             return (mShowDividers & SHOW_DIVIDER_END) != 0;
-        } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
-            boolean hasVisibleViewBefore = false;
-            for (int i = childIndex - 1; i >= 0; i--) {
-                if (getChildAt(i).getVisibility() != GONE) {
-                    hasVisibleViewBefore = true;
-                    break;
-                }
+        }
+        boolean allViewsAreGoneBefore = allViewsAreGoneBefore(childIndex);
+        if (allViewsAreGoneBefore) {
+            // This is the first view that's not gone, check if beginning divider is enabled.
+            return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
+        } else {
+            return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0;
+        }
+    }
+
+    /**
+     * Checks whether all (virtual) child views before the given index are gone.
+     */
+    private boolean allViewsAreGoneBefore(int childIndex) {
+        for (int i = childIndex - 1; i >= 0; i--) {
+            if (getVirtualChildAt(i).getVisibility() != GONE) {
+                return false;
             }
-            return hasVisibleViewBefore;
         }
-        return false;
+        return true;
     }
 
     /**
index 534bfad..c6de5dd 100644 (file)
@@ -1127,10 +1127,19 @@ public class ListPopupWindow {
                     break;
                 }
 
-                // measure the hint's height to find how much more vertical space
-                // we need to add to the drop down's height
-                int widthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.AT_MOST);
-                int heightSpec = MeasureSpec.UNSPECIFIED;
+                // Measure the hint's height to find how much more vertical
+                // space we need to add to the drop down's height.
+                final int widthSize;
+                final int widthMode;
+                if (mDropDownWidth >= 0) {
+                    widthMode = MeasureSpec.AT_MOST;
+                    widthSize = mDropDownWidth;
+                } else {
+                    widthMode = MeasureSpec.UNSPECIFIED;
+                    widthSize = 0;
+                }
+                final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
+                final int heightSpec = MeasureSpec.UNSPECIFIED;
                 hintView.measure(widthSpec, heightSpec);
 
                 hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
index 49226cd..f45e750 100644 (file)
@@ -242,6 +242,38 @@ public class Switch extends CompoundButton {
                 com.android.internal.R.styleable.Switch_switchPadding, 0);
         mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);
 
+        ColorStateList thumbTintList = a.getColorStateList(
+                com.android.internal.R.styleable.Switch_thumbTint);
+        if (thumbTintList != null) {
+            mThumbTintList = thumbTintList;
+            mHasThumbTint = true;
+        }
+        PorterDuff.Mode thumbTintMode = Drawable.parseTintMode(
+                a.getInt(com.android.internal.R.styleable.Switch_thumbTintMode, -1), null);
+        if (mThumbTintMode != thumbTintMode) {
+            mThumbTintMode = thumbTintMode;
+            mHasThumbTintMode = true;
+        }
+        if (mHasThumbTint || mHasThumbTintMode) {
+            applyThumbTint();
+        }
+
+        ColorStateList trackTintList = a.getColorStateList(
+                com.android.internal.R.styleable.Switch_trackTint);
+        if (trackTintList != null) {
+            mTrackTintList = trackTintList;
+            mHasTrackTint = true;
+        }
+        PorterDuff.Mode trackTintMode = Drawable.parseTintMode(
+                a.getInt(com.android.internal.R.styleable.Switch_trackTintMode, -1), null);
+        if (mTrackTintMode != trackTintMode) {
+            mTrackTintMode = trackTintMode;
+            mHasTrackTintMode = true;
+        }
+        if (mHasTrackTint || mHasTrackTintMode) {
+            applyTrackTint();
+        }
+
         final int appearance = a.getResourceId(
                 com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
         if (appearance != 0) {
index dfe373f..7b58b5b 100644 (file)
@@ -292,6 +292,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     // New state used to change background based on whether this TextView is multiline.
     private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline };
 
+    // Accessibility action to share selected text.
+    private static final int ACCESSIBILITY_ACTION_SHARE = 0x10000000;
+
     // System wide time for last cut, copy or text changed action.
     static long sLastCutCopyOrTextChangedTime;
 
@@ -5231,7 +5234,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         // ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can
         // not be set. Do the test here instead.
         if (isInExtractedMode() && hasSelection() && mEditor != null
-                && mEditor.mTextActionMode == null) {
+                && mEditor.mTextActionMode == null && isShown() && hasWindowFocus()) {
             mEditor.startSelectionActionMode();
         }
 
@@ -5849,31 +5852,43 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
 
     @Override
     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK) {
-            boolean isInSelectionMode = mEditor != null && mEditor.mTextActionMode != null;
-
-            if (isInSelectionMode) {
-                if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
-                    KeyEvent.DispatcherState state = getKeyDispatcherState();
-                    if (state != null) {
-                        state.startTracking(event, this);
-                    }
-                    return true;
-                } else if (event.getAction() == KeyEvent.ACTION_UP) {
-                    KeyEvent.DispatcherState state = getKeyDispatcherState();
-                    if (state != null) {
-                        state.handleUpEvent(event);
-                    }
-                    if (event.isTracking() && !event.isCanceled()) {
-                        stopTextActionMode();
-                        return true;
-                    }
-                }
-            }
+        // Note: If the IME is in fullscreen mode and IMS#mExtractEditText is in text action mode,
+        // InputMethodService#onKeyDown and InputMethodService#onKeyUp are responsible to call
+        // InputMethodService#mExtractEditText.maybeHandleBackInTextActionMode(event).
+        if (keyCode == KeyEvent.KEYCODE_BACK && handleBackInTextActionModeIfNeeded(event)) {
+            return true;
         }
         return super.onKeyPreIme(keyCode, event);
     }
 
+    /**
+     * @hide
+     */
+    public boolean handleBackInTextActionModeIfNeeded(KeyEvent event) {
+        // Do nothing unless mEditor is in text action mode.
+        if (mEditor == null || mEditor.mTextActionMode == null) {
+            return false;
+        }
+
+        if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+            KeyEvent.DispatcherState state = getKeyDispatcherState();
+            if (state != null) {
+                state.startTracking(event, this);
+            }
+            return true;
+        } else if (event.getAction() == KeyEvent.ACTION_UP) {
+            KeyEvent.DispatcherState state = getKeyDispatcherState();
+            if (state != null) {
+                state.handleUpEvent(event);
+            }
+            if (event.isTracking() && !event.isCanceled()) {
+                stopTextActionMode();
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         int which = doKeyDown(keyCode, event, null);
@@ -8837,6 +8852,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
             if (canCut()) {
                 info.addAction(AccessibilityNodeInfo.ACTION_CUT);
             }
+            if (canShare()) {
+                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+                        ACCESSIBILITY_ACTION_SHARE,
+                        getResources().getString(com.android.internal.R.string.share)));
+            }
         }
 
         // Check for known input filter types.
@@ -8943,6 +8963,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
                 ensureIterableTextForAccessibilitySelectable();
                 return super.performAccessibilityActionInternal(action, arguments);
             }
+            case ACCESSIBILITY_ACTION_SHARE: {
+                if (isFocused() && canShare()) {
+                    if (onTextContextMenuItem(ID_SHARE)) {
+                        return true;
+                    }
+                }
+            } return false;
             default: {
                 return super.performAccessibilityActionInternal(action, arguments);
             }
@@ -9650,6 +9677,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
         // controllers interact with how selection behaves.
         if (mEditor != null) {
             mEditor.hideCursorAndSpanControllers();
+            mEditor.stopTextActionMode();
         }
         CharSequence text = getIterableTextForAccessibility();
         if (Math.min(start, end) >= 0 && Math.max(start, end) <= text.length()) {
index c97fdf4..197004c 100644 (file)
@@ -46,8 +46,12 @@ public final class Zygote {
 
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = 0;
-    /** Default user-specific external storage should be mounted. */
+    /** Default external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_DEFAULT = 1;
+    /** Read-only external storage should be mounted. */
+    public static final int MOUNT_EXTERNAL_READ = 2;
+    /** Read-write external storage should be mounted. */
+    public static final int MOUNT_EXTERNAL_WRITE = 3;
 
     private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
 
index fa870b9..3e86fac 100644 (file)
@@ -519,6 +519,10 @@ class ZygoteConnection {
                     niceName = arg.substring(arg.indexOf('=') + 1);
                 } else if (arg.equals("--mount-external-default")) {
                     mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
+                } else if (arg.equals("--mount-external-read")) {
+                    mountExternal = Zygote.MOUNT_EXTERNAL_READ;
+                } else if (arg.equals("--mount-external-write")) {
+                    mountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
                 } else if (arg.equals("--query-abi-list")) {
                     abiListQuery = true;
                 } else if (arg.startsWith("--instruction-set=")) {
index 1d0511f..0a01ae9 100644 (file)
@@ -17,6 +17,10 @@ import com.android.internal.R;
 
 /**
  * Takes care of unmounting and formatting external storage.
+ *
+ * @deprecated Please use {@link Intent#ACTION_MASTER_CLEAR} broadcast with extra
+ * {@link Intent#EXTRA_WIPE_EXTERNAL_STORAGE} to wipe and factory reset, or call
+ * {@link StorageManager#wipeAdoptableDisks} directly to format external storages.
  */
 public class ExternalStorageFormatter extends Service {
     static final String TAG = "ExternalStorageFormatter";
index 863506b..c869722 100644 (file)
@@ -30,6 +30,8 @@ import com.android.internal.util.Preconditions;
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.widget.FloatingToolbar;
 
+import java.util.Arrays;
+
 public class FloatingActionMode extends ActionMode {
 
     private static final int MAX_HIDE_DURATION = 3000;
@@ -42,7 +44,9 @@ public class FloatingActionMode extends ActionMode {
     private final Rect mContentRectOnWindow;
     private final Rect mPreviousContentRectOnWindow;
     private final int[] mViewPosition;
+    private final int[] mPreviousViewPosition;
     private final Rect mViewRect;
+    private final Rect mPreviousViewRect;
     private final Rect mScreenRect;
     private final View mOriginatingView;
     private final int mBottomAllowance;
@@ -75,7 +79,9 @@ public class FloatingActionMode extends ActionMode {
         mContentRectOnWindow = new Rect();
         mPreviousContentRectOnWindow = new Rect();
         mViewPosition = new int[2];
+        mPreviousViewPosition = new int[2];
         mViewRect = new Rect();
+        mPreviousViewRect = new Rect();
         mScreenRect = new Rect();
         mOriginatingView = Preconditions.checkNotNull(originatingView);
         mOriginatingView.getLocationInWindow(mViewPosition);
@@ -129,9 +135,17 @@ public class FloatingActionMode extends ActionMode {
 
     public void updateViewLocationInWindow() {
         checkToolbarInitialized();
+
         mOriginatingView.getLocationInWindow(mViewPosition);
         mOriginatingView.getGlobalVisibleRect(mViewRect);
-        repositionToolbar();
+
+        if (!Arrays.equals(mViewPosition, mPreviousViewPosition)
+                || !mViewRect.equals(mPreviousViewRect)) {
+            repositionToolbar();
+            mPreviousViewPosition[0] = mViewPosition[0];
+            mPreviousViewPosition[1] = mViewPosition[1];
+            mPreviousViewRect.set(mViewRect);
+        }
     }
 
     private void repositionToolbar() {
index 523663c..163b8ce 100644 (file)
@@ -23,6 +23,7 @@ import android.animation.ObjectAnimator;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -30,6 +31,7 @@ import android.graphics.Region;
 import android.graphics.drawable.ColorDrawable;
 import android.text.TextUtils;
 import android.util.Size;
+import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -83,6 +85,7 @@ public final class FloatingToolbar {
     private final FloatingToolbarPopup mPopup;
 
     private final Rect mContentRect = new Rect();
+    private final Rect mPreviousContentRect = new Rect();
 
     private Menu mMenu;
     private List<Object> mShowingMenuItems = new ArrayList<Object>();
@@ -108,8 +111,10 @@ public final class FloatingToolbar {
      * Initializes a floating toolbar.
      */
     public FloatingToolbar(Context context, Window window) {
-        mContext = Preconditions.checkNotNull(context);
-        mPopup = new FloatingToolbarPopup(window.getDecorView());
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(window);
+        mContext = applyDefaultTheme(context);
+        mPopup = new FloatingToolbarPopup(mContext, window.getDecorView());
     }
 
     /**
@@ -174,11 +179,13 @@ public final class FloatingToolbar {
             mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth);
             mShowingMenuItems = getShowingMenuItemsReferences(menuItems);
         }
-        mPopup.updateCoordinates(mContentRect);
         if (!mPopup.isShowing()) {
             mPopup.show(mContentRect);
+        } else if (!mPreviousContentRect.equals(mContentRect)) {
+            mPopup.updateCoordinates(mContentRect);
         }
         mWidthChanged = false;
+        mPreviousContentRect.set(mContentRect);
         return this;
     }
 
@@ -276,6 +283,7 @@ public final class FloatingToolbar {
         public static final int OVERFLOW_DIRECTION_UP = 0;
         public static final int OVERFLOW_DIRECTION_DOWN = 1;
 
+        private final Context mContext;
         private final View mParent;
         private final PopupWindow mPopupWindow;
         private final ViewGroup mContentContainer;
@@ -313,24 +321,8 @@ public final class FloatingToolbar {
                 };
         private final AnimatorSet mDismissAnimation;
         private final AnimatorSet mHideAnimation;
-        private final AnimationSet mOpenOverflowAnimation = new AnimationSet(true) {
-            @Override
-            public void cancel() {
-                if (hasStarted() && !hasEnded()) {
-                    super.cancel();
-                    setOverflowPanelAsContent();
-                }
-            }
-        };
-        private final AnimationSet mCloseOverflowAnimation = new AnimationSet(true) {
-            @Override
-            public void cancel() {
-                if (hasStarted() && !hasEnded()) {
-                    super.cancel();
-                    setMainPanelAsContent();
-                }
-            }
-        };
+        private final AnimationSet mOpenOverflowAnimation = new AnimationSet(true);
+        private final AnimationSet mCloseOverflowAnimation = new AnimationSet(true);
 
         private final Runnable mOpenOverflow = new Runnable() {
             @Override
@@ -375,9 +367,10 @@ public final class FloatingToolbar {
          * @param parent  A parent view to get the {@link android.view.View#getWindowToken()} token
          *      from.
          */
-        public FloatingToolbarPopup(View parent) {
+        public FloatingToolbarPopup(Context context, View parent) {
             mParent = Preconditions.checkNotNull(parent);
-            mContentContainer = createContentContainer(parent.getContext());
+            mContext = Preconditions.checkNotNull(context);
+            mContentContainer = createContentContainer(context);
             mPopupWindow = createPopupWindow(mContentContainer);
             mDismissAnimation = createExitAnimation(
                     mContentContainer,
@@ -415,7 +408,7 @@ public final class FloatingToolbar {
 
             mContentContainer.removeAllViews();
             if (mMainPanel == null) {
-                mMainPanel = new FloatingToolbarMainPanel(mParent.getContext(), mOpenOverflow);
+                mMainPanel = new FloatingToolbarMainPanel(mContext, mOpenOverflow);
             }
             List<MenuItem> overflowMenuItems =
                     mMainPanel.layoutMenuItems(menuItems, getToolbarWidth(suggestedWidth));
@@ -423,7 +416,7 @@ public final class FloatingToolbar {
             if (!overflowMenuItems.isEmpty()) {
                 if (mOverflowPanel == null) {
                     mOverflowPanel =
-                            new FloatingToolbarOverflowPanel(mParent.getContext(), mCloseOverflow);
+                            new FloatingToolbarOverflowPanel(mContext, mCloseOverflow);
                 }
                 mOverflowPanel.setMenuItems(overflowMenuItems);
                 mOverflowPanel.setOnMenuItemClickListener(menuItemClickListener);
@@ -540,7 +533,7 @@ public final class FloatingToolbar {
          * Returns the context this popup is running in.
          */
         public Context getContext() {
-            return mContentContainer.getContext();
+            return mContext;
         }
 
         private void refreshCoordinatesAndOverflowDirection(Rect contentRect) {
@@ -562,7 +555,7 @@ public final class FloatingToolbar {
                 } else if (availableHeightBelowContent >= getToolbarHeightWithVerticalMargin()) {
                     // There is enough space at the bottom of the content.
                     y = contentRect.bottom;
-                } else if (availableHeightBelowContent >= getEstimatedToolbarHeight(getContext())) {
+                } else if (availableHeightBelowContent >= getEstimatedToolbarHeight(mContext)) {
                     // Just enough space to fit the toolbar with no vertical margins.
                     y = contentRect.bottom - mMarginVertical;
                 } else {
@@ -621,7 +614,7 @@ public final class FloatingToolbar {
         }
 
         private int getToolbarHeightWithVerticalMargin() {
-            return getEstimatedToolbarHeight(mParent.getContext()) + mMarginVertical * 2;
+            return getEstimatedToolbarHeight(mContext) + mMarginVertical * 2;
         }
 
         /**
@@ -651,8 +644,24 @@ public final class FloatingToolbar {
         }
 
         private void cancelOverflowAnimations() {
-            mOpenOverflowAnimation.cancel();
-            mCloseOverflowAnimation.cancel();
+            if (mOpenOverflowAnimation.hasStarted()
+                    && !mOpenOverflowAnimation.hasEnded()) {
+                // Remove the animation listener, stop the animation,
+                // then trigger the lister explicitly so it is not posted
+                // to the message queue.
+                mOpenOverflowAnimation.setAnimationListener(null);
+                mContentContainer.clearAnimation();
+                mOnOverflowOpened.onAnimationEnd(null);
+            }
+            if (mCloseOverflowAnimation.hasStarted()
+                    && !mCloseOverflowAnimation.hasEnded()) {
+                // Remove the animation listener, stop the animation,
+                // then trigger the lister explicitly so it is not posted
+                // to the message queue.
+                mCloseOverflowAnimation.setAnimationListener(null);
+                mContentContainer.clearAnimation();
+                mOnOverflowClosed.onAnimationEnd(null);
+            }
         }
 
         /**
@@ -1477,6 +1486,17 @@ public final class FloatingToolbar {
         return animation;
     }
 
+    /**
+     * Returns a re-themed context with controlled look and feel for views.
+     */
+    private static Context applyDefaultTheme(Context originalContext) {
+        TypedArray a = originalContext.obtainStyledAttributes(new int[]{R.attr.isLightTheme});
+        boolean isLightTheme = a.getBoolean(0, true);
+        int themeId = isLightTheme ? R.style.Theme_Material_Light : R.style.Theme_Material;
+        a.recycle();
+        return new ContextThemeWrapper(originalContext, themeId);
+    }
+
     private static int getEstimatedToolbarHeight(Context context) {
         return context.getResources().getDimensionPixelSize(R.dimen.floating_toolbar_height);
     }
@@ -1486,18 +1506,6 @@ public final class FloatingToolbar {
                 .getDimensionPixelSize(R.dimen.floating_toolbar_menu_button_minimum_width);
     }
 
-    private static int getAdjustedToolbarWidth(Context context, int width) {
-        int maximumWidth = getScreenWidth(context) - 2 * context.getResources()
-                .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
-
-        if (width <= 0 || width > maximumWidth) {
-            int defaultWidth = context.getResources()
-                    .getDimensionPixelSize(R.dimen.floating_toolbar_preferred_width);
-            width = Math.min(defaultWidth, maximumWidth);
-        }
-        return width;
-    }
-
     /**
      * Returns the device's screen width.
      */
index 70a7805..e2cfa44 100644 (file)
@@ -682,18 +682,28 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
             break;
         case FAILED_TRANSACTION: {
             ALOGE("!!! FAILED BINDER TRANSACTION !!!  (parcel size = %d)", parcelSize);
+            const char* exceptionToThrow;
             char msg[128];
-            snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize);
             // TransactionTooLargeException is a checked exception, only throw from certain methods.
             // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
             //        but it is not the only one.  The Binder driver can return BR_FAILED_REPLY
             //        for other reasons also, such as if the transaction is malformed or
             //        refers to an FD that has been closed.  We should change the driver
             //        to enable us to distinguish these cases in the future.
-            jniThrowException(env, canThrowRemoteException
-                    ? "android/os/TransactionTooLargeException"
-                            : "java/lang/RuntimeException",
-                    parcelSize > 0 ? msg : NULL);
+            if (canThrowRemoteException && parcelSize > 200*1024) {
+                // bona fide large payload
+                exceptionToThrow = "android/os/TransactionTooLargeException";
+                snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize);
+            } else {
+                // Heuristic: a payload smaller than this threshold "shouldn't" be too
+                // big, so it's probably some other, more subtle problem.  In practice
+                // it nearly always means that the remote process died while the binder
+                // transaction was already in flight.
+                exceptionToThrow = "java/lang/RuntimeException";
+                snprintf(msg, sizeof(msg)-1,
+                        "Transaction failed on small parcel; remote process probably died");
+            }
+            jniThrowException(env, exceptionToThrow, msg);
         } break;
         case FDS_NOT_ALLOWED:
             jniThrowException(env, "java/lang/RuntimeException",
index a526223..daa6f82 100644 (file)
@@ -48,7 +48,6 @@
 #define LIB_SUFFIX_LEN (sizeof(LIB_SUFFIX) - 1)
 
 #define RS_BITCODE_SUFFIX ".bc"
-#define RS_BITCODE_SUFFIX_LEN (sizeof(RS_BITCODE_SUFFIX) -1)
 
 #define GDBSERVER "gdbserver"
 #define GDBSERVER_LEN (sizeof(GDBSERVER) - 1)
@@ -322,7 +321,8 @@ private:
 public:
     static NativeLibrariesIterator* create(ZipFileRO* zipFile) {
         void* cookie = NULL;
-        if (!zipFile->startIteration(&cookie)) {
+        // Do not specify a suffix to find both .so files and gdbserver.
+        if (!zipFile->startIteration(&cookie, APK_LIB, NULL /* suffix */)) {
             return NULL;
         }
 
@@ -337,11 +337,6 @@ public:
                 continue;
             }
 
-            // Make sure we're in the lib directory of the ZIP.
-            if (strncmp(fileName, APK_LIB, APK_LIB_LEN)) {
-                continue;
-            }
-
             // Make sure the filename is at least to the minimum library name size.
             const size_t fileNameLen = strlen(fileName);
             static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN;
@@ -529,7 +524,7 @@ com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode(JNIEnv *
         jlong apkHandle) {
     ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
     void* cookie = NULL;
-    if (!zipFile->startIteration(&cookie)) {
+    if (!zipFile->startIteration(&cookie, NULL /* prefix */, RS_BITCODE_SUFFIX)) {
         return APK_SCAN_ERROR;
     }
 
@@ -539,12 +534,9 @@ com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode(JNIEnv *
         if (zipFile->getEntryFileName(next, fileName, sizeof(fileName))) {
             continue;
         }
-
-        const size_t fileNameLen = strlen(fileName);
         const char* lastSlash = strrchr(fileName, '/');
         const char* baseName = (lastSlash == NULL) ? fileName : fileName + 1;
-        if (!strncmp(fileName + fileNameLen - RS_BITCODE_SUFFIX_LEN, RS_BITCODE_SUFFIX,
-                     RS_BITCODE_SUFFIX_LEN) && isFilenameSafe(baseName)) {
+        if (isFilenameSafe(baseName)) {
             zipFile->endIteration(cookie);
             return BITCODE_PRESENT;
         }
index 76db5d3..f7cfe0e 100644 (file)
@@ -66,6 +66,8 @@ static jmethodID gCallPostForkChildHooks;
 enum MountExternalKind {
   MOUNT_EXTERNAL_NONE = 0,
   MOUNT_EXTERNAL_DEFAULT = 1,
+  MOUNT_EXTERNAL_READ = 2,
+  MOUNT_EXTERNAL_WRITE = 3,
 };
 
 static void RuntimeAbort(JNIEnv* env) {
@@ -249,38 +251,49 @@ static void SetSchedulerPolicy(JNIEnv* env) {
 
 // Create a private mount namespace and bind mount appropriate emulated
 // storage for the given user.
-static bool MountEmulatedStorage(uid_t uid, jint mount_mode, bool force_mount_namespace) {
-  if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) {
-    return true;
-  }
-
-  // Create a second private mount namespace for our process
-  if (unshare(CLONE_NEWNS) == -1) {
-      ALOGW("Failed to unshare(): %s", strerror(errno));
-      return false;
-  }
+static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
+        bool force_mount_namespace) {
+    // See storage config details at http://source.android.com/tech/storage/
 
-  if (mount_mode == MOUNT_EXTERNAL_NONE) {
-    return true;
-  }
-
-  // See storage config details at http://source.android.com/tech/storage/
-  userid_t user_id = multiuser_get_user_id(uid);
-
-  // Bind mount user-specific storage into place
-  const String8 source(String8::format("/mnt/user/%d", user_id));
-  const String8 target(String8::format("/storage/self"));
+    // Create a second private mount namespace for our process
+    if (unshare(CLONE_NEWNS) == -1) {
+        ALOGW("Failed to unshare(): %s", strerror(errno));
+        return false;
+    }
 
-  if (fs_prepare_dir(source.string(), 0755, 0, 0) == -1) {
-    return false;
-  }
+    // Unmount storage provided by root namespace and mount requested view
+    umount2("/storage", MNT_FORCE);
+
+    String8 storageSource;
+    if (mount_mode == MOUNT_EXTERNAL_DEFAULT) {
+        storageSource = "/mnt/runtime_default";
+    } else if (mount_mode == MOUNT_EXTERNAL_READ) {
+        storageSource = "/mnt/runtime_read";
+    } else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
+        storageSource = "/mnt/runtime_write";
+    } else {
+        // Sane default of no storage visible
+        return true;
+    }
+    if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
+            NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
+        ALOGW("Failed to mount %s to /storage: %s", storageSource.string(), strerror(errno));
+        return false;
+    }
 
-  if (TEMP_FAILURE_RETRY(mount(source.string(), target.string(), NULL, MS_BIND, NULL)) == -1) {
-    ALOGW("Failed to mount %s to %s: %s", source.string(), target.string(), strerror(errno));
-    return false;
-  }
+    // Mount user-specific symlink helpers into place
+    userid_t user_id = multiuser_get_user_id(uid);
+    const String8 userSource(String8::format("/mnt/user/%d", user_id));
+    if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
+        return false;
+    }
+    if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
+            NULL, MS_BIND, NULL)) == -1) {
+        ALOGW("Failed to mount %s to /storage/self: %s", userSource.string(), strerror(errno));
+        return false;
+    }
 
-  return true;
+    return true;
 }
 
 static bool NeedsNoRandomizeWorkaround() {
@@ -543,7 +556,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
   pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
                                       debug_flags, rlimits,
                                       permittedCapabilities, effectiveCapabilities,
-                                      MOUNT_EXTERNAL_NONE, NULL, NULL, true, NULL,
+                                      MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
                                       NULL, NULL);
   if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
index 9637cf5..bada791 100644 (file)
         android:description="@string/permgroupdesc_contacts"
         android:priority="100" />
 
-    <!-- Allows an application to read the user's contacts data. -->
+    <!-- Allows an application to read the user's contacts data.
+        <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.READ_CONTACTS"
         android:permissionGroup="android.permission-group.CONTACTS"
         android:label="@string/permlab_readContacts"
         android:description="@string/permdesc_readContacts"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to write the user's contacts data. -->
+    <!-- Allows an application to write the user's contacts data.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.WRITE_CONTACTS"
         android:permissionGroup="android.permission-group.CONTACTS"
         android:label="@string/permlab_writeContacts"
         android:description="@string/permgroupdesc_calendar"
         android:priority="200" />
 
-    <!-- Allows an application to read the user's calendar data. -->
+    <!-- Allows an application to read the user's calendar data.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.READ_CALENDAR"
         android:permissionGroup="android.permission-group.CALENDAR"
         android:label="@string/permlab_readCalendar"
         android:description="@string/permdesc_readCalendar"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to write the user's calendar data. -->
+    <!-- Allows an application to write the user's calendar data.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.WRITE_CALENDAR"
         android:permissionGroup="android.permission-group.CALENDAR"
         android:label="@string/permlab_writeCalendar"
         android:description="@string/permgroupdesc_sms"
         android:priority="300" />
 
-    <!-- Allows an application to send SMS messages. -->
+    <!-- Allows an application to send SMS messages.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.SEND_SMS"
         android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_sendSms"
         android:permissionFlags="costsMoney"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to receive SMS messages. -->
+    <!-- Allows an application to receive SMS messages.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.RECEIVE_SMS"
         android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_receiveSms"
         android:description="@string/permdesc_receiveSms"
         android:protectionLevel="dangerous"/>
 
-    <!-- Allows an application to read SMS messages. -->
+    <!-- Allows an application to read SMS messages.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.READ_SMS"
         android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_readSms"
         android:description="@string/permdesc_readSms"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to receive WAP push messages. -->
+    <!-- Allows an application to receive WAP push messages.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.RECEIVE_WAP_PUSH"
         android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_receiveWapPush"
         android:description="@string/permdesc_receiveWapPush"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to monitor incoming MMS messages. -->
+    <!-- Allows an application to monitor incoming MMS messages.
+        <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.RECEIVE_MMS"
         android:permissionGroup="android.permission-group.SMS"
         android:label="@string/permlab_receiveMms"
          additional emergency information (if Internet access is available)
          when the alert is first received, and to delay presenting the info
          to the user until after the initial alert dialog is dismissed.
+         <p>Protection level: dangerous
          @hide Pending API council approval -->
     <permission android:name="android.permission.READ_CELL_BROADCASTS"
         android:permissionGroup="android.permission-group.SMS"
      targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
      grants your app this permission. If you don't need this permission, be sure your <a
      href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
-     targetSdkVersion}</a> is 4 or higher.-->
+     targetSdkVersion}</a> is 4 or higher.
+     <p>Protection level: dangerous
+     -->
     <permission android:name="android.permission.READ_EXTERNAL_STORAGE"
         android:permissionGroup="android.permission-group.STORAGE"
         android:label="@string/permlab_sdcardRead"
          <p>Starting in API level 19, this permission is <em>not</em> required to
          read/write files in your application-specific directories returned by
          {@link android.content.Context#getExternalFilesDir} and
-         {@link android.content.Context#getExternalCacheDir}. -->
+         {@link android.content.Context#getExternalCacheDir}.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
         android:permissionGroup="android.permission-group.STORAGE"
         android:label="@string/permlab_sdcardWrite"
         android:description="@string/permgroupdesc_location"
         android:priority="400" />
 
-    <!-- Allows an app to access precise location. -->
+    <!-- Allows an app to access precise location.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.ACCESS_FINE_LOCATION"
         android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessFineLocation"
         android:description="@string/permdesc_accessFineLocation"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an app to access approximate location. -->
+    <!-- Allows an app to access approximate location.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.ACCESS_COARSE_LOCATION"
         android:permissionGroup="android.permission-group.LOCATION"
         android:label="@string/permlab_accessCoarseLocation"
          targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
          grants your app this permission. If you don't need this permission, be sure your <a
          href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
-         targetSdkVersion}</a> is 4 or higher. -->
+         targetSdkVersion}</a> is 4 or higher.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.READ_PHONE_STATE"
         android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_readPhoneState"
         android:protectionLevel="dangerous" />
 
     <!-- Allows an application to initiate a phone call without going through
-        the Dialer user interface for the user to confirm the call. -->
+        the Dialer user interface for the user to confirm the call.
+        <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.CALL_PHONE"
         android:permissionGroup="android.permission-group.PHONE"
         android:permissionFlags="costsMoney"
          targetSdkVersion}</a> values are set to 15 or lower, the system implicitly
          grants your app this permission. If you don't need this permission, be sure your <a
          href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
-         targetSdkVersion}</a> is 16 or higher.</p> -->
+         targetSdkVersion}</a> is 16 or higher.</p>
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.READ_CALL_LOG"
         android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_readCallLog"
          targetSdkVersion}</a> values are set to 15 or lower, the system implicitly
          grants your app this permission. If you don't need this permission, be sure your <a
          href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
-         targetSdkVersion}</a> is 16 or higher.</p>  -->
+         targetSdkVersion}</a> is 16 or higher.</p>
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.WRITE_CALL_LOG"
         android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_writeCallLog"
         android:description="@string/permdesc_writeCallLog"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to add voicemails into the system. -->
+    <!-- Allows an application to add voicemails into the system.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
         android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_addVoicemail"
         android:description="@string/permdesc_addVoicemail"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an application to use SIP service. -->
+    <!-- Allows an application to use SIP service.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.USE_SIP"
         android:permissionGroup="android.permission-group.PHONE"
         android:description="@string/permdesc_use_sip"
 
     <!-- Allows an application to see the number being dialed during an outgoing
          call with the option to redirect the call to a different number or
-         abort the call altogether. -->
+         abort the call altogether.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
         android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_processOutgoingCalls"
         android:description="@string/permgroupdesc_microphone"
         android:priority="600" />
 
-    <!-- Allows an application to record audio. -->
+    <!-- Allows an application to record audio.
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.RECORD_AUDIO"
         android:permissionGroup="android.permission-group.MICROPHONE"
         android:label="@string/permlab_recordAudio"
          &lt;uses-feature&gt;}</a> manifest element for <em>all</em> camera features.
          If you do not require all camera features or can properly operate if a camera
          is not available, then you must modify your manifest as appropriate in order to
-         install on devices that don't support all camera features.</p> -->
+         install on devices that don't support all camera features.</p>
+         <p>Protection level: dangerous
+    -->
     <permission android:name="android.permission.CAMERA"
         android:permissionGroup="android.permission-group.CAMERA"
         android:label="@string/permlab_camera"
         android:priority="800" />
 
     <!-- Allows an application to access data from sensors that the user uses to
-         measure what is happening inside his/her body, such as heart rate. -->
+         measure what is happening inside his/her body, such as heart rate.
+         <p>Protection level: dangerous -->
     <permission android:name="android.permission.BODY_SENSORS"
         android:permissionGroup="android.permission-group.SENSORS"
         android:label="@string/permlab_bodySensors"
         android:description="@string/permdesc_bodySensors"
         android:protectionLevel="dangerous" />
 
-    <!-- Allows an app to use fingerprint hardware. -->
+    <!-- Allows an app to use fingerprint hardware.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.USE_FINGERPRINT"
         android:permissionGroup="android.permission-group.SENSORS"
         android:label="@string/permlab_useFingerprint"
     <!-- =============================================================== -->
     <eat-comment />
 
-    <!-- Allows an application to broadcast an Intent to set an alarm for the user. -->
+    <!-- Allows an application to broadcast an Intent to set an alarm for the user.
+         <p>Protection level: normal
+    -->
     <permission android:name="com.android.alarm.permission.SET_ALARM"
         android:label="@string/permlab_setAlarm"
         android:description="@string/permdesc_setAlarm"
     <!-- =============================================================== -->
     <eat-comment />
 
-    <!-- Allows an application to modify and remove existing voicemails in the system -->
+    <!-- Allows an application to modify and remove existing voicemails in the system
+        <p>Protection level: system|signature
+    -->
     <permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL"
         android:protectionLevel="system|signature" />
 
-    <!-- Allows an application to read voicemails in the system. -->
+    <!-- Allows an application to read voicemails in the system.
+         <p>Protection level: system|signature
+    -->
     <permission android:name="com.android.voicemail.permission.READ_VOICEMAIL"
         android:protectionLevel="system|signature" />
 
     <!-- ======================================= -->
     <eat-comment />
 
-    <!-- Allows an application to access extra location provider commands -->
+    <!-- Allows an application to access extra location provider commands
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"
         android:label="@string/permlab_accessLocationExtraCommands"
         android:description="@string/permdesc_accessLocationExtraCommands"
         android:protectionLevel="signature|system" />
     <uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
 
-    <!-- Allows an application to create mock location providers for testing. -->
+    <!-- @SystemApi Allows an application to create mock location providers for testing.
+         <p>Protection level: signature
+         @hide
+    -->
     <permission android:name="android.permission.ACCESS_MOCK_LOCATION"
         android:protectionLevel="signature" />
 
     <!-- ======================================= -->
     <eat-comment />
 
-    <!-- Allows applications to open network sockets. -->
+    <!-- Allows applications to open network sockets.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.INTERNET"
         android:description="@string/permdesc_createNetworkSockets"
         android:label="@string/permlab_createNetworkSockets"
         android:protectionLevel="normal" />
 
-    <!-- Allows applications to access information about networks -->
+    <!-- Allows applications to access information about networks
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.ACCESS_NETWORK_STATE"
         android:description="@string/permdesc_accessNetworkState"
         android:label="@string/permlab_accessNetworkState"
         android:protectionLevel="normal" />
 
-    <!-- Allows applications to access information about Wi-Fi networks -->
+    <!-- Allows applications to access information about Wi-Fi networks.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.ACCESS_WIFI_STATE"
         android:description="@string/permdesc_accessWifiState"
         android:label="@string/permlab_accessWifiState"
         android:protectionLevel="normal" />
 
-    <!-- Allows applications to change Wi-Fi connectivity state -->
+    <!-- Allows applications to change Wi-Fi connectivity state.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.CHANGE_WIFI_STATE"
         android:description="@string/permdesc_changeWifiState"
         android:label="@string/permlab_changeWifiState"
     <!-- ======================================= -->
     <eat-comment />
 
-    <!-- Allows applications to connect to paired bluetooth devices -->
+    <!-- Allows applications to connect to paired bluetooth devices.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.BLUETOOTH"
         android:description="@string/permdesc_bluetooth"
         android:label="@string/permlab_bluetooth"
         android:protectionLevel="normal" />
 
-    <!-- Allows applications to discover and pair bluetooth devices -->
+    <!-- Allows applications to discover and pair bluetooth devices.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.BLUETOOTH_ADMIN"
         android:description="@string/permdesc_bluetoothAdmin"
         android:label="@string/permlab_bluetoothAdmin"
     <permission android:name="android.permission.BLUETOOTH_STACK"
         android:protectionLevel="signature" />
 
-    <!-- Allows applications to perform I/O operations over NFC -->
+    <!-- Allows applications to perform I/O operations over NFC.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.NFC"
         android:description="@string/permdesc_nfc"
         android:label="@string/permlab_nfc"
         android:permissionGroupFlags="personalInfo"
         android:priority="1000" />
 
-    <!-- Allows access to the list of accounts in the Accounts Service -->
+    <!-- Allows access to the list of accounts in the Accounts Service.
+        <p>Protection level: normal
+    -->
     <permission android:name="android.permission.GET_ACCOUNTS"
         android:permissionGroup="android.permission-group.CONTACTS"
         android:protectionLevel="normal"
     <!-- ================================== -->
     <eat-comment />
 
-    <!-- Allows applications to enter Wi-Fi Multicast mode -->
+    <!-- Allows applications to enter Wi-Fi Multicast mode.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"
         android:description="@string/permdesc_changeWifiMulticastState"
         android:label="@string/permlab_changeWifiMulticastState"
         android:protectionLevel="normal" />
 
-    <!-- Allows access to the vibrator -->
+    <!-- Allows access to the vibrator.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.VIBRATE"
         android:label="@string/permlab_vibrate"
         android:description="@string/permdesc_vibrate"
         android:protectionLevel="normal" />
 
-    <!-- Allows access to the flashlight -->
+    <!-- Allows access to the flashlight.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.FLASHLIGHT"
         android:label="@string/permlab_flashlight"
         android:description="@string/permdesc_flashlight"
         android:protectionLevel="normal" />
 
     <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
-         from dimming -->
+         from dimming.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.WAKE_LOCK"
         android:label="@string/permlab_wakeLock"
         android:description="@string/permdesc_wakeLock"
         android:protectionLevel="normal" />
 
-    <!-- Allows using the device's IR transmitter, if available -->
+    <!-- Allows using the device's IR transmitter, if available.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.TRANSMIT_IR"
         android:label="@string/permlab_transmitIr"
         android:description="@string/permdesc_transmitIr"
     <!-- ==================================================== -->
     <eat-comment />
 
-    <!-- Allows an application to modify global audio settings -->
+    <!-- Allows an application to modify global audio settings.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"
         android:label="@string/permlab_modifyAudioSettings"
         android:description="@string/permdesc_modifyAudioSettings"
     <permission android:name="android.permission.ACCESS_MTP"
         android:protectionLevel="signature|system" />
 
-    <!-- Allows access to hardware peripherals.  Intended only for hardware testing.
-    <p>Not for use by third-party applications. -->
+    <!-- @SystemApi Allows access to hardware peripherals.  Intended only for hardware testing.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.HARDWARE_TEST"
         android:protectionLevel="signature" />
 
         android:protectionLevel="system|signature" />
 
     <!-- Must be required by a {@link android.telecom.InCallService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: system|signature
+    -->
     <permission android:name="android.permission.BIND_INCALL_SERVICE"
         android:protectionLevel="system|signature" />
 
         android:protectionLevel="system|signature" />
 
     <!-- Must be required by a {@link android.telecom.ConnectionService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: system|signature
+    -->
     <permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
         android:protectionLevel="system|signature" />
 
         android:protectionLevel="signature|system" />
 
     <!-- Allows an application to manage access to documents, usually as part
-         of a document picker. -->
+         of a document picker.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.MANAGE_DOCUMENTS"
         android:protectionLevel="signature" />
 
     <!-- ================================== -->
     <eat-comment />
 
-    <!-- Allows applications to disable the keyguard if it is not secure. -->
+    <!-- Allows applications to disable the keyguard if it is not secure.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.DISABLE_KEYGUARD"
         android:description="@string/permdesc_disableKeyguard"
         android:label="@string/permlab_disableKeyguard"
     <permission android:name="android.permission.GET_DETAILED_TASKS"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to change the Z-order of tasks -->
+    <!-- Allows an application to change the Z-order of tasks.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.REORDER_TASKS"
         android:label="@string/permlab_reorderTasks"
         android:description="@string/permdesc_reorderTasks"
         android:protectionLevel="normal" />
 
     <!-- Allows an application to call
-        {@link android.app.ActivityManager#killBackgroundProcesses}. -->
+        {@link android.app.ActivityManager#killBackgroundProcesses}.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"
         android:label="@string/permlab_killBackgroundProcesses"
         android:description="@string/permdesc_killBackgroundProcesses"
     <!-- ================================== -->
     <eat-comment />
 
-    <!-- Allows applications to set the wallpaper -->
+    <!-- Allows applications to set the wallpaper.
+         <p>Protection level: normal
+     -->
     <permission android:name="android.permission.SET_WALLPAPER"
         android:label="@string/permlab_setWallpaper"
         android:description="@string/permdesc_setWallpaper"
         android:protectionLevel="normal" />
 
-    <!-- Allows applications to set the wallpaper hints -->
+    <!-- Allows applications to set the wallpaper hints.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.SET_WALLPAPER_HINTS"
         android:label="@string/permlab_setWallpaperHints"
         android:description="@string/permdesc_setWallpaperHints"
     <permission android:name="android.permission.SET_TIME"
         android:protectionLevel="signature|system" />
 
-    <!-- Allows applications to set the system time zone -->
+    <!-- Allows applications to set the system time zone.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.SET_TIME_ZONE"
         android:label="@string/permlab_setTimeZone"
         android:description="@string/permdesc_setTimeZone"
     <!-- ==================================================== -->
     <eat-comment />
 
-    <!-- Allows an application to expand or collapse the status bar. -->
+    <!-- Allows an application to expand or collapse the status bar.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.EXPAND_STATUS_BAR"
         android:label="@string/permlab_expandStatusBar"
         android:description="@string/permdesc_expandStatusBar"
     <!-- ============================================================== -->
     <eat-comment />
 
-    <!-- Allows an application to install a shortcut in Launcher -->
+    <!-- Allows an application to install a shortcut in Launcher.
+         <p>Protection level: normal
+    -->
     <permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
         android:label="@string/permlab_install_shortcut"
         android:description="@string/permdesc_install_shortcut"
         android:protectionLevel="normal"/>
 
-    <!-- Allows an application to uninstall a shortcut in Launcher -->
+    <!-- Allows an application to uninstall a shortcut in Launcher.
+         <p>Protection level: normal
+    -->
     <permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
         android:label="@string/permlab_uninstall_shortcut"
         android:description="@string/permdesc_uninstall_shortcut"
     <!-- ==================================================== -->
     <eat-comment />
 
-    <!-- Allows applications to read the sync settings -->
+    <!-- Allows applications to read the sync settings.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.READ_SYNC_SETTINGS"
         android:description="@string/permdesc_readSyncSettings"
         android:label="@string/permlab_readSyncSettings"
         android:protectionLevel="normal" />
 
-    <!-- Allows applications to write the sync settings -->
+    <!-- Allows applications to write the sync settings.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.WRITE_SYNC_SETTINGS"
         android:description="@string/permdesc_writeSyncSettings"
         android:label="@string/permlab_writeSyncSettings"
         android:protectionLevel="normal" />
 
-    <!-- Allows applications to read the sync stats -->
+    <!-- Allows applications to read the sync stats.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.READ_SYNC_STATS"
         android:description="@string/permdesc_readSyncStats"
         android:label="@string/permlab_readSyncStats"
         android:description="@string/permdesc_persistentActivity"
         android:protectionLevel="normal" />
 
-    <!-- Allows an application to find out the space used by any package. -->
+    <!-- Allows an application to find out the space used by any package.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.GET_PACKAGE_SIZE"
         android:label="@string/permlab_getPackageSize"
         android:description="@string/permdesc_getPackageSize"
          system to start and allowing applications to have themselves
          running without the user being aware of them.  As such, you must
          explicitly declare your use of this facility to make that visible
-         to the user. -->
+         to the user.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"
         android:label="@string/permlab_receiveBootCompleted"
         android:description="@string/permdesc_receiveBootCompleted"
     <!-- Allows an application to broadcast sticky intents.  These are
          broadcasts whose data is held by the system after being finished,
          so that clients can quickly retrieve that data without having
-         to wait for the next broadcast. -->
+         to wait for the next broadcast.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.BROADCAST_STICKY"
         android:label="@string/permlab_broadcastSticky"
         android:description="@string/permdesc_broadcastSticky"
     <permission android:name="android.permission.WRITE_APN_SETTINGS"
         android:protectionLevel="signature|system" />
 
-    <!-- Allows applications to change network connectivity state -->
+    <!-- Allows applications to change network connectivity state.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.CHANGE_NETWORK_STATE"
         android:description="@string/permdesc_changeNetworkState"
         android:label="@string/permlab_changeNetworkState"
         android:protectionLevel="normal" />
 
     <!-- Allows an application to clear the caches of all installed
-         applications on the device.  -->
+         applications on the device.
+         <p>Protection level: system|signature
+    -->
     <permission android:name="android.permission.CLEAR_APP_CACHE"
         android:protectionLevel="signatureOrSystem" />
 
     <permission android:name="android.permission.STATUS_BAR_SERVICE"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to force a BACK operation on whatever is the
+    <!-- @SystemApi Allows an application to force a BACK operation on whatever is the
          top activity.
-         <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.FORCE_BACK"
         android:protectionLevel="signature" />
 
     <permission android:name="android.permission.UPDATE_APP_OPS_STATS"
         android:protectionLevel="signature|system" />
 
-    <!-- Allows an application to open windows that are for use by parts
+    <!-- @SystemApi Allows an application to open windows that are for use by parts
          of the system user interface.
-         <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to manage (create, destroy,
+    <!-- @SystemApi Allows an application to manage (create, destroy,
          Z-order) application tokens in the window manager.
-         <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.MANAGE_APP_TOKENS"
         android:protectionLevel="signature" />
 
     <permission android:name="android.permission.FREEZE_SCREEN"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to inject user events (keys, touch, trackball)
+    <!-- @SystemApi Allows an application to inject user events (keys, touch, trackball)
          into the event stream and deliver them to ANY window.  Without this
          permission, you can only deliver events to windows in your own process.
-         <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.INJECT_EVENTS"
         android:protectionLevel="signature" />
 
     <permission android:name="android.permission.TEMPORARY_ENABLE_ACCESSIBILITY"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to watch and control how activities are
+    <!-- @SystemApi Allows an application to watch and control how activities are
          started globally in the system.  Only for is in debugging
          (usually the monkey command).
-         <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.SET_ACTIVITY_WATCHER"
         android:protectionLevel="signature" />
 
     <permission android:name="android.permission.STOP_APP_SWITCHES"
         android:protectionLevel="signature|system" />
 
-    <!-- Allows an application to retrieve private information about
+    <!-- @SystemApi Allows an application to retrieve private information about
          the current top activity, such as any assist context it can provide.
-         <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.GET_TOP_ACTIVITY_INFO"
         android:protectionLevel="signature" />
 
         android:protectionLevel="signature" />
 
     <!-- Must be required by an {@link android.inputmethodservice.InputMethodService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_INPUT_METHOD"
         android:protectionLevel="signature" />
 
     <!-- Must be required by an {@link android.media.midi.MidiDeviceService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_MIDI_DEVICE_SERVICE"
         android:protectionLevel="signature" />
 
     <!-- Must be required by an {@link android.accessibilityservice.AccessibilityService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
         android:protectionLevel="signature" />
 
     <!-- Must be required by a {@link android.printservice.PrintService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_PRINT_SERVICE"
         android:protectionLevel="signature" />
 
     <!-- Must be required by a {@link android.nfc.cardemulation.HostApduService}
          or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only
-         the system can bind to it. -->
+         the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_NFC_SERVICE"
         android:protectionLevel="signature" />
 
         android:protectionLevel="signature" />
 
     <!-- Must be required by a TextService (e.g. SpellCheckerService)
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_TEXT_SERVICE"
         android:protectionLevel="signature" />
 
     <!-- Must be required by a {@link android.net.VpnService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_VPN_SERVICE"
         android:protectionLevel="signature" />
 
     <!-- Must be required by a {@link android.service.wallpaper.WallpaperService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: system|signature
+    -->
     <permission android:name="android.permission.BIND_WALLPAPER"
         android:protectionLevel="signature|system" />
 
     <!-- Must be required by a {@link android.service.voice.VoiceInteractionService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_VOICE_INTERACTION"
         android:protectionLevel="signature" />
 
         android:protectionLevel="signature" />
 
     <!-- Must be required by a {@link android.media.tv.TvInputService}
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_TV_INPUT"
         android:protectionLevel="signature|system" />
 
         android:protectionLevel="signature" />
 
     <!-- Must be required by device administration receiver, to ensure that only the
-         system can interact with it. -->
+         system can interact with it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_DEVICE_ADMIN"
         android:protectionLevel="signature" />
 
     <permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
         android:protectionLevel="signature|system" />
 
-    <!-- Allows low-level access to setting the orientation (actually
+    <!-- @SystemApi Allows low-level access to setting the orientation (actually
          rotation) of the screen.
-         <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.SET_ORIENTATION"
         android:protectionLevel="signature" />
 
-    <!-- Allows low-level access to setting the pointer speed.
-         <p>Not for use by third-party applications. -->
+    <!-- @SystemApi Allows low-level access to setting the pointer speed.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.SET_POINTER_SPEED"
         android:protectionLevel="signature" />
 
 
     <!-- Allows an application to request installing packages. Apps
          targeting APIs greater than 22 must hold this permission in
-         order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}.-->
+         order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"
         android:label="@string/permlab_requestInstallPackages"
         android:description="@string/permdesc_requestInstallPackages"
     <permission android:name="android.permission.INSTALL_PACKAGES"
         android:protectionLevel="signature|system" />
 
-    <!-- Allows an application to clear user data.
-    <p>Not for use by third-party applications. -->
+    <!-- @SystemApi Allows an application to clear user data.
+         <p>Not for use by third-party applications
+         @hide
+    -->
     <permission android:name="android.permission.CLEAR_APP_USER_DATA"
         android:protectionLevel="signature" />
 
     <permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
         android:protectionLevel="signatureOrSystem" />
 
-    <!-- Allows an application to use SurfaceFlinger's low level features.
-    <p>Not for use by third-party applications. -->
+    <!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.ACCESS_SURFACE_FLINGER"
         android:protectionLevel="signature" />
 
     <permission android:name="android.permission.MEDIA_CONTENT_CONTROL"
         android:protectionLevel="signature|system" />
 
-    <!-- Required to be able to disable the device (very dangerous!).
-    <p>Not for use by third-party applications.. -->
+    <!-- @SystemApi Required to be able to disable the device (very dangerous!).
+         <p>Not for use by third-party applications.
+         @hide
+    -->
     <permission android:name="android.permission.BRICK"
         android:protectionLevel="signature" />
 
     <permission android:name="android.permission.REBOOT"
         android:protectionLevel="signature|system" />
 
-   <!-- Allows low-level access to power management.
-   <p>Not for use by third-party applications. -->
-    <permission android:name="android.permission.DEVICE_POWER"
+   <!-- @SystemApi Allows low-level access to power management.
+        <p>Not for use by third-party applications.
+        @hide
+    -->
+   <permission android:name="android.permission.DEVICE_POWER"
         android:protectionLevel="signature" />
 
    <!-- Allows access to the PowerManager.userActivity function.
 
     <!-- Run as a manufacturer test application, running as the root user.
          Only available when the device is running in manufacturer test mode.
-         <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+    -->
     <permission android:name="android.permission.FACTORY_TEST"
         android:protectionLevel="signature" />
 
     <!-- Allows an application to broadcast a notification that an application
          package has been removed.
-         <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+    -->
     <permission android:name="android.permission.BROADCAST_PACKAGE_REMOVED"
         android:protectionLevel="signature" />
 
     <!-- Allows an application to broadcast an SMS receipt notification.
-    <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+    -->
     <permission android:name="android.permission.BROADCAST_SMS"
         android:protectionLevel="signature" />
 
     <!-- Allows an application to broadcast a WAP PUSH receipt notification.
-    <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.
+    -->
     <permission android:name="android.permission.BROADCAST_WAP_PUSH"
         android:protectionLevel="signature" />
 
     <permission android:name="android.permission.ACCESS_NOTIFICATIONS"
         android:protectionLevel="signature|system" />
 
-    <!-- Marker permission for applications that wish to access notification policy. -->
+    <!-- Marker permission for applications that wish to access notification policy.
+         <p>Protection level: normal
+    -->
     <permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"
         android:description="@string/permdesc_access_notification_policy"
         android:label="@string/permlab_access_notification_policy"
 
     <!-- Must be required by an {@link
          android.service.notification.NotificationListenerService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
         android:protectionLevel="signature" />
 
     <!-- Must be required by a {@link
          android.service.chooser.ChooserTargetService}, to ensure that
-         only the system can bind to it. -->
+         only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE"
         android:protectionLevel="signature" />
 
         android:protectionLevel="signature" />
 
     <!-- Must be required by an {@link android.service.dreams.DreamService},
-         to ensure that only the system can bind to it. -->
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
     <permission android:name="android.permission.BIND_DREAM_SERVICE"
         android:protectionLevel="signature" />
 
 
     <!-- The system process that is allowed to bind to services in carrier apps will
          have this permission. Carrier apps should use this permission to protect
-         their services that only the system is allowed to bind to. -->
+         their services that only the system is allowed to bind to.
+         <p>Protection level: system|signature
+    -->
     <permission android:name="android.permission.BIND_CARRIER_SERVICES"
         android:label="@string/permlab_bindCarrierServices"
         android:description="@string/permdesc_bindCarrierServices"
index 1b58ce5..482f91f 100644 (file)
@@ -18,7 +18,8 @@
 <Button xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="match_parent"
-    android:minWidth="@dimen/floating_toolbar_menu_button_side_padding"
+    android:minWidth="@dimen/floating_toolbar_menu_button_minimum_width"
+    android:minHeight="@dimen/floating_toolbar_height"
     android:paddingStart="@dimen/floating_toolbar_menu_button_side_padding"
     android:paddingEnd="@dimen/floating_toolbar_menu_button_side_padding"
     android:paddingTop="0dp"
index 33c9c60..fd47d49 100644 (file)
@@ -27,6 +27,9 @@
         <!-- ============== -->
         <eat-comment />
 
+        <!-- Specifies that a theme has a light background with dark text on top.  -->
+        <attr name="isLightTheme" format="boolean" />
+
         <!-- Default color of foreground imagery. -->
         <attr name="colorForeground" format="color" />
         <!-- Default color of foreground imagery on an inverted background. -->
index f6cefba..4bc2205 100755 (executable)
          application is desired. -->
     <string name="default_sms_application" translatable="false">com.android.mms</string>
 
+    <!-- Default web browser.  This is the package name of the application that will
+         be the default browser when the device first boots.  Afterwards the user
+         can select whatever browser app they wish to use as the default.
+
+         If this string is empty or the specified package does not exist, then
+         the behavior will be as though no app was named as an explicit default. -->
+    <string name="default_browser" translatable="false"></string>
+
     <!-- Enable/disable default bluetooth profiles:
         HSP_AG, ObexObjectPush, Audio, NAP -->
     <bool name="config_bluetooth_default_profiles">true</bool>
          (range of 18 - 21 kHz). -->
     <bool name="config_supportSpeakerNearUltrasound">true</bool>
 
+    <!-- Flag indicating device support for EAP SIM, AKA, AKA' -->
+    <bool name="config_eap_sim_based_auth_supported">true</bool>
 </resources>
index 5c974bd..5288fa3 100644 (file)
     <string name="permgroupdesc_phone">make and manage phone calls</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgrouplab_sensors">Sensors</string>
+    <string name="permgrouplab_sensors">Body Sensors</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgroupdesc_sensors">access sensor data about your vital signs</string>
 
index 3a1a156..ebbbc4c 100755 (executable)
   <java-symbol type="attr" name="windowFixedHeightMajor" />
   <java-symbol type="attr" name="windowFixedHeightMinor" />
   <java-symbol type="attr" name="accessibilityFocusedDrawable"/>
+  <java-symbol type="attr" name="isLightTheme"/>
 
   <java-symbol type="bool" name="action_bar_embed_tabs" />
   <java-symbol type="bool" name="action_bar_embed_tabs_pre_jb" />
   <java-symbol type="string" name="sipAddressTypeOther" />
   <java-symbol type="string" name="sipAddressTypeWork" />
   <java-symbol type="string" name="default_sms_application" />
+  <java-symbol type="string" name="default_browser" />
   <java-symbol type="string" name="sms_control_message" />
   <java-symbol type="string" name="sms_control_title" />
   <java-symbol type="string" name="sms_control_no" />
   <java-symbol type="drawable" name="ic_dialog_alert_material" />
 
   <java-symbol type="bool" name="allow_stacked_button_bar" />
-
+  <java-symbol type="bool" name="config_eap_sim_based_auth_supported" />
 </resources>
index b7acdd4..c230645 100644 (file)
@@ -42,6 +42,7 @@ please see themes_device_defaults.xml.
     -->
     <style name="Theme">
 
+        <item name="isLightTheme">false</item>
         <item name="colorForeground">@color/bright_foreground_dark</item>
         <item name="colorForegroundInverse">@color/bright_foreground_dark_inverse</item>
         <item name="colorBackground">@color/background_dark</item>
@@ -472,6 +473,7 @@ please see themes_device_defaults.xml.
          background will be a light color.
          <p>This is designed for API level 10 and lower.</p>-->
     <style name="Theme.Light">
+        <item name="isLightTheme">true</item>
         <item name="windowBackground">@drawable/screen_background_selector_light</item>
         <item name="windowClipToOutline">false</item>
 
index 9548e51..5783a49 100644 (file)
     <shortcode country="fi" pattern="\\d{5,6}" premium="0600.*|0700.*|171(?:59|63)" free="116\\d{3}" />
 
     <!-- France: 5 digits, free: 3xxxx, premium [4-8]xxxx, plus EU:
-         http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements -->
+         http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements,
+         visual voicemail code for Orange: 21101 -->
     <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}|21101" />
 
     <!-- United Kingdom (Great Britain): 4-6 digits, common codes [5-8]xxxx, plus EU:
-         http://www.short-codes.com/media/Co-regulatoryCodeofPracticeforcommonshortcodes170206.pdf -->
+         http://www.short-codes.com/media/Co-regulatoryCodeofPracticeforcommonshortcodes170206.pdf,
+         visual voicemail code for EE: 887 -->
     <shortcode country="gb" pattern="\\d{4,6}" premium="[5-8]\\d{4}" free="116\\d{3}|887" />
 
     <!-- Georgia: 4 digits, known premium codes listed -->
     <!-- Ukraine: 4 digits, known premium codes listed -->
     <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
 
-    <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm) -->
+    <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
+         visual voicemail code for T-Mobile: 122 -->
     <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" free="122|87902" />
 
 </shortcodes>
index 6ee6ffa..6b3b55e 100644 (file)
@@ -5,6 +5,4 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_PACKAGE_NAME := install_jni_lib_open_from_apk
 
-LOCAL_PAGE_ALIGN_JNI_SHARED_LIBRARIES := true
-
 include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
new file mode 100644 (file)
index 0000000..d2e2131
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.format;
+
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.format.Formatter.BytesResult;
+
+import java.util.Locale;
+
+public class FormatterTest extends AndroidTestCase {
+
+    private Locale mOriginalLocale;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mOriginalLocale = mContext.getResources().getConfiguration().locale;
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mOriginalLocale != null) {
+            setLocale(mOriginalLocale);
+        }
+        super.tearDown();
+    }
+
+    private void setLocale(Locale locale) {
+        Resources res = getContext().getResources();
+        Configuration config = res.getConfiguration();
+        config.locale = locale;
+        res.updateConfiguration(config, res.getDisplayMetrics());
+
+        Locale.setDefault(locale);
+    }
+
+    @SmallTest
+    public void testFormatBytes() {
+        setLocale(Locale.ENGLISH);
+
+        checkFormatBytes(0, true, "0.00", 0);
+        checkFormatBytes(0, false, "0.00", 0);
+
+        checkFormatBytes(1, true, "1.0", 1);
+        checkFormatBytes(1, false, "1.00", 1);
+
+        checkFormatBytes(12, true, "12", 12);
+        checkFormatBytes(12, false, "12.00", 12);
+
+        checkFormatBytes(123, true, "123", 123);
+        checkFormatBytes(123, false, "123", 123);
+
+        checkFormatBytes(812, true, "812", 812);
+        checkFormatBytes(812, false, "812", 812);
+
+        checkFormatBytes(912, true, "0.89", 911);
+        checkFormatBytes(912, false, "0.89", 911);
+
+        checkFormatBytes(9123, true, "8.9", 9113);
+        checkFormatBytes(9123, false, "8.91", 9123);
+
+        checkFormatBytes(9123000, true, "8.7", 9122611);
+        checkFormatBytes(9123000, false, "8.70", 9122611);
+
+        // The method doesn't really support negative values, but apparently people pass -1...
+        checkFormatBytes(-1, true, "-1.00", -1);
+        checkFormatBytes(-1, false, "-1.00", -1);
+
+        // Missing FLAG_CALCULATE_ROUNDED case.
+        BytesResult r = Formatter.formatBytes(getContext().getResources(), 1, 0);
+        assertEquals("1.00", r.value);
+        assertEquals(0, r.roundedBytes); // Didn't pass FLAG_CALCULATE_ROUNDED
+
+        // Make sure it works on different locales.
+        setLocale(new Locale("es", "ES"));
+        checkFormatBytes(9123000, false, "8,70", 9122611);
+    }
+
+    private void checkFormatBytes(long bytes, boolean useShort,
+            String expectedString, long expectedRounded) {
+        BytesResult r = Formatter.formatBytes(getContext().getResources(), bytes,
+                Formatter.FLAG_CALCULATE_ROUNDED | (useShort ? Formatter.FLAG_SHORTER : 0));
+        assertEquals(expectedString, r.value);
+        assertEquals(expectedRounded, r.roundedBytes);
+    }
+}
index c517201..377e6a1 100644 (file)
         <group gid="log" />
     </permission>
 
-    <permission name="android.permission.READ_EXTERNAL_STORAGE" perUser="true" >
-        <group gid="sdcard_r" />
-    </permission>
-
-    <permission name="android.permission.WRITE_EXTERNAL_STORAGE" perUser="true" >
-        <group gid="sdcard_r" />
-        <group gid="sdcard_rw" />
-    </permission>
-
-    <permission name="android.permission.ACCESS_ALL_EXTERNAL_STORAGE" >
-        <group gid="sdcard_r" />
-        <group gid="sdcard_rw" />
-        <group gid="sdcard_all" />
-    </permission>
-
     <permission name="android.permission.WRITE_MEDIA_STORAGE" >
         <group gid="media_rw" />
     </permission>
index 6f919da..cfab3c9 100644 (file)
@@ -52,9 +52,7 @@ must explicitly share resources and data. They do this by declaring the
 <em>permissions</em> they need for additional capabilities not provided by
 the basic sandbox. Applications statically declare the permissions they
 require, and the Android system prompts the user for consent at the time the
-application is installed. Android has no mechanism for granting permissions
-dynamically (at run-time) because it complicates the user experience to the
-detriment of security.</p>
+application is installed.</p>
 
 <p>The application sandbox does not depend on the technology used to build
 an application. In particular the Dalvik VM is not a security boundary, and
diff --git a/docs/html/images/tools/eclipse-notepad-pre-import--structure.png b/docs/html/images/tools/eclipse-notepad-pre-import--structure.png
new file mode 100644 (file)
index 0000000..b9c3814
Binary files /dev/null and b/docs/html/images/tools/eclipse-notepad-pre-import--structure.png differ
diff --git a/docs/html/images/tools/studio-import-destination-dir.png b/docs/html/images/tools/studio-import-destination-dir.png
new file mode 100644 (file)
index 0000000..d1c6c70
Binary files /dev/null and b/docs/html/images/tools/studio-import-destination-dir.png differ
diff --git a/docs/html/images/tools/studio-import-options.png b/docs/html/images/tools/studio-import-options.png
new file mode 100644 (file)
index 0000000..f14eca0
Binary files /dev/null and b/docs/html/images/tools/studio-import-options.png differ
diff --git a/docs/html/images/tools/studio-import-project-structure-android.png b/docs/html/images/tools/studio-import-project-structure-android.png
new file mode 100644 (file)
index 0000000..4cd7186
Binary files /dev/null and b/docs/html/images/tools/studio-import-project-structure-android.png differ
diff --git a/docs/html/images/tools/studio-import-project-structure-project.png b/docs/html/images/tools/studio-import-project-structure-project.png
new file mode 100644 (file)
index 0000000..c7ffda8
Binary files /dev/null and b/docs/html/images/tools/studio-import-project-structure-project.png differ
diff --git a/docs/html/images/tools/studio-import-summary.png b/docs/html/images/tools/studio-import-summary.png
new file mode 100644 (file)
index 0000000..a85e339
Binary files /dev/null and b/docs/html/images/tools/studio-import-summary.png differ
diff --git a/docs/html/images/tools/studio-select-project-forimport.png b/docs/html/images/tools/studio-select-project-forimport.png
new file mode 100644 (file)
index 0000000..c6a3599
Binary files /dev/null and b/docs/html/images/tools/studio-select-project-forimport.png differ
index 5559d1a..b89c068 100644 (file)
@@ -7,6 +7,14 @@ adt.zip.checksum=f64b7e50c84799f41c642218c35f1bbe
 @jd:body
 
 
+<p class="caution">
+  <strong>Important:</strong> Support for the Android Developer Tools (ADT) in Eclipse is ending,
+  per our <a href=
+  "http://android-developers.blogspot.com/2015/06/an-update-on-eclipse-android-developer.html"
+  class="external-link">announcement</a>. You should migrate your app development projects to
+  Android Studio as soon as possible. For more information on transitioning to Android Studio, see
+  <a href="{@docRoot}sdk/installing/migrate.html">Migrating to Android Studio</a>.
+</p>
 
 <p>Android offers a custom plugin for the Eclipse IDE, called Android
 Development Tools (ADT). This plugin provides a powerful, integrated
@@ -15,15 +23,6 @@ of Eclipse to let you quickly set up new Android projects, build an app
 UI, debug your app, and export signed (or unsigned) app packages (APKs) for distribution.
 </p>
 
-<p class="note"><strong>Note:</strong>
-If you have been using Eclipse with ADT, be aware that <a
-href="{@docRoot}tools/studio/index.html">Android Studio</a> is now the official IDE
-for Android, so you should migrate to Android Studio to receive all the
-latest IDE updates. For help moving projects,
-see <a href="/sdk/installing/migrate.html">Migrating to Android
-Studio</a>.</p>
-
-
 <p>You should install the ADT plugin
 only if you already have an Eclipse installation that you want to continue using.
 Your existing Eclipse installation must meet these requirements:</p>
index 345e89a..d982939 100644 (file)
@@ -4,53 +4,264 @@ page.title=Migrating to Android Studio
 
 <div id="qv-wrapper">
 <div id="qv">
+
+
+<h2>In this document</h2>
+<ol>
+  <li><a href="#overview">Migration Overview</a></li>
+  <li><a href="#prerequisites">Migration Prerequisites</a></li>
+  <li><a href="#migrate">Importing Projects to Android Studio</a></li>
+  <li><a href="#post-migration">Validating imported projects</a></li>
+</ol>
+
+
 <h2>See also</h2>
 <ul>
+  <li><a href="{@docRoot}tools/studio/eclipse-transition-guide.html">
+    Transition Guide for Eclipse ADT</a></li>
   <li><a href="http://confluence.jetbrains.com/display/IntelliJIDEA/FAQ+on+Migrating+to+IntelliJ+IDEA"
-  class="external-link">IntelliJ FAQ on migrating to IntelliJ IDEA</a></li>
- <li><a href="http://confluence.jetbrains.com/display/IntelliJIDEA/Working+in+Eclipse+Compatibility+Mode" class="external-link"
- >Eclipse Compatibility Mode</a></li>
- <li><a href="http://confluence.jetbrains.com/display/IntelliJIDEA/FAQ+on+Migrating+to+IntelliJ+IDEA" class="external-link"
- >FAQ on Migrating</a></li>
+    class="external-link">IntelliJ FAQ on migrating to IntelliJ IDEA</a></li>
+  <li><a href="http://confluence.jetbrains.com/display/IntelliJIDEA/IntelliJ+IDEA+for+Eclipse+Users"
+    class="external-link">IntelliJ IDEA for Eclipse users</a></li>
+  <li><a href="{@docRoot}tools/studio/index.html">Android Studio Overview</a></li>
 </ul>
 </div>
 </div>
 
 
-<p>If you have been using <a href="{@docRoot}tools/help/adt.html">Eclipse with ADT</a>, be aware
-that <a href="{@docRoot}tools/studio/index.html">Android Studio</a> is now the official IDE for
-Android, so you should migrate to Android Studio to receive all the latest IDE updates.</p>
+<p>Migrating from Eclipse ADT to Android Studio requires adapting to a new project structure,
+build system, and IDE functionality. To simplify the migration process, Android Studio provides an
+import tool so you can quickly transition your Eclipse ADT workspaces and Ant build scripts to
+Android Studio projects and <a href="http://www.gradle.org">Gradle</a>-based build files.</p>
 
-<p>To migrate existing Android projects, simply import them using Android Studio:</p>
+<p>This document provides an overview of the migration process and walks you
+through a sample import procedure. For more information about Android Studio features and the
+Gradle-based build system, see <a href="{@docRoot}tools/studio/index.html">Android Studio Overview</a>
+and <a href="{@docRoot}tools/building/configuring-gradle.html">Configuring Gradle Builds</a>.</p>
 
-<ol>
-  <li>In Android Studio, from the main menu or the <strong>Welcome to Android Studio</strong> page,
-  choose <strong>File &gt; Import Project</strong>.</li>
-  <li> Select the Eclipse root project directory</strong> and click <strong>OK</strong>.
-  <p class="note"><strong>Note:</strong> The Eclipse root directory must contain the
-  <code>AndroidManifest.xml</code> file. Also, the root directory must contain either the
-  <code>.project</code> and <strong>.classpath</strong> files from Eclipse or the
-  <code>res/</code> and <code>src/</code> directories.</p>
+
+
+<h2 id="overview">Migration Overview </h2>
+<p>Migrating from Eclipse to Android Studio requires that you change the structure of your
+development projects, move to a new build system, and use a new user interface. Here are some of
+the key changes you should be aware of as you prepare to migrate to Android Studio:</p>
+<ul>
+  <li><strong>Project files</strong>
+    <p>Android Studio uses a different project structure. Each Eclipse ADT
+    project is called a module in Android Studio. Each instance of Android
+    Studio contains a project with one or more app modules. For more information see,
+    <a href="{@docRoot}tools/studio/eclipse-migration-guide.html#project-structure">Project
+    Structure</a>.</p></li>
+
+  <li><strong>Manifest settings</strong>
+    <p>Several elements in the <code>AndroidManifest.xml</code> file are now properties in the
+    <code>defaultConfig</code> and <code>productFlavors</code> blocks in the
+    <code>build.gradle</code> file. These elements are still valid manifest entries and may
+    appear in manifests from older projects, imported projects, dependencies, and libraries. For
+    more information see,
+    <a href="{@docRoot}tools/studio/eclipse-migration-guide.html#manifest-settings">Manifest
+    Settings</a>.</p></li>
+
+  <li><strong>Dependencies</strong>
+    <p>Library dependencies are handled differently in Android Studio, using Gradle dependency
+    declarations and Maven dependencies for well-known local source and binary libraries with
+    Maven coordinates.  For more information see,
+    <a href="{@docRoot}tools/studio/eclipse-migration-guide.html#dependencies">Dependencies</a></p>
+    </li>
+
+  <li><strong>Test code</strong>
+    <p>With Eclipse ADT, test code is written in separate projects and integrated through the
+    <code>&lt;instrumentation&gt;</code> element in your manifest file. Android Studio provides a
+    <code>AndroidTest</code> folder within your project so you can easily add and maintain your test
+    code within the same project view. JUnit tests can also be configured to run locally to reduce
+    testing cycles.</p></li>
+
+  <li><strong>Gradle-based build system</strong>
+    <p>In place of XML-based Ant build files, Android Studio supports Gradle build files, which
+    use the Gradle Domain Specific Language (DSL) for ease of extensibility and customization.
+    The Android Studio build system also supports
+    <a href="{@docRoot}tools/building/configuring-gradle.html#workBuildVariants"> build variants</a>,
+    which are combinations of <code>productFlavor</code> and <code>buildTypes</code>, to customize
+    your build outputs.</p></li>
+
+  <li><strong>User interface</strong>
+    <p>Android Studio provides an intuitive interface and menu options based on the
+    <a class="external-link" href="https://www.jetbrains.com/idea/" target="_blank">IntelliJ IDEA</a>
+    IDE. To become familiar with the IDE basics, such as navigation, code completion, and keyboard
+    shortcuts, see
+    <a class="external-link" href="https://www.jetbrains.com/idea/help/intellij-idea-quick-start-guide.html"
+    target="_blank">IntelliJ IDEA Quick Start Guide</a>.</p></li>
+
+  <li><strong>Developer tools versioning</strong>
+    <p>Android Studio updates independently of the Gradle-based build system so different build
+    settings can be applied across different versions of command line, Android Studio, and
+    continuous integration builds. For more information, see
+    <a href="{@docRoot}tools/building/configuring-gradle.html">Configuring Gradle Builds</a>.</p>
+    </li>
+</ul>
+
+
+
+
+<h2 id="prerequisites">Migration Prerequisites</h2>
+<p>Before migrating your Eclipse ADT app to Android Studio, review the following steps to make
+sure your project is ready for conversion, and verify you have the tool configuration you need in
+Android Studio:</p>
+
+<ul>
+ <li>In Eclipse ADT:
+   <ul>
+     <li>Make sure the Eclipse ADT root directory contains the <code>AndroidManifest.xml</code>
+       file. Also, the root directory must contain either the <code>.project</code> and
+       <code>.classpath</code> files from Eclipse or the <code>res/</code> and <code>src/</code>
+       directories.</li>
+     <li>Build your project to ensure your latest workspace and project updates are saved and
+       included in the import.</li>
+     <li>Comment out any references to Eclipse ADT workspace library files in the
+       <code>project.properties</code> or <code>.classpath</code> files for import. You can
+       add these references in the <code>build.gradle</code> file after the import. For more
+       information, see
+       <a href="{@docRoot}tools/building/configuring-gradle.html">Configuring Gradle Builds</a>.</li>
+     <li>It may be useful to record your workspace directory, path variables, and any actual path
+       maps that could be used to specify any unresolved relative paths, path variables, and
+       linked resource references. Android Studio allows you to manually specify any unresolved
+       paths during the import process.</li>
+   </ul>
+ </li>
+ <li>In Android Studio:
+   <ul>
+    <li>Make a note of any third-party Eclipse ADT plugins in use and check for equivalent features
+      in Android Studio or search for a compatible plugin in the
+      <a href="https://plugins.jetbrains.com/?androidstudio" class="external-link">IntelliJ Android
+      Studio Plugins</a> repository. Use the <strong>File &gt; Settings &gt; Plugins</strong> menu
+      option to manage plugins in Android Studio. Android Studio does not migrate any third-party
+      Eclipse ADT plugins.</li>
+    <li>If you plan to run Android Studio behind a firewall, be sure to set the proxy settings for
+      Android Studio and the SDK Manager. Android Studio requires an internet connection for
+      Setup Wizard synchronization, 3rd-party library access, access to remote repositories,
+      <a href="http://www.gradle.org" class="external-link">Gradle</a>
+      initialization and synchronization, and Android Studio version updates. For more information,
+      see <a href="{@docRoot}tools/studio/index.html#proxy">Proxy Settings</a>.</li>
+    <li>Use the <strong>File &gt; Settings &gt; System Settings</strong> menu option to verify the
+      current version and, if necessary, update Android Studio to the latest version from the
+      stable channel. To install Android Studio, please visit the
+      <a href="{@docRoot}sdk/index.html">Android Studio download page</a>.</li>
+    </ul>
   </li>
-  <li>Follow the steps in the import wizard. </li>
+ </ul>
+
+
+
+<h2 id="migrate">Importing Projects to Android Studio</h2>
+<p>Android Studio provides a function for importing Eclipse ADT projects, which creates a new
+Android Studio project and app modules based on your current
+Eclipse ADT workspace and projects. No changes are made to your Eclipse project files. The Eclipse
+ADT workspace becomes a new Android Studio project, and each Eclipse ADT project within the workspace
+becomes a new Android Studio module. Each instance of Android Studio contains a project with one or
+more app modules.</p>
+
+<p>After selecting an Eclipse ADT project to import, Android Studio creates the Android
+Studio project structure and app modules, generates the new Gradle-based build files and settings,
+and configures the required dependencies. The import options also allow you to enter your workspace
+directory and any actual path maps to handle any unresolved relative paths, path variables, and
+linked resource references.</p>
+
+<p>Depending on the structure of your Eclipse ADT development project, you should select specific
+files for importing:</p>
+<ul>
+<li>For workspaces with multiple projects, select the project folder for each Eclipse ADT
+  project individually to import the projects into the same Android Studio project. Android
+  Studio combines the Eclipse ADT projects into a single Android Studio project with different app
+  modules for each imported project.</li>
+
+<li>For Eclipse ADT projects with separate test projects, select the test project folder for
+  import. Android Studio imports the test project and then follows the dependency chain to import
+  the source project and any project dependencies.</li>
+
+ <li>If Eclipse ADT projects share dependencies within the same workspace, import each
+   project individually into Android Studio. Android Studio maintains the shared dependencies
+   across the newly created modules as part of the import process.</li>
+</ul>
+
+<p>To import a project to Android Studio:</p>
+
+<ol>
+ <li>Start Android Studio and close any open Android Studio projects.</li>
+ <li>From the Android Studio menu select <strong>File &gt; New &gt; Import Project</strong>.
+  <p>Alternatively, from the <em>Welcome</em> screen, select <strong>Import project
+  (Eclipse ADT, Gradle, etc.)</strong>.</p></li>
+ <li>Select the Eclipse ADT project folder with the <code>AndroidManifest.xml</code> file
+   and click <strong>Ok</strong>.
+   <p> <img src="{@docRoot}images/tools/studio-select-project-forimport.png" alt="" /></p>
+ </li>
+ <li>Select the destination folder and click <strong>Next</strong>.
+   <p> <img src="{@docRoot}images/tools/studio-import-destination-dir.png" alt="" /></p></li>
+ <li>Select the import options and click <strong>Finish</strong>.
+   <p>The import process prompts to migrate any library and project dependencies to Android Studio,
+   and add the dependency declarations to the <code>build.gradle</code> file. The import process
+   also replaces any well-known source libraries, binary libraries, and JAR files that have known
+   Maven coordinates with Maven dependencies, so you no longer need to maintain these dependencies
+   manually. The import options also allow you to enter your workspace directory and any actual
+   path maps to handle any unresolved relative paths, path variables, and linked resource
+   references.</p>
+   <p> <img src="{@docRoot}images/tools/studio-import-options.png" alt="" /></p></li>
+
+ <li>Android Studio imports the app and displays the project import summary. Review the summary
+   for details about the project restructuring and the import process.
+    <p> <img src="{@docRoot}images/tools/studio-import-summary.png"/></p>
+ </li>
 </ol>
 
-<p>Android Studio imports the current dependencies, downloads libraries, and
-creates an Android Studio project with the imported Eclipse project as the main module. Android
-Studio also creates the required Gradle build files. </p>
+<p>After importing the project from Eclipse ADT to the new Android Studio project and module
+structure, each app module folder in Android Studio contains the complete source set for that
+module, including the {@code src/main} and {@code src/androidTest} directories, resources, build
+file, and Android manifest. Before starting app development, you should resolve any issues shown in
+the project import summary to make sure the project re-structuring and import process completed
+properly.</p>
+
+
+
+<h3 id="post-migration">Validating imported projects</h3>
+<p>After completing the import process, use the Android Studio <strong>Build</strong> and
+<strong>Run</strong> menu options to build your project and verify the output. If your project
+is not building properly, check the following settings:</p>
+
+<ul>
+<ul>
+  <li>Use the <strong>Android SDK</strong> button in Android Studio to launch the <a href=
+  "{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and verify the installed versions of SDK
+  tools, build tools, and platform match the settings for your Eclipse ADT project. Android Studio
+  inherits the SDK Manager and JDK settings from your imported Eclipse project.
+  </li>
+  <li>Use the <strong>File &gt; Project Structure</strong> menu option to verify additional
+    Android Studio settings:
+   <ul>
+     <li>Under <em>SDK Location</em> verify Android Studio has access to the correct SDK and
+       JDK locations and versions. </li>
+     <li>Under <em>Project</em> verify the Gradle version, Android Plugin version, and related
+       repositories.</li>
+     <li>Under <em>Modules</em> verify the app and module settings, such as signing configuration
+       and library dependencies. </li>
+   </ul>
+ </li>
+ <li>If your project depends on another project, make sure that dependency is defined properly in
+  the <code>build.gradle</code> file in the app module folder.</li>
+</ul>
+
 
-<p>The import process replaces any JAR files and libraries with Gradle dependencies, and replaces
-source libraries and binary libraries with Maven dependencies, so you no longer need to maintain
-these files manually.</p>
+<p>If there still are unexpected issues when building and running your project in Android
+Studio after you have checked these settings, consider modifying the Eclipse ADT project and
+re-starting the import process. Importing an Eclipse ADT project to Android Studio creates a new
+Android Studio project and does not impact the existing Eclipse ADT project. </p>
 
- <p class="note"><strong>Note:</strong> If there are references to Eclipse workspace library files,
- comment them out in the <code>project.properties</code> or <code>.classpath</code> files
- that you imported from the Eclipse project. You can then add these files in the
- <code>build.gradle</code> file. See
- <a href="{@docRoot}tools/building/configuring-gradle.html">Configuring Gradle Builds</a>. </p>
 
 
-<p>For more help getting started with Android Studio and the IntelliJ user experience,
-<a href="{@docRoot}tools/studio/index.html">learn more about Android Studio</a> and
-read <a href="http://confluence.jetbrains.com/display/IntelliJIDEA/FAQ+on+Migrating+to+IntelliJ+IDEA"
-  class="external-link">FAQ on Migrating to IntelliJ IDEA</a>.</p>
+<p>To get started using Android Studio, review the
+<a href="{@docRoot}tools/studio/index.html">Android Studio</a> features and
+<a href="http://www.gradle.org">Gradle</a>-based build system to become familiar with the new
+project and module structure, flexible build settings, and other advanced Android development
+capabilities. For a comparison of Eclipse ADT and Android Studio features and usage, see
+<a href="{@docRoot}tools/studio/eclipse-migration-guide.html">Transitioning to Android Studio from
+Eclipse</a>. For specific Android Studio how-to documentation, see the pages in the
+<a href="{@docRoot}tools/workflow/index.html">Workflow</a> section.
+</p>
index 51158de..add6ca2 100644 (file)
@@ -31,6 +31,14 @@ Emulator</a></li>
     </div>
   </div>
 
+<p class="caution">
+  <strong>Important:</strong> Support for Ant as a build tool for Android is ending, per our
+  <a href="http://android-developers.blogspot.com/2015/06/an-update-on-eclipse-android-developer.html"
+  class="external-link">announcement</a>. You should migrate your app development projects to
+  Android Studio and Gradle as soon as possible. For more information on transitioning to these
+  tools, see <a href="{@docRoot}sdk/installing/migrate.html">Migrating to Android Studio</a>.
+</p>
+
   <p>There are two ways to build your application using the Ant build script: one for
   testing/debugging your application &mdash; <em>debug mode</em> &mdash; and one for building your
   final package for release &mdash; <em>release mode</em>. Regardless of which way you build your application,
diff --git a/docs/html/tools/building/manifest-merge.jd b/docs/html/tools/building/manifest-merge.jd
new file mode 100644 (file)
index 0000000..54166ec
--- /dev/null
@@ -0,0 +1,510 @@
+page.title=Manifest Merging
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#merge-rules">Merge Conflict Rules</a></li>
+    <li><a href="#markers-selectors">Merge Conflict Markers and Selectors</a></li>
+    <li><a href="#inject-values">Injecting Build Values into a Manifest</a></li>
+    <li><a href="#merge-prodflavorsGroups">Manifest Merging Across Product Flavor Groups</a></li>
+    <li><a href="#implicit-permissions">Implicit Permissions</a></li>
+    <li><a href="#merge-errors">Handling Manifest Merge Build Errors</a></li>
+  </ol>
+
+  <h2>See also</h2>
+  <ol>
+    <li><a href="{@docRoot}sdk/installing/studio-build.html">Build System Overview</a></li>
+    <li><a href="{@docRoot}tools/building/configuring-gradle.html">Configuring Gradle Builds</a> </li>
+  </ol>
+
+</div>
+</div>
+
+
+<p>With Android Studio and <a href="http://www.gradle.org">Gradle</a>-based builds, each app can
+contain manifest files in multiple locations, such as the <code>src/main/</code> folder for
+the <code>productFlavor</code>, libraries, Android ARchive (AAR) bundles of Android Library
+projects, and dependencies. During the build process, manifest merging combines the settings from
+the various <code>AndroidManifest.xml</code> files included in your app into a single, generated APK
+manifest file for app packaging and distribution. Manifest settings are merged based on the manifest
+priority, determined by the manifest's file location. Building your app merges the
+manifest elements, attributes, and sub-elements from these manifests for the specified
+<a href="{@docRoot}tools/building/configuring-gradle.html#workBuildVariants">build variant</a>.</p>
+
+
+<h2 id="merge-rules">Merge Conflict Rules</h2>
+<p>Merge conflicts occur when merged manifests contain the same manifest element but with a
+different attribute value that does not resolve based on the default merge conflict rules.
+<a href="#markers-selectors">Conflict markers and selectors</a> can also define custom merge rules,
+such as allowing an imported library to have a <code>minSdkVersion</code> higher than the
+version defined in the other higher priority manifests.  </p>
+
+<p>The manifest merge priority determines which manifest settings are retained in merge conflicts,
+with the settings in higher priority manifest overwriting those in lower priority manifests.
+The following list details which manifest settings are are the highest priority during the merge
+process:</p>
+
+<ul>
+ <li>Highest priority: <code>buildType</code> manifest settings </li>
+ <li>Higher priority: <code>productFlavor</code> manifest settings </li>
+ <li>Medium priority: Manifests in the <code>src/main/</code> directory of an app project</li>
+ <li>Low priority: Dependency and library manifest settings </li>
+</ul>
+
+<p>Manifest merge conflicts are resolved at the XML node and
+attribute levels based on the following merge rules. </p>
+
+<table>
+    <tr>
+        <th scope="col">High Priority Element</th>
+        <th scope="col">Low Priority Element</th>
+        <th scope="col">Manifest Merge Result</th>
+    </tr>
+    <tr>
+        <td rowspan="3">no attribute</td>
+        <td>no attribute</td>
+        <td>no attribute</td>
+    </tr>
+    <tr>
+
+        <td>attribute set to default</td>
+        <td>default attribute</td>
+    </tr>
+    <tr>
+
+        <td>attribute set to non-default </td>
+        <td>low priority attribute</td>
+    </tr>
+    <tr>
+        <td>attribute set to default</td>
+        <td rowspan="2">no attribute</td>
+        <td>default attribute</td>
+    </tr>
+    <tr>
+        <td>attribute set to non-default </td>
+
+        <td>high priority attribute</td>
+    </tr>
+    <tr>
+        <td>attribute set to default</td>
+        <td>attribute set to default</td>
+        <td>default attribute</td>
+    </tr>
+    <tr>
+        <td>attribute set to default</td>
+        <td>attribute set to non-default </td>
+        <td>low priority attribute</td>
+    </tr>
+    <tr>
+        <td>attribute set to non-default</td>
+        <td>attribute set to default</td>
+        <td>high priority attribute</td>
+    </tr>
+    <tr>
+        <td>attribute set to non-default</td>
+        <td>attribute set to non-default </td>
+        <td>Merge if settings match, otherwise causes conflict error.</td>
+    </tr>
+   </table>
+
+
+
+<p>Exceptions to the manifest merge rules: </p>
+
+<ul>
+ <li>The <code>uses-feature android:required;</code> and
+ <code>uses-library android:required</code> elements default to <code>true</code> and use
+ an <em>OR</em> merge so that any required feature or library is included in the generated APK. </li>
+
+ <li>If not declared, the
+ <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code>&lt;uses-sdk&gt;</code></a>
+ elements, <code>minSdkVersion</code> and
+ <code>targetSdkVersion</code>, default to a value of 1. When
+ merge conflicts occur, the value in the higher priority manifest version is used.</li>
+
+ <li>Importing a library with a <code>minSdkVersion</code> value higher than the app's
+ <code>src/main/</code> manifest manifest generates an error unless
+ the <code>overrideLibrary</code> conflict marker is used.
+
+ <p class="note"><strong>Note:</strong> If not explicitly declared, the <code>targetSdkVersion</code>
+ defaults to the <code>minSdkVersion</code> value. When no <code><uses-sdk></code> element is
+ present in any manifest or the <code>build.gradle</code> file, the
+ <code>minSdkVersion</code> defaults to 1.</p> </li>
+
+ <li>When importing a library with a <code>targetSdkVersion</code> value lower than the app's
+ <code>src/main/</code> manifest, the manifest merge
+ process explicitly grants permissions and ensures that the imported library functions properly. </li>
+
+ <li>The <code>manifest</code> element only merges with child manifest elements. </li>
+
+ <li>The <code>intent-filter</code> element is never changed and is always added to the common
+ parent node in the merged manifest. </li>
+</ul>
+
+<p class="caution"><strong>Important:</strong> After the manifests are merged, the build process
+overrides the final manifest settings with any settings that are also in the
+<code>build.gradle</code> file. For more details, see
+<a href="{@docRoot}tools/building/configuring-gradle.html">Configuring Gradle Builds</a>. </p>
+
+
+
+<h2 id="markers-selectors">Merge Conflict Markers and Selectors</h2>
+<p>Manifest markers and selectors override the default merge rules through
+specific conflict resolutions. For example, use a conflict marker to
+merge a library manifest with a higher <code>minSdkVersion</code> value than the higher priority
+manifest, or to merge manifests with the same activity but different <code>android:theme</code>
+values. </p>
+
+<h3 id="conflict-markers">Merge Conflict Markers</h3>
+<p>A merge conflict marker is a special attribute in the Android tools namespace that defines a
+specific merge conflict resolution. Create a conflict marker to avoid a merge conflict error for
+conflicts not resolved by the default merge rules. Supported merge conflict markers include:</p>
+
+<dl>
+  <dt><code>merge</code></dt>
+    <dd>Merges attributes when there are no conflicts with the merge rules. The default merge
+    action.</dd>
+  <dt><code>replace</code></dt>
+    <dd>Replaces attributes in the lower priority manifest with those from the higher priority
+    manifest.</dd>
+  <dt><code>strict</code></dt>
+    <dd>Sets the merge policy level so that merged elements with same attributes, but different
+     values generate a build failure, unless resolved through the conflict rules.</dd>
+  <dt><code>merge-only</code></dt>
+    <dd>Allows merge actions for only lower priority attributes.</dd>
+  <dt><code>remove</code></dt>
+    <dd>Removes the specified lower priority element from the merged manifest.</dd>
+  <dt><code>remove-All</code></dt>
+    <dd>Removes all lower priority elements of the same node type from the merged manifest.</dd>
+</dl>
+
+
+<p>By default, the manifest merge process applies the <code>merge</code> conflict marker to
+the node level. All declared manifest attributes default to a <code>strict</code>
+merging policy. </p>
+
+<p>To set a merge conflict marker, first declare the namespace in the
+<code>AndroidManifest.xml</code> file. Then, enter the merge conflict marker in the manifest to
+specify a custom merge conflict action. This example inserts the <code>replace</code> marker to
+set a replace action to resolve conflicts between the <code>android:icon</code> and
+<code>android:label</code> manifest elements. </p>
+
+<pre>
+
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+   package="com.android.tests.flavorlib.app"
+   xmlns:tools="http://schemas.android.com/tools"&gt;
+
+   &lt;application
+       android:icon="&#64;drawable/icon"
+       android:label="&#64;string/app_name"
+       tools:replace="icon, label"&gt;
+       ...
+
+</manifest>
+
+</pre>
+
+
+<h4>Marker attributes</h4>
+<p>Conflict markers use <code>tools:node</code> and <code>tools:attr</code> attributes to
+restrict merge actions at the XML node or attribute level. </p>
+
+<p>The <code>tools:attr</code> markers use only the <code>restrict</code>, <code>remove</code>, and
+<code>replace</code> merge actions. Multiple <code>tools:attr</code> marker values can be applied
+to a specific element. For example, use <code>tools:replace="icon, label, theme"</code> to replace
+lower priority <code>icon</code>, <code>label</code>, and <code>theme</code> attributes. </p>
+
+
+<h4>Merge conflict marker for imported libraries</h4>
+<p>The <code>overrideLibrary</code> conflict marker applies to the <code>&lt;uses-sdk&gt;</code>
+manifest declaration and is used to import a library even though the library's
+<code>&lt;uses-sdk&gt;</code> values, such as <code>minSdkVersion</code>
+are set to different values than those in the other higher priority manifests. </p>
+
+<p>Without this marker, library manifest merge conflicts from the
+<code>&lt;uses-sdk&gt;</code> values cause the merge process to fail.</p>
+
+<p>This example applies the <code>overrideLibrary</code> conflict marker to resolve the merge
+conflict between <code>minSdkVersion</code> values in the <code>src/main/</code> manifest and an
+imported library manifest.
+
+
+<p><code>src/main/</code> manifest: </p>
+<pre>
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+   package="com.android.example.app"
+   xmlns:tools="http://schemas.android.com/tools"&gt;
+   ...
+   &lt;uses-sdk android:targetSdkVersion="22" android:minSdkVersion="2"
+             tools:overrideLibrary="com.example.lib1, com.example.lib2"/&gt;
+   ...
+</pre>
+
+<p>Library manifest: </p>
+
+<pre>
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.example.lib1"&gt;
+     ...
+        &lt;uses-sdk android:minSdkVersion="4" /&gt;
+     ...
+    &lt;/manifest&gt;
+</pre>
+
+<p class="note"><strong>Note:</strong> The default merge process does not allow importing a library
+with a higher <code>minSdkVersion</code> than the app's <code>src/main/</code> manifest unless
+the <code>overrideLibrary</code> conflict marker is used. </p>
+
+
+
+<h3 id="marker-selectors">Marker Selectors</h3>
+<p>Marker selectors limit a merge action to a specific lower priority manifest. For example, a
+marker selector can be used to remove a permission from only one library, while allowing the
+same permission from other libraries.</p>
+
+<p>This example uses the <code>tools:node</code> marker to remove the <code>permisionOne</code>
+attribute, while the <code>tools:selector</code> selector specifies the specific library as
+<em>com.example.lib1</em>. The <code>permisionOne</code> permission is filtered from only the
+<code>lib1</code> library manifests. </p>
+
+<pre>
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+   package="com.android.example.app"
+   xmlns:tools="http://schemas.android.com/tools"&gt;
+   ...
+   &lt;permission
+         android:name="permissionOne"
+         tools:node="remove"
+         tools:selector="com.example.lib1"&gt;
+   ...
+</pre>
+
+
+
+<h2 id="inject-values">Injecting Build Values into a Manifest</h2>
+<p>Manifest merging can also be configured to use manifest placeholders to inject
+property values from the <code>build.gradle</code> file into the manifest attributes. </p>
+
+<p>Manifest placeholders use the syntax <code>&#36;{name}</code> for attribute values, where
+<code>name</code> is the injected <code>build.gradle</code> property. The <code>build.gradle</code>
+file uses the <code>manifestPlaceholders</code> property to define the placeholder values. </p>
+
+<p class="note"><strong>Note:</strong> Unresolved placeholder names in apps cause build failures.
+Unresolved placeholder names in libraries generate warnings and need to be resolved when importing
+the library into an app.</p>
+
+<p>This example shows the manifest placeholder <code>&#36;{applicationId}</code> used to inject the
+<code>build.gradle</code> <code>applicationId</code> property value in to <code>android:name</code>
+attribute value.  </p>
+
+<p class="note"><strong>Note:</strong> Android Studio provides a default
+<code>&#36;{applicationId}</code> placeholder for the <code>build.gradle</code>
+<code>applicationId</code> value that is not shown in the build file.</p>
+
+
+<p>Manifest entry:</p>
+
+<pre>
+
+&lt;activity
+android:name=".Main"&gt;
+     &lt;intent-filter&gt;
+     &lt;action android:name="&#36;{applicationId}.foo"&gt;
+         &lt;/action&gt;
+&lt;/intent-filter&gt;
+&lt;/activity&gt;
+
+</pre>
+
+
+<p>Gradle build file:</p>
+
+<pre>
+android {
+   compileSdkVersion 22
+   buildToolsVersion "22.0.1"
+
+   productFlavors {
+       flavor1 {
+           applicationId = "com.mycompany.myapplication.productFlavor1"
+       }
+}
+
+</pre>
+
+<p>Merged manifest value: </p>
+
+<pre>
+&lt;action android:name="com.mycompany.myapplication.productFlavor1.foo"&gt;
+</pre>
+
+
+<p>The manifest placeholder syntax and build file <code>manifestPlaceholders</code>
+property can be used to inject other manifest values. For properties other than the
+<code>applicationId</code>, the <code>manifestPlaceholders</code> property is explicitly declared
+in the <code>build.gradle</code> file. This example shows the manifest placeholder for injecting
+<code>activityLabel</code> values.</p>
+
+<p>Gradle build file: </p>
+
+<pre>
+android {
+    defaultConfig {
+        manifestPlaceholders = [ activityLabel:"defaultName"]
+    }
+    productFlavors {
+        free {
+        }
+        pro {
+            manifestPlaceholders = [ activityLabel:"proName" ]
+        }
+    }
+
+</pre>
+
+<p>Placeholder in the manifest file: </p>
+
+<pre>
+&lt;activity android:name=".MainActivity" android:label="&#36;{activityLabel}" &gt;
+</pre>
+
+<p class="note"><strong>Note:</strong> The placeholder value supports partial value injection,
+for example <code>android:authority="com.acme.&#36;{localApplicationId}.foo"</code>. </p>
+
+
+
+<h2 id="merge-prodflavorsGroups">Manifest Merging Across Product Flavor Groups</h2>
+
+<p>When using the <code>GroupableProductFlavor</code> property, the manifest merge
+priority of any manifests in the product flavor groups follows the order in which the
+product flavor groups are listed in the build file. The manifest merge process creates a single
+merged manifest for the product flavor groups based on the configured build variant. </p>
+
+<p>For example, if a build variant references the product flavors <code>x86</code>,
+<code>mdpi</code>, <code>21</code>, and <code>paid</code> from the respective product flavor
+groups <code>ABI</code>, <code>Density</code>, <code>API</code>, and <code>Prod</code>, listed
+in this order in the <code>build.gradle</code> file, then the manifest merge process merges the
+manifests in this priority order, which follows how the product flavors are listed in the build
+file.</p>
+
+<p>To illustrate this example, the following table shows how the product flavors are listed for
+each product flavor group. This combination of product flavors and groups defines the
+build variant. </p>
+<table>
+    <tr>
+        <th scope="col">Product Flavor Group</th>
+        <th scope="col">Product Flavor</th>
+    <tr>
+        <td>ABI</td>
+        <td>x86</td>
+    </tr>
+    <tr>
+       <td>density</td>
+        <td>mdpi</td>
+    </tr>
+    <tr>
+        <td>API</td>
+        <td>22</td>
+    </tr>
+    <tr>
+        <td>prod</td>
+        <td>paid</td>
+    </tr>
+</table>
+
+<p>Manifest merge order:</p>
+
+ <ul>
+  <li>prod-paid AndroidManifest.xml (lowest priority) merges into API-22 AndroidManifest.xml</li>
+  <li>API-22 AndroidManifest.xml merges into density-mpi AndroidManifest.xml</li>
+  <li>density-mpi AndroidManifest.xml merges into ABI-x86 AndroidManifest.xml (highest priority)</li>
+ </ul>
+
+
+<h2 id="implicit-permissions">Implicit Permissions</h2>
+<p>Importing a library that targets an Android runtime with implicitly
+granted permissions may automatically add the permissions to the resulting merged manifest.
+For example, if an application with a <code>targetSdkVersion</code> of 16 imports a library with a
+<code>targetSdkVersion</code> of 2, Android Studio adds the <code>WRITE_EXTERNAL_STORAGE</code>
+permission to ensure permission compatibility across the SDK versions.
+
+<p class="note"><strong>Note:</strong> More recent Android releases replace implicit
+permissions with permission declarations.</p>
+
+
+This table lists the importing library versions and the declared permissions.
+</p>
+
+  <table>
+    <tr>
+      <th>Importing this library version</th>
+      <th>Declares this permission in the manifest </th>
+    </tr>
+    <tr>
+      <td><code>targetSdkVersion</code> &lt; 2 </td>
+      <td><code>WRITE_EXTERNAL_STORAGE</code> </td>
+    </tr>
+    <tr>
+      <td><code>targetSdkVersion</code> &lt; 4 </td>
+      <td><code>WRITE_EXTERNAL_STORAGE</code>, <code>READ_PHONE_STATE</code> </td>
+    </tr>
+    <tr>
+      <td>Declared <code>WRITE_EXTERNAL_STORAGE</code></td>
+      <td><code>READ_EXTERNAL_STORAGE</code></td>
+    </tr>
+    <tr>
+      <td><code>targetSdkVersion</code> &lt; 16 and using the <code>READ_CONTACTS</code>
+      permission</td>
+      <td><code>READ_CALL_LOG</code></td>
+    </tr>
+    <tr>
+      <td><code>targetSdkVersion</code> &lt; 16 and using the <code>WRITE_CONTACTS</code>
+      permission</td>
+      <td><code>WRITE_CALL_LOG</code></td>
+    </tr>
+  </table>
+
+
+
+<h2 id="merge-errors">Handling Manifest Merge Build Errors</h2>
+<p>During the build process, the manifest merge process stores a record of each merge transaction
+in the <code>manifest-merger-&lt;productFlavor&gt;-report.txt</code> file in the module
+<code>build/outputs/logs</code> folder. A different log file is generated for each of the
+module's build variants. </p>
+
+<p>When a manifest merge build error occurs, the merge process records the error message
+describing the merge conflict in the log file. For example, the
+<code>android:screenOrientation</code> merge conflict between the following manifests causes
+a build error. </p>
+
+<p>Higher priority manifest declaration: </p>
+
+<pre>
+&lt;activity
+   android:name="com.foo.bar.ActivityOne"
+   android:screenOrientation="portrait"
+   android:theme="&#64;theme1"/&gt;
+</pre>
+
+<p>Lower priority manifest declaration: </p>
+
+<pre>
+&lt;activity
+   android:name="com.foo.bar.ActivityOne"
+   android:screenOrientation="landscape"/&gt;
+</pre>
+
+<p>Error log:</p>
+
+<pre>
+/project/app/src/main/AndroidManifest.xml:3:9 Error:
+ Attribute activity&#64;screenOrientation value=(portrait) from AndroidManifest.xml:3:9
+ is also present at flavorlib:lib1:unspecified:3:18 value=(landscape)
+ Suggestion: add 'tools:replace="icon"' to <activity> element at AndroidManifest.xml:1:5 to override
+</pre>
+
+
index 8abe1b4..0fac62d 100644 (file)
@@ -30,20 +30,21 @@ page.tags=adt
     </div>
   </div>
 
-  <p>ADT (Android Developer Tools) is a plugin for Eclipse that provides a suite of
+<p class="caution">
+  <strong>Important:</strong> Support for the Android Developer Tools (ADT) in Eclipse is ending,
+  per our <a href=
+  "http://android-developers.blogspot.com/2015/06/an-update-on-eclipse-android-developer.html"
+  class="external-link">announcement</a>. You should migrate your app development projects to
+  Android Studio as soon as possible. For more information on transitioning to Android Studio, see
+  <a href="{@docRoot}sdk/installing/migrate.html">Migrating to Android Studio</a>.
+</p>
+
+  <p>Android Developer Tools (ADT) is a plugin for Eclipse that provides a suite of
   tools that are integrated with the Eclipse IDE. It offers you access to many features that help
   you develop Android applications. ADT
   provides GUI access to many of the command line SDK tools as well as a UI design tool for rapid
   prototyping, designing, and building of your application's user interface.</p>
 
-<p class="note"><strong>Note:</strong>
-If you have been using Eclipse with ADT, be aware that <a
-href="{@docRoot}tools/studio/index.html">Android Studio</a> is now the official IDE
-for Android, so you should migrate to Android Studio to receive all the
-latest IDE updates. For help moving projects,
-see <a href="/sdk/installing/migrate.html">Migrating to Android
-Studio</a>.</p>
-
 <p>If you still wish to use the ADT plugin for Eclipse, see
 <a href="{@docRoot}sdk/installing/installing-adt.html">Installing Eclipse Plugin.</a>
 </p>
diff --git a/docs/html/tools/studio/eclipse-transition-guide.jd b/docs/html/tools/studio/eclipse-transition-guide.jd
new file mode 100644 (file)
index 0000000..aaacbe3
--- /dev/null
@@ -0,0 +1,773 @@
+page.title=Transition Guide for Eclipse ADT
+@jd:body
+
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+  <ol>
+    <li><a href="#project-structure">Project Structure</a></li>
+    <li><a href="#manifest-settings">Manifest Settings</a></li>
+    <li><a href="#dependencies">Dependencies</a></li>
+    <li><a href="#build-process">Gradle-based Build Process</a></li>
+    <li><a href="#debug-inspect">Debugging and Code Inspections</a></li>
+    <li><a href="#resource-optimization">Resource Optimization</a></li>
+    <li><a href="#signing">App Signing</a></li>
+    <li><a href="#support-lib">Android Support Repository and Google Play services Repository</a></li>
+    <li><a href="#app-package">App Packaging</a></li>
+    <li><a href="#software-updates">Software Updates </a></li>
+    <li><a href="#version-control">Version Control</a></li>
+    </ol>
+
+  <h2>See also</h2>
+  <ol>
+    <li><a class="external-link"
+      href="http://confluence.jetbrains.com/display/IntelliJIDEA/FAQ+on+Migrating+to+IntelliJ+IDEA">
+      IntelliJ FAQ on migrating to IntelliJ IDEA</a></li>
+    <li><a class="external-link"
+      href="https://confluence.jetbrains.com/display/IntelliJIDEA/IntelliJ+IDEA+for+Eclipse+Users">
+      IntelliJ IntelliJ for Eclipse Users</a></li>
+    <li><a href="{@docRoot}tools/studio/index.html">Android Studio Overview</a> </li>
+  </ol>
+
+</div>
+</div>
+
+
+<p>This document describes the differences between Eclipse ADT and Android Studio, including project
+  structure, build system, debugging, and application packaging. This guide is intended to help you
+  transition to using Android Studio as your development environment.</p>
+
+<h2 id="project-structure">Project Structure </h2>
+<p>Eclipse provides workspaces as a common area for grouping related projects, configurations, and
+settings. In Android Studio, each instance of Android Studio contains a top-level project with one
+or more app modules. Each app module folder contains the equivalent to an Eclipse
+project, the complete source sets for that module, including {@code src/main} and
+{@code src/androidTest} directories, resources, build file, and the Android manifest. In general,
+to update and build your app you modify the files under each module's
+{@code src/main} directory for source code updates, the <code>gradle.build</code> file for
+build specification, and the files under {@code src/androidTest} directory for test case creation. </p>
+
+<p>You can also customize the view of the project files in Android Studio to focus on specific
+aspects of your app development: </p>
+
+<ul>
+  <li><em>Packages</em> </li>
+  <li><em>Project Files</em> </li>
+  <li><em>Scratches</em> </li>
+  <li><em>Problems</em> </li>
+  <li><em>Production</em> </li>
+  <li><em>Tests</em> </li>
+</ul>
+
+
+<p>The following table shows the general mapping of the Eclipse ADT project structure and file
+locations to Android Studio.</p>
+
+<p class="table-caption" id="table-project-structure-mapping">
+  <strong>Table 1.</strong> Project structure mapping.</p>
+
+<table>
+    <tr>
+        <th scope="col">Eclipse ADT</th>
+        <th scope="col">Android Studio</th>
+    </tr>
+
+    <tr>
+        <td>Workspace </td>
+        <td>Project </td>
+    </tr>
+
+    <tr>
+        <td>Project </td>
+        <td>Module </td>
+    </tr>
+
+     <tr>
+        <td>Project-specific JRE </td>
+        <td>Module JDK </td>
+     </tr>
+
+     <tr>
+        <td>Classpath variable </td>
+        <td>Path variable</td>
+     </tr>
+
+     <tr>
+        <td>Project dependency</td>
+        <td>Module dependency</td>
+     </tr>
+
+     <tr>
+        <td>Library Module</td>
+        <td>Library </td>
+     </tr>
+
+     <tr>
+       <td><code>AndroidManifest.xml</code></td>
+       <td><code>app/src/main/AndroidManifest.xml</code> </td>
+     </tr>
+     <tr>
+       <td><code>assets/</code></td>
+       <td><code>app/src/main/assets</code> </td>
+     </tr>
+     <tr>
+       <td><code>res/</code></td>
+       <td><code>app/src/main/res/</code> </td>
+     </tr>
+     <tr>
+       <td><code>src/</code></td>
+       <td><code>app/src/main/java/ </code> </td>
+     </tr>
+     <tr>
+       <td><code>tests/src/</code></td>
+       <td><code>app/src/androidTest/java/</code> </td>
+     </tr>
+
+   </table>
+
+
+
+<p>Table 2 shows Eclipse ADT and Android Studio project views. </p>
+
+<p class="table-caption" id="table2">
+  <strong>Table 2.</strong> Comparing project views.</p>
+<table>
+  <tbody><tr>
+    <th>Eclipse ADT</th>
+    <th>Android Studio Project View</th>
+    <th>Android Studio Android View</th>
+  </tr>
+  <tr>
+    <td><img src="{@docRoot}images/tools/eclipse-notepad-pre-import--structure.png"/>  </td>
+    <td><img src="{@docRoot}images/tools/studio-import-project-structure-project.png"/>  </td>
+    <td><img src="{@docRoot}images/tools/studio-import-project-structure-android.png"/>  </td>
+  </tr>
+ </tbody>
+</table>
+
+
+<p class="note"><strong>Note:</strong> Multiple instances of Android Studio can be used to develop
+independent projects. </p>
+
+
+
+
+<h2 id="manifest-settings">Manifest Settings</h2>
+<p>Android Studio and <a href="http://www.gradle.org">Gradle</a>-based builds support
+<a href="{@docRoot}tools/building/configuring-gradle.html#workBuildVariants"> build variants</a>,
+which are combinations of <code>productFlavor</code> and <code>buildTypes</code>, to customize
+your build outputs. To support these custom builds, several elements in the
+<code>AndroidManifest.xml</code> file are now properties in the <code>defaultConfig</code> and
+<code>productFlavors</code> blocks in the <code>build.gradle</code> file. The import process
+copies these manifest settings to the properties in the <code>build.gradle</code> file.
+These properties overwrite the settings in any other manifest files as shown in table 3. </p>
+
+<p class="table-caption" id="table-manifest-gradle-settings">
+  <strong>Table 3.</strong> Manifest and Gradle property settings.</p>
+<table>
+    <tr>
+        <th scope="col">Manifest Setting</th>
+        <th scope="col">build.gradle Setting</th>
+    </tr>
+    <tr>
+        <td><code>&lt;uses-sdk</code> <br>
+            <p><code>android:minSdkVersion</code></p>
+            <p><code>android:targetSdkVersion /&gt;</code></p>
+    </td>
+        <td> <br>
+           <p><code>minSdkVersion</code></p>
+           <p><code>targetSdkVersion</code></p> </td>
+    </tr>
+    <tr>
+        <td><code>&lt;manifest</code>
+            <p>package (Required in the default manifest file.) </p>
+            <p><code>android:versionCode</code></p>
+            <p><code>android:versionName /&gt;</code></p>
+    </td>
+        <td> <br>
+            <p><code>applicationId</code> (See
+            <a href="{@docRoot}tools/studio/index.html#app-id"> Application ID
+            for Package Identification</a>)</p>
+            <p><code>versionCode</code></p>
+            <p><code>versionName</code></p> </td>
+    </tr>
+
+   </table>
+
+
+<p>Although these settings may no longer appear in the default app manifest file, they are still
+valid manifest entries and may still appear in manifests from older projects, imported projects,
+dependencies, and libraries.</p>
+
+<p>The <code>package</code> element must still be specified in the manifest file. It is used in
+your source code to refer to your <code>R</code> class and to resolve any relative activity/service
+registrations. </p>
+
+<p class="note"><strong>Note:</strong> When multiple manifests are present in your app, for
+example a library manifest and a <code>src/main/</code> manifest, the build process combines
+the manifest settings into a single merged manifest based on the manifest priority and
+manifest merge settings. For more information about the manifest merge process and merge settings,
+see
+<a href="{@docRoot}tools/building/manifest-merger.html"> Manifest Merger</a>. </p>
+
+
+<h2>Application ID for package identification </h2>
+<p>With the Android build system, the <code>applicationId</code> attribute is used to
+uniquely identify application packages for publishing. The application ID is set in the
+<code>android</code> section of the <code>build.gradle</code> file. This field is populated in the
+build file as part of the migration process. </p>
+
+<pre>
+apply plugin: &#39;com.android.application&#39;
+
+android {
+   compileSdkVersion 19
+   buildToolsVersion "19.1"
+
+   defaultConfig {
+       <strong>applicationId "com.example.my.app"</strong>
+       minSdkVersion 15
+       targetSdkVersion 19
+       versionCode 1
+       versionName "1.0"
+   }
+ ...
+</pre>
+
+<p class="note"><strong>Note:</strong> The <code>applicationId</code> is specified only in your
+<code>build.gradle</code> file, and not in the <code>AndroidManifest.xml</code> file.</p>
+
+<p><a href="{@docRoot}tools/building/configuring-gradle.html#workBuildVariants">Build variants</a>
+enable you to uniquely identify different
+packages for each product flavor and build type. The application ID in the build type setting can
+be added as a suffix to the ID specified for the product flavors. The following example adds the
+<code>.debug</code> suffix to the application ID of the <code>pro</code> and <code>free</code>
+product flavors: </p>
+
+<pre>
+productFlavors {
+     pro {
+          applicationId = &quot;com.example.my.pkg.pro&quot;
+     }
+     free {
+          applicationId = &quot;com.example.my.pkg.free&quot;
+     }
+}
+
+buildTypes {
+    debug {
+          applicationIdSuffix &quot;.debug&quot;
+    }
+}
+....
+</pre>
+
+
+
+<h2 id="dependencies">Dependencies</h2>
+<p>During the import process, Android Studio imports the current Eclipse ADT dependencies and
+downloads any project libraries as Android Studio modules. The dependency declarations are added to
+the <code>build.gradle</code> file. The declarations include a
+<a href="#scopes">dependency scope</a>, such as
+<code>compile</code>, to specify in which builds the dependency is included. </p>
+
+<p>The following example shows how to add an external library JAR dependency so it's included in
+each compile:</p>
+
+<pre>
+dependencies {
+    compile files(&#39;libs/*.jar&#39;)
+}
+
+android {
+    ...
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> Android Studio supports the Android ARchive (AAR) format
+for the distribution of Android library projects as dependencies. For more information, see
+<a href="{@docRoot}tools/building/configuring-gradle.html">Configuring Gradle Builds</a>. </p>
+
+
+<p>The import process replaces any well-known source libraries, binary libraries, and JAR files
+that have known Maven coordinates with Maven dependencies, so you no longer need to
+maintain these dependencies manually. </p>
+
+<p>Android Studio enables access to Maven, JCenter, and Ivy repositories with the
+<code>repositories</code> block in the <code>build.gradle</code> as a shortcut to specifying
+the URL of the repository.
+
+<p>If there are required repositories not declared in the <code>build.gradle</code> file, first add
+the repository to the <code>repositories</code> block, and then declare the dependencies in a way
+that Maven, JCenter, or Ivy declare their artifacts. The following example shows how to add the
+Maven repository with the guava 11.0.2 dependency using the <code>mavenCentral()</code> property: </p>
+
+<pre>
+repositories {
+    mavenCentral()
+}
+
+android {
+    ...
+}
+
+dependencies {
+    compile &#39;com.google.guava:guava:11.0.2&#39;
+    instrumentationtestCompile &#39;com.squareup.fast-android:1:0.4&#39;
+}
+
+</pre>
+
+<p>The Android Studio project created during the import process can also re-use any
+dependencies on other components. These components can be external binary packages or other
+<a href="http://www.gradle.org">Gradle</a> projects. If a dependency has dependencies of its own,
+those dependencies are also included in the new Android Studio project. </p>
+
+
+<p class="note"><strong>Note:</strong> If there were references to Eclipse ADT workspace library
+files in the <code>project.properties</code> or <code>.classpath</code> files
+that were not imported from the Eclipse project, you can now add dependencies to these library files
+in the <code>build.gradle</code> file. For more information, see
+<a href="{@docRoot}tools/building/configuring-gradle.html">Configuring Gradle Builds</a>. </p>
+
+
+<h3 id="scopes">Dependency and compilation scopes </h3>
+<p>Android Studio supports compilation scopes to customize which dependencies get
+included in each build, for example assigning different dependencies to different
+<a href="{@docRoot}tools/building/configuring-gradle.html#workBuildVariants"> build variants</a>.</p>
+
+<p>This list shows the Android Studio scope names and definitions: </p>
+
+<ul>
+ <li>compile - <code>compile</code> </li>
+ <li>run time - <code>package</code></li>
+ <li>testCompile - <code>AndroidTestCompile</code></li>
+ <li>testRuntime - <code>AndroidTestRunPackage</code></li>
+ <li>buildTypeCompile - <code>buildTypeCompile</code> </li>
+ <li>productFlavorCompile - <code>productFlavorCompile</code> </li>
+</ul>
+
+<p class="note"><strong>Note:</strong> Dependencies for library projects must be added with the
+<code>compile</code> scope. </p>
+
+
+<p>With the <a href="http://www.gradle.org">Gradle</a>-based DSL, you can also add custom
+dependency scopes, such as <code>betaCompile file('libs/protobug.jar')</code> to define a beta
+build dependency. </p>
+
+<p>The scope and compilation configuration in the build file determine the
+components compiled into the app, added to the compilation classpath, and packaged in the final
+APK file. Based on the dependency and compilation scope, different compilation configurations
+can be specified to include the dependencies and classpaths, for example: </p>
+
+<ul>
+<li><code>compile</code> - for the main application. </li>
+<li><code>androidTestCompile</code> - for the test application. </li>
+<li><code>debugCompile</code> - for the debug buildType application.</li>
+<li><code>releaseCompile</code> - for the release buildType application. </li>
+</ul>
+
+<p class="note"><strong>Note:</strong> Because it’s not possible to build an APK that does not
+have an associated <code>buildType</code>, the APK built from your app is always configured with
+at least two dependency and compile configurations: <code>compile</code> and
+<code>debugCompile</code>. </p>
+
+<p>Unlike Eclipse ADT, by default Android Studio does not compile your code when there are changes.
+Use the <strong>File &gt; Settings &gt; Build, Execution, Deployment Compiler</strong> option
+to enable automatic compilation. </p>
+
+
+
+<h2 id="build-process">Gradle-based Build Process </h2>
+<p>Android Studio imports the Eclipse ADT Ant-based
+build tasks and converts the tasks to <a href="http://www.gradle.org">Gradle</a>-based build tasks.
+These new build tasks include the
+main <code>assemble</code> task and at least two outputs based on the default build types:
+a <code>debug</code> APK and a
+<code>release</code> APK. Each of these build tasks has its own Android build system anchor task
+to facilitate building them independently: </p>
+<ul>
+  <li><code>assemble</code></li>
+  <li><code>assembleDebug</code></li>
+  <li><code>assembleRelease</code></li>
+</ul>
+
+<p>In Android Studio, you can view all the supported build tasks in the
+<em>Gradle</em> project tab. </p>
+
+<p>With the <a href="http://www.gradle.org">Gradle</a>-based build system, Android Studio uses a
+<a href="http://www.gradle.org">Gradle</a> wrapper to fully integrate the
+Android Plugin for Gradle. The Android Plugin for Gradle also
+runs independent of Android Studio. This means that with Android Studio build system your build
+output is always the same, whether you build your Android apps from Android Studio, from the
+command line on your machine, or on machines where Android Studio is not installed (such as
+continuous integration servers). </p>
+
+<p>Unlike Eclipse ADT with dependent plugin and build updates, the <code>build.gradle</code>
+files allow you to customize the build settings for each Android Studio module and build variant,
+so the build versions can be set independently, and are not dependent on the Android Studio
+or build tools versions. This makes it easy to maintain and build legacy apps along with your
+current app, using build variants to generate different APKs from the same app modules, but
+built with different build versions and build chains. </p>
+
+<p>For more details about the Android Studio build system, see
+<a href="{@docRoot}sdk/installing/studio-build.html">Build System Overview</a>.</p>
+
+<h3>Using the Android Studio build system's declarative logic </h3>
+<p>In contrast with the XML statements in Ant build files, the Android build system and
+<a href="http://www.gradle.org">Gradle</a> DSL provide a declarative build language so you can
+easily extend the Gradle-based build process beyond the typical XML build tasks. For example,
+this build file shows how to define a custom function to inject a dynamic <code>versionCode</code>
+in build outputs: </p>
+
+<pre>
+def getVersionCode) {
+      def code = â€¦
+      return code
+}
+
+android {
+    defaultConfig {
+        versionCode getVersionCode()
+              â€¦
+    }
+}
+</pre>
+
+<p>This example shows how to append <em>debug</em> to your package and version names used in the
+<code>debug</code> build variant of your app: </p>
+
+<pre>
+android {
+    buildTypes {
+        debug {
+            packageNameSuffix â€˜.debug’
+            versionNameSuffix â€˜-DEBUG’
+              }
+            beta {
+                   â€¦
+            }
+        }
+}
+</pre>
+
+
+<p>You can also use the declarative DSL in the Android build system to generate custom build
+versions, for example a debuggable version of your release APK. This examples adds the
+<code>debuggable true</code> property to the <code>release</code> build type in the
+<code>build.gradle</code> file to build an identical debuggable version of the release package.  </p>
+
+<pre>
+android {
+    buildTypes {
+        debugRelease.initWith(buildTypes.release)
+        debugRelease {
+            debuggable true
+            packageNameSuffix &#39;.debugrelease&#39;
+            signingConfig signingConfigs.debug
+        }
+
+    }
+    sourceSets.debugRelease.setRoot(&#39;src/release&#39;)
+}
+</pre>
+
+
+
+
+
+
+<h2 id="debug-inspect">Debugging and Code Inspections</h2>
+<p>Using code inspection tools such as <a href="{@docRoot}tools/help/lint.html">lint</a> is a
+standard part of Android development. Android Studio extends
+<a href="{@docRoot}tools/help/lint.html">lint</a> support with additional
+<a href="{@docRoot}tools/help/lint.html">lint</a> checks and supports Android
+<a href="{@docRoot}tools/debugging/annotations.html">annotations</a> that
+allow you to help detect more subtle code problems, such as null pointer exceptions and resource
+type conflicts. Annotations are added as metadata tags that you attach to variables, parameters,
+and return values to inspect method return values, passed parameters, and local variables and
+fields.  </p>
+
+<p>For more information on enabling <a href="{@docRoot}tools/help/lint.html">lint</a> inspections
+and running <a href="{@docRoot}tools/help/lint.html">lint</a>,
+see <a href="{@docRoot}tools/debugging/improving-w-lint.html">Improving Your Code with lint</a>.
+For more information about using annotations, see
+<a href="{@docRoot}tools/debugging/annotations.html#annotations">Improving your Code with
+Annotations</a>. </p>
+
+<p>In addition to code inspection, Android Studio provides an integrated
+<a href="{@docRoot}tools/studio/index.html#mem-cpu">memory and CPU monitor</a> view so you
+can more easily monitor your app's performance and memory usage to track CPU usage, find
+deallocated objects, locate memory leaks, and track the amount of memory the connected device is
+using. </p>
+
+
+
+<h2 id="resource-optimization">Resource Optimization </h2>
+<p>After importing and building your app, Android Studio supports several
+<a href="http://www.gradle.org">Gradle</a>-based properties to help you minimize your app's
+resource utilization. </p>
+
+
+<h3>Resource shrinking</h3>
+<p>In Android Studio, resource shrinking enables the automatic removal of unused resources from
+your packaged app and also removes resources from library dependencies if the resources are not
+actually used by your app.</p>
+
+<p>Use the <code>shrinkResources</code> attribute in the <code>buildType</code> block in your
+<code>build.gradle</code> file to enable resource shrinking. For example, if your application is
+using <a href="{@docRoot}google/play-services/index.html">Google Play services</a>
+to access Google Drive functionality, and you are not currently using
+<a href="{@docRoot}google/play-services/plus.html">Google+ Sign In</a>, then
+this setting removes the various drawable assets for the <code>SignInButton</code> buttons. </p>
+
+<p class="note"><strong>Note:</strong> Resource shrinking works in conjunction with code shrinking
+tools, such as <a href="{@docRoot}tools/help/proguard.html">ProGuard</a>. </p>
+
+<p>To enable resource shrinking, update the <code>buildTypes</code> block in the
+<code>build.gradle</code> file to include <code>minifyEnabled true</code>,
+<code>shrinkResources true</code>, and <code>proguardFiles</code> settings as shown in the
+following example <a href="http://www.gradle.org">Gradle</a> build file.</p>
+
+<pre>
+android {
+    ...
+
+    buildTypes {
+        release {
+            minifyEnabled true
+            shrinkResources true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'),
+            'proguard-rules.pro'
+        }
+    }
+}
+</pre>
+
+
+
+<h3>Filtering language resources</h3>
+<p>Use the <code>resConfig</code> attribute in your <code>build.gradle</code> file
+to filter the locale resources included in your packaged app. This filtering can be especially
+useful when library dependencies such as <code>appcompat-v7</code> and other libraries such as
+<code>google-play-services_lib</code> are included in your app. </p>
+
+<p>The following example limits the locale resources to three language settings: <code>en</code>,
+<code>de</code>, and <code>es</code>:</p>
+
+<pre>
+apply plugin: 'android'
+
+android {
+    compileSdkVersion 22
+    buildToolsVersion "22.0.1"
+
+    defaultConfig {
+        minSdkVersion 8
+        targetSdkVersion 22
+        versionCode 1
+        versionName "1.0"
+        resConfigs "en", "de", "es" //Define the included language resources.
+    }
+...
+
+</pre>
+
+
+
+<h4>Filtering bundled resources</h4>
+<p>You can also use the <code>resConfig</code> build setting to limit the bundled resources
+in any resource folder. For example, you could also add <code>resConfigs</code>
+settings for density folders, such as <code>mdpi</code> or <code>hdpi</code> to limit the drawable
+resources that are packaged in your <code>APK</code> file. This example limits the app's
+bundled resources to medium-density (MDPI) and high-density (HDPI) resources. </p>
+
+<pre>
+android {
+    defaultConfig {
+        ...
+        resConfigs "mdpi", "hdpi"
+    }
+}
+</pre>
+
+For more information about screen and resource densities, see
+<a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>
+and <a href="{@docRoot}training/multiscreen/screendensities.html">Supporting Different Densities</a>.
+
+
+<h3>Resource merging </h3>
+<p>With Android Studio, identical resources, such as copies of launcher and menu icons, may end up
+in different resource folders throughout your app. To reduce resource duplication and improve
+the performance of your app, Android Studio merges resources with an identical resource name, type,
+and qualifier into a single resource and passes the single, merged resource to the Android Asset
+Packaging Tool (AAPT) for distribution in the APK file. </p>
+
+<p>The resource merging process looks for identical resources in the following <code>/res/</code>
+folders: </p>
+<ul>
+  <li>AAR bundles of library project dependencies</li>
+  <li><code>src/main/</code> </li>
+  <li><code>src/<em>productFlavor</em>/</code> </li>
+  <li><code>src/<em>buildType</em>/</code> </li>
+</ul>
+
+<p>Identical resources are merged in the following low to high priority order: </p>
+<pre>
+dependencies --> src/main/ --> src/productFlavor/ --> src/buildType/
+</pre>
+
+<p>For example, if the <code>res/ic_menu.png</code> file is included in both the
+<code>src/main/res/</code> and <code>src/productFlavor/res/</code> folders, the resources are merged
+so only the file with the higher priority, in this case the <code>src/productFlavor/res/</code>
+file, is included in the APK file. </p>
+
+<p class="note"><strong>Note:</strong> Identical resources in the same source set are not merged
+and instead generate a resource merge error. This can happen if the <code>sourceSet</code> property
+in the <code>build.gradle</code> file is used to define multiple source sets, for example
+<code>src/main/res/</code> and <code>src/main/res2/</code>, and these folders contain identical
+resources. </p>
+
+
+
+
+<h2 id="signing">App Signing and ProGuard </h2>
+<p>Based on the imported Eclipse ADT app settings, Android Studio automatically sets up your app
+signing and maintains any ProGuard settings. </p>
+
+<h3>App Signing</h3>
+<p>If your app used a debug certificate in Eclipse ADT, Android Studio continues to reference that
+certificate. Otherwise, the <code>debug</code> configuration uses the Android Studio generated
+debug keystore, with a known password and a default key with a known password located in
+<code>$HOME/.android/debug.keystore</code>. The <code>debug</code> build type is set to use this
+debug <code>SigningConfig</code> automatically when you run or debug your project
+from Android Studio. </p>
+
+<p>In release mode, Android Studio applies the release certificate used in Eclipse ADT. If no
+release certificate was located during the import process, add the release signing configuration to
+the <code>build.gradle</code> file or use the <strong> Build > Generate Signed APK</strong> menu
+option to open the Generate Signed APK Wizard. For more information about signing your app, see
+<a href="{@docRoot}tools/publishing/app-signing.html">Signing Your Applications</a>. </p>
+
+
+<h3>ProGuard</h3>
+<p>If the <a href="{@docRoot}tools/help/proguard.html">ProGuard</a> option is specified in the
+<code>project.properties</code> file in the Eclipse ADT project, Android Studio imports the
+<a href="{@docRoot}tools/help/proguard.html">ProGuard</a> files and adds the
+<a href="{@docRoot}tools/help/proguard.html">ProGuard</a> settings to the
+<code>build.gradle</code> file. <a href="{@docRoot}tools/help/proguard.html">ProGuard</a> is
+supported through the <code>minifyEnabled</code> property as shown in this example. </p>
+
+<pre>
+android {
+    buildTypes {
+        release {
+            minifyEnabled true
+            proguardFile getDefaultProguardFile(&#39;proguard-android.txt&#39;)
+        }
+    }
+
+    productFlavors {
+        flavor1 {
+        }
+        flavor2 {
+            proguardFile &#39;some-other-rules.txt&#39;
+        }
+    }
+}
+
+</pre></p>
+
+
+
+
+<h2 id="support-lib">Android Support Repository and Google Play services Repository</h2>
+<p>While Eclipse ADT uses the Android <a href="{@docRoot}tools/support-library/index.html">Support
+Library</a> and Google Play services Library, Android Studio replaces these libraries during the
+import process with the Android Support Repository and Google Repository to maintain
+compatible functionality and support new Android features. Android Studio adds these dependencies
+as Maven dependencies using the known Maven coordinates, so these dependencies do not require
+manual updates.  </p>
+
+<p>In Eclipse, in order to use a
+<a href="{@docRoot}tools/support-library/index.html">Support Library</a>, you must modify your
+project's classpath dependencies within your development environment for each
+<a href="{@docRoot}tools/support-library/index.html">Support Library</a> you want to use. In
+Android Studio, you no longer need to copy library sources into your
+own projects, you can simply declare a dependency and the library is automatically downloaded and
+merged into your project. This includes automatically merging in resources, manifest entries,
+<a href="{@docRoot}tools/help/proguard.html">ProGuard</a> exclusion rules, and custom lint rules
+at build time. </p>
+
+<p>Android Studio also supports binary library Android ARchives (AARs). AARs are a library project's
+main output as a combination of compiled code (as a jar file and/or native .so files) and
+resources (manifest, res, assets). <p/>
+
+
+<h2 id="app-package">App Packaging</h2>
+<p>The Android build system introduces the use of the <code>applicationId</code> attribute to
+uniquely identify application packages for publishing. The application ID is set in the
+<code>android</code> section of the <code>build.gradle</code> file. </p>
+
+<p>The <code>applicationId</code> is specified only in your <code>build.gradle</code> file, and
+not in the
+<code>AndroidManifest.xml</code> file. The Gradle-based build system enables you
+to uniquely identify different packages for each build variant based on product flavors and build
+types. You can also add the <code>applicationIdSuffix</code> property to the build type in the
+<code>build.gradle</code> file to append an identifier, such as '.debug', to the  application ID
+generated for each product flavor. </p>
+
+
+
+<h2 id="software-updates">Software Updates</h2>
+<p>Android Studio provides several levels of update and maintenance to help you keep Android Studio
+up-to-date based on your code-level preference: </p>
+
+<ul>
+  <li><strong>Canary channel</strong>: Canary builds provide bleeding edge releases and are updated
+  about weekly. These builds do get tested, but are still subject to bugs, as these are
+  early releases. This is not recommended for production.</li>
+  <li><strong>Dev channel</strong>: Dev builds are canary builds that passed initial testing and
+  usage. They are updated roughly bi-weekly or monthly.</li>
+  <li><strong>Beta channel</strong>: Beta builds provide beta-quality releases for final testing
+  and feedback before a production release.</li>
+  <li><strong>Stable channel</strong>: Stable builds provide stable, production-ready release
+  versions.</li>
+</ul>
+
+
+
+<h2 id="version-control">Version Control </h2>
+<p>Eclipse ADT supports version control through the use of plugins, such as the EGit and Subversive
+plug-ins.  </p>
+
+<p>Android Studio supports a variety of version control systems (Git, GitHub, CVS, Mercurial,
+Subversion, and Google Cloud) so version control operations can continue from within Android
+Studio. </p>
+
+<p>After importing your Eclipse ADT app into Android Studio, use the
+Android Studio <em>VCS</em> menu options to enable VCS support for the desired version control
+system, create a repository, import the new files into version control, and perform other version
+control operations.  </p>
+
+<p class="note"><strong>Note:</strong> You can also use the
+<strong>File &gt; Setting &gt; Version Control</strong> menu option to setup and modify the version
+control settings. </p>
+
+<h3>Files to ignore </h3>
+<p>A number of Android Studio files are typically not added to version control as these are
+temporary files or files that get overwritten with each build. These files are listed in
+an exclusion file, such as <code>.gitignore</code>, for the project and each app module.
+Typically, the following files are excluded from version control:  </p>
+
+<ul>
+  <li>.gradle </li>
+  <li>/local.properties </li>
+  <li>/.idea/workspace.xml </li>
+  <li>/.idea/libraries </li>
+  <li>.DS_Store</li>
+  <li>/build </li>
+  <li>/captures </li>
+</ul>
index 0113347..fa6d987 100644 (file)
@@ -175,7 +175,7 @@ To configure custom build settings in an Android Studio project, see
 <a href="{@docRoot}tools/building/configuring-gradle.html">Configuring Gradle Builds</a>.</p>
 
 
-<h3>Application ID for package identification </h3>
+<h3 id="app-id">Application ID for package identification </h3>
 <p>With the Android build system, the <em>applicationId</em> attribute is used to
 uniquely identify application packages for publishing. The application ID is set in the
 <em>android</em> section of the <code>build.gradle</code> file.
index a5e617d..abfa030 100644 (file)
     <div class="nav-section-header"><a href="<?cs var:toroot ?>tools/help/index.html"><span
 class="en">Tools Help</span></a></div>
     <ul>
-      <li class="nav-section">
-        <div class="nav-section-header"><a href="<?cs var:toroot ?>tools/help/adb.html">adb</a></div>
-        <ul>
-          <li><a href="<?cs var:toroot ?>tools/help/shell.html">Shell commands</a></li>
-        </ul>
-      </li>
+      <li><a href="<?cs var:toroot ?>tools/help/adb.html">adb</a></li>
       <li><a href="<?cs var:toroot ?>tools/help/android.html">android</a></li>
       <li><a href="<?cs var:toroot ?>tools/help/avd-manager.html">AVD Manager</a></li>
       <li><a href="<?cs var:toroot ?>tools/help/bmgr.html">bmgr</a>
@@ -173,7 +168,6 @@ class="en">Tools Help</span></a></div>
       <li><a href="<?cs var:toroot ?>tools/help/mksdcard.html">mksdcard</a></li>
        <li><a href="<?cs var:toroot ?>tools/help/proguard.html" zh-cn-lang="ProGuard">ProGuard</a></li>
        <li><a href="<?cs var:toroot ?>tools/help/sdk-manager.html">SDK Manager</a></li>
-       <li><a href="<?cs var:toroot ?>tools/help/sqlite3.html">sqlite3</a></li>
        <li><a href="<?cs var:toroot ?>tools/help/systrace.html">Systrace</a></li>
        <li><a href="<?cs var:toroot ?>tools/help/gltracer.html">Tracer for OpenGL ES</a></li>
        <li><a href="<?cs var:toroot ?>tools/help/traceview.html">Traceview</a></li>
@@ -195,71 +189,13 @@ class="en">Tools Help</span></a></div>
         <span class="en">Configuring Gradle Builds</span></a></li>
       <li><a href="<?cs var:toroot ?>tools/building/plugin-for-gradle.html">
         <span class="en">Android Plugin for Gradle</span></a></li>
+      <li><a href="<?cs var:toroot ?>tools/building/manifest-merge.html">
+        <span class="en">Manifest Merging</span></a></li>
       <li><a href="<?cs var:toroot ?>tools/building/multidex.html">
         <span class="en">Apps Over 65K Methods</span></a></li>
       </ul>
   </li><!-- end of build system -->
 
-<!-- Performance Tools menu-->
-  <li class="nav-section">
-    <div class="nav-section-header">
-      <a href="<?cs var:toroot ?>tools/performance/index.html">Peformance Tools</a>
-    </div>
-    <ul>
-      <li><a href="<?cs var:toroot ?>tools/performance/debug-gpu-overdraw/index.html">
-        Overdraw Debugger</a>
-      </li>
-      <li><a href="<?cs var:toroot ?>tools/performance/profile-gpu-rendering/index.html">
-        Rendering Profiler</a>
-      </li>
-      <li class="nav-section">
-        <div class="nav-section-header">
-          <a href="<?cs var:toroot ?>tools/performance/hierarchy-viewer/index.html">
-          Hierarchy Viewer</a></div>
-        <ul>
-          <li><a href="<?cs var:toroot ?>tools/performance/hierarchy-viewer/setup.html"><span
-            class="en">Setup</span></a>
-          </li>
-          <li><a href="<?cs var:toroot ?>tools/performance/hierarchy-viewer/profiling.html"><span
-            class="en">Profiling</span></a>
-          </li>
-        </ul>
-      </li>
-      <li class="nav-section">
-        <div class="nav-section-header">
-          <a href="<?cs var:toroot ?>tools/performance/comparison.html">
-          Memory Profilers</a></div>
-        <ul>
-          <li><a href="<?cs var:toroot ?>tools/performance/memory-monitor/index.html"><span
-            class="en">Memory Monitor</span></a>
-          </li>
-          <li><a href="<?cs var:toroot ?>tools/performance/heap-viewer/index.html"><span
-            class="en">Heap Viewer</span></a>
-          </li>
-          <li><a href="<?cs var:toroot ?>tools/performance/allocation-tracker/index.html"><span
-            class="en">Allocation Tracker</span></a>
-          </li>
-        </ul>
-      </li>
-      <li><a href="<?cs var:toroot ?>tools/performance/traceview/index.html">
-        Traceview</a>
-      </li>
-      <li><a href="<?cs var:toroot ?>tools/performance/systrace/index.html">
-        Systrace</a>
-      </li>
-      <li class="nav-section">
-        <div class="nav-section-header">
-          <a href="<?cs var:toroot ?>tools/performance/batterystats-battery-historian/index.html">
-          Battery Profilers</a></div>
-        <ul>
-          <li><a href="<?cs var:toroot ?>tools/performance/batterystats-battery-historian/charts.html"><span
-            class="en">Historian Charts</span></a>
-          </li>
-        </ul>
-      </li>
-    </ul>
-  </li><!-- End Performance Tools menu-->
-
   <!-- Testing Tools menu-->
 
   <li class="nav-section">
@@ -357,7 +293,15 @@ class="en">Data Binding Library</span></a></div>
       <span class="en">Eclipse with ADT</span></a>
     </div>
     <ul>
-    <li><a href="<?cs var:toroot ?>sdk/installing/migrate.html">Migrating to Android Studio</a></li>
+        <li class="nav-section">
+        <div class="nav-section-header"><a href="<?cs var:toroot ?>sdk/installing/migrate.html">
+          <span class="en">Migrating to Android Studio</span></a></div>
+         <ul>
+            <li><a href="<?cs var:toroot ?>tools/studio/eclipse-transition-guide.html">
+            Transition Guide</span></a> </li>
+         </ul>
+        </li>
+
     <li><a href="<?cs var:toroot ?>sdk/installing/installing-adt.html">
         <span class="en">Installing the Eclipse Plugin</span></a></li>
     <li><a href="<?cs var:toroot ?>tools/projects/projects-eclipse.html">Managing Projects</a></li>
index 5e892bf..6d19cee 100644 (file)
@@ -139,7 +139,8 @@ rotates the device).
 <p>Here is an example of a singleton class that provides {@code RequestQueue} and
 {@code ImageLoader} functionality:</p>
 
-<pre>private static MySingleton mInstance;
+<pre>public class MySingleton {
+    private static MySingleton mInstance;
     private RequestQueue mRequestQueue;
     private ImageLoader mImageLoader;
     private static Context mCtx;
index 1410d87..7680342 100644 (file)
@@ -91,6 +91,7 @@ public:
      * a matching call to endIteration with the same cookie.
      */
     bool startIteration(void** cookie);
+    bool startIteration(void** cookie, const char* prefix, const char* suffix);
 
     /**
      * Return the next entry in iteration order, or NULL if there are no more
index 50f3ed4..38cacd0 100644 (file)
@@ -731,6 +731,21 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
         return mMainDataStreamer.getProducedOutputSizeBytes();
     }
 
+    static String opmodeToString(int opmode) {
+        switch (opmode) {
+            case Cipher.ENCRYPT_MODE:
+                return "ENCRYPT_MODE";
+            case Cipher.DECRYPT_MODE:
+                return "DECRYPT_MODE";
+            case Cipher.WRAP_MODE:
+                return "WRAP_MODE";
+            case Cipher.UNWRAP_MODE:
+                return "UNWRAP_MODE";
+            default:
+                return String.valueOf(opmode);
+        }
+    }
+
     // The methods below need to be implemented by subclasses.
 
     /**
index 8e58195..94ed8b4 100644 (file)
@@ -60,9 +60,10 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
         }
 
         @Override
-        protected boolean isEncryptingUsingPrivateKeyPermitted() {
-            // RSA encryption with no padding using private key is is a way to implement raw RSA
-            // signatures. We have to support this.
+        protected boolean adjustConfigForEncryptingWithPrivateKey() {
+            // RSA encryption with no padding using private key is a way to implement raw RSA
+            // signatures which JCA does not expose via Signature. We thus have to support this.
+            setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
             return true;
         }
 
@@ -198,6 +199,15 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
         }
 
         @Override
+        protected boolean adjustConfigForEncryptingWithPrivateKey() {
+            // RSA encryption with PCKS#1 padding using private key is a way to implement RSA
+            // signatures with PKCS#1 padding. We have to support this for legacy reasons.
+            setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
+            setKeymasterPaddingOverride(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
+            return true;
+        }
+
+        @Override
         protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
 
         @Override
@@ -425,6 +435,7 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
     }
 
     private final int mKeymasterPadding;
+    private int mKeymasterPaddingOverride;
 
     private int mModulusSizeBytes = -1;
 
@@ -458,20 +469,15 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
                     // Permitted
                     break;
                 case Cipher.ENCRYPT_MODE:
-                    if (!isEncryptingUsingPrivateKeyPermitted()) {
+                case Cipher.WRAP_MODE:
+                    if (!adjustConfigForEncryptingWithPrivateKey()) {
                         throw new InvalidKeyException(
-                                "RSA private keys cannot be used with Cipher.ENCRYPT_MODE"
+                                "RSA private keys cannot be used with " + opmodeToString(opmode)
+                                + " and padding "
+                                + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding)
                                 + ". Only RSA public keys supported for this mode");
                     }
-                    // JCA doesn't provide a way to generate raw RSA signatures (with arbitrary
-                    // padding). Thus, encrypting with private key is used instead.
-                    setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
                     break;
-                case Cipher.WRAP_MODE:
-                    throw new InvalidKeyException(
-                            "RSA private keys cannot be used with Cipher.WRAP_MODE"
-                            + ". Only RSA public keys supported for this mode");
-                    // break;
                 default:
                     throw new InvalidKeyException(
                             "RSA private keys cannot be used with opmode: " + opmode);
@@ -485,12 +491,15 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
                     break;
                 case Cipher.DECRYPT_MODE:
                 case Cipher.UNWRAP_MODE:
-                    throw new InvalidKeyException("RSA public keys cannot be used with opmode: "
-                            + opmode + ". Only RSA private keys supported for this opmode.");
+                    throw new InvalidKeyException(
+                            "RSA public keys cannot be used with " + opmodeToString(opmode)
+                            + " and padding "
+                            + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding)
+                            + ". Only RSA private keys supported for this opmode.");
                     // break;
                 default:
                     throw new InvalidKeyException(
-                            "RSA public keys cannot be used with opmode: " + opmode);
+                            "RSA public keys cannot be used with " + opmodeToString(opmode));
             }
         }
 
@@ -511,13 +520,22 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
         setKey(keystoreKey);
     }
 
-    protected boolean isEncryptingUsingPrivateKeyPermitted() {
+    /**
+     * Adjusts the configuration of this cipher for encrypting using the private key.
+     *
+     * <p>The default implementation does nothing and refuses to adjust the configuration.
+     *
+     * @return {@code true} if the configuration has been adjusted, {@code false} if encrypting
+     *         using private key is not permitted for this cipher.
+     */
+    protected boolean adjustConfigForEncryptingWithPrivateKey() {
         return false;
     }
 
     @Override
     protected final void resetAll() {
         mModulusSizeBytes = -1;
+        mKeymasterPaddingOverride = -1;
         super.resetAll();
     }
 
@@ -530,7 +548,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
     protected void addAlgorithmSpecificParametersToBegin(
             @NonNull KeymasterArguments keymasterArgs) {
         keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
-        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+        int keymasterPadding = getKeymasterPaddingOverride();
+        if (keymasterPadding == -1) {
+            keymasterPadding = mKeymasterPadding;
+        }
+        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, keymasterPadding);
         int purposeOverride = getKeymasterPurposeOverride();
         if ((purposeOverride != -1)
                 && ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN)
@@ -568,4 +590,15 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
         }
         return mModulusSizeBytes;
     }
+
+    /**
+     * Overrides the default padding of the crypto operation.
+     */
+    protected final void setKeymasterPaddingOverride(int keymasterPadding) {
+        mKeymasterPaddingOverride = keymasterPadding;
+    }
+
+    protected final int getKeymasterPaddingOverride() {
+        return mKeymasterPaddingOverride;
+    }
 }
index de483f4..dc8f1e3 100644 (file)
@@ -140,21 +140,64 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
             throw new NullPointerException("alias == null");
         }
 
-        byte[] certificate = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
-        if (certificate != null) {
-            return wrapIntoKeyStoreCertificate(
-                    Credentials.USER_PRIVATE_KEY + alias, toCertificate(certificate));
+        byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+        if (encodedCert != null) {
+            return getCertificateForPrivateKeyEntry(alias, encodedCert);
         }
 
-        certificate = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
-        if (certificate != null) {
-            return wrapIntoKeyStoreCertificate(
-                    Credentials.USER_PRIVATE_KEY + alias, toCertificate(certificate));
+        encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+        if (encodedCert != null) {
+            return getCertificateForTrustedCertificateEntry(encodedCert);
         }
 
+        // This entry/alias does not contain a certificate.
         return null;
     }
 
+    private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) {
+        // For this certificate there shouldn't be a private key in this KeyStore entry. Thus,
+        // there's no need to wrap this certificate as opposed to the certificate associated with
+        // a private key entry.
+        return toCertificate(encodedCert);
+    }
+
+    private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) {
+        // All crypto algorithms offered by Android Keystore for its private keys must also
+        // be offered for the corresponding public keys stored in the Android Keystore. The
+        // complication is that the underlying keystore service operates only on full key pairs,
+        // rather than just public keys or private keys. As a result, Android Keystore-backed
+        // crypto can only be offered for public keys for which keystore contains the
+        // corresponding private key. This is not the case for certificate-only entries (e.g.,
+        // trusted certificates).
+        //
+        // getCertificate().getPublicKey() is the only way to obtain the public key
+        // corresponding to the private key stored in the KeyStore. Thus, we need to make sure
+        // that the returned public key points to the underlying key pair / private key
+        // when available.
+
+        X509Certificate cert = toCertificate(encodedCert);
+        if (cert == null) {
+            // Failed to parse the certificate.
+            return null;
+        }
+
+        String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+        if (mKeyStore.contains(privateKeyAlias)) {
+            // As expected, keystore contains the private key corresponding to this public key. Wrap
+            // the certificate so that its getPublicKey method returns an Android Keystore
+            // PublicKey. This key will delegate crypto operations involving this public key to
+            // Android Keystore when higher-priority providers do not offer these crypto
+            // operations for this key.
+            return wrapIntoKeyStoreCertificate(privateKeyAlias, cert);
+        } else {
+            // This KeyStore entry/alias is supposed to contain the private key corresponding to
+            // the public key in this certificate, but it does not for some reason. It's probably a
+            // bug. Let other providers handle crypto operations involving the public key returned
+            // by this certificate's getPublicKey.
+            return cert;
+        }
+    }
+
     /**
      * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key
      * returned by the certificate contains information about the alias of the private key in
index 6f927b4..a6f6d8c 100644 (file)
@@ -126,10 +126,18 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod,
     return true;
 }
 
-bool ZipFileRO::startIteration(void** cookie)
+bool ZipFileRO::startIteration(void** cookie) {
+  return startIteration(cookie, NULL, NULL);
+}
+
+bool ZipFileRO::startIteration(void** cookie, const char* prefix, const char* suffix)
 {
     _ZipEntryRO* ze = new _ZipEntryRO;
-    int32_t error = StartIteration(mHandle, &(ze->cookie), NULL /* prefix */);
+    ZipEntryName pe(prefix ? prefix : "");
+    ZipEntryName se(suffix ? suffix : "");
+    int32_t error = StartIteration(mHandle, &(ze->cookie),
+                                   prefix ? &pe : NULL,
+                                   suffix ? &se : NULL);
     if (error) {
         ALOGW("Could not start iteration over %s: %s", mFileName, ErrorCodeString(error));
         delete ze;
index 5e9b213..c98932c 100644 (file)
@@ -19,6 +19,7 @@
 #include <SkPath.h>
 
 #include "Rect.h"
+#include "utils/MathUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -85,6 +86,11 @@ public:
         return mShouldClip && (mType == kOutlineType_RoundRect);
     }
 
+    bool willRoundRectClip() const {
+        // only round rect outlines can be used for clipping
+        return willClip() && MathUtils::isPositive(mRadius);
+    }
+
     bool getAsRoundRect(Rect* outRect, float* outRadius) const {
         if (mType == kOutlineType_RoundRect) {
             outRect->set(mBounds);
index 81cf2df..11abd70 100644 (file)
@@ -169,7 +169,7 @@ public:
         bool functorsNeedLayer = ancestorDictatesFunctorsNeedLayer
 
                 // Round rect clipping forces layer for functors
-                || CC_UNLIKELY(getOutline().willClip())
+                || CC_UNLIKELY(getOutline().willRoundRectClip())
                 || CC_UNLIKELY(getRevealClip().willClip())
 
                 // Complex matrices forces layer, due to stencil clipping
index 024ff10..09d1258 100644 (file)
@@ -21,6 +21,7 @@
 #include <utils/Log.h>
 #include <utils/Trace.h>
 #include <utils/Vector.h>
+#include <utils/MathUtils.h>
 
 #include "AmbientShadow.h"
 #include "Properties.h"
@@ -172,6 +173,8 @@ int ShadowTessellator::getExtraVertexNumber(const Vector2& vector1,
     // acos( )     --- [0, M_PI]
     // floor(...)  --- [0, EXTRA_VERTEX_PER_PI]
     float dotProduct = vector1.dot(vector2);
+    // make sure that dotProduct value is in acsof input range [-1, 1]
+    dotProduct = MathUtils::clamp(dotProduct, -1.0f, 1.0f);
     // TODO: Use look up table for the dotProduct to extraVerticesNumber
     // computation, if needed.
     float angle = acosf(dotProduct);
index a806440..b3b2b97 100644 (file)
@@ -619,10 +619,6 @@ public class AudioManager {
                 com.android.internal.R.bool.config_useVolumeKeySounds);
         mUseFixedVolume = getContext().getResources().getBoolean(
                 com.android.internal.R.bool.config_useFixedVolume);
-        sAudioPortEventHandler.init();
-
-        mPortListener = new OnAmPortUpdateListener();
-        registerAudioPortUpdateListener(mPortListener);
     }
 
     private Context getContext() {
@@ -3554,6 +3550,7 @@ public class AudioManager {
      * @hide
      */
     public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+        sAudioPortEventHandler.init();
         sAudioPortEventHandler.registerListener(l);
     }
 
@@ -3586,6 +3583,7 @@ public class AudioManager {
 
     static int updateAudioPortCache(ArrayList<AudioPort> ports, ArrayList<AudioPatch> patches,
                                     ArrayList<AudioPort> previousPorts) {
+        sAudioPortEventHandler.init();
         synchronized (sAudioPortGeneration) {
 
             if (sAudioPortGeneration == AUDIOPORT_GENERATION_INIT) {
@@ -3849,6 +3847,12 @@ public class AudioManager {
             android.os.Handler handler) {
         if (callback != null && !mDeviceCallbacks.containsKey(callback)) {
             synchronized (mDeviceCallbacks) {
+                if (mDeviceCallbacks.size() == 0) {
+                    if (mPortListener == null) {
+                        mPortListener = new OnAmPortUpdateListener();
+                    }
+                    registerAudioPortUpdateListener(mPortListener);
+                }
                 NativeEventHandlerDelegate delegate =
                         new NativeEventHandlerDelegate(callback, handler);
                 mDeviceCallbacks.put(callback, delegate);
@@ -3867,6 +3871,9 @@ public class AudioManager {
         synchronized (mDeviceCallbacks) {
             if (mDeviceCallbacks.containsKey(callback)) {
                 mDeviceCallbacks.remove(callback);
+                if (mDeviceCallbacks.size() == 0) {
+                    unregisterAudioPortUpdateListener(mPortListener);
+                }
             }
         }
     }
index 6c26220..28d0713 100644 (file)
@@ -705,6 +705,9 @@ public final class MediaCodecInfo {
 
             int maxInstances = Utils.parseIntSafely(
                     map.get("max-supported-instances"), mMaxSupportedInstances);
+            // TODO: replace all max-supported-instances with max-concurrent-instances.
+            maxInstances = Utils.parseIntSafely(
+                    map.get("max-concurrent-instances"), maxInstances);
             mMaxSupportedInstances =
                     Range.create(1, MAX_SUPPORTED_INSTANCES_LIMIT).clamp(maxInstances);
 
index ac9dc85..d265e0d 100644 (file)
@@ -50,7 +50,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
         mAppearAnimationUtils = new AppearAnimationUtils(context);
         mDisappearAnimationUtils = new DisappearAnimationUtils(context,
                 125, 0.6f /* translationScale */,
-                0.6f /* delayScale */, AnimationUtils.loadInterpolator(
+                0.45f /* delayScale */, AnimationUtils.loadInterpolator(
                         mContext, android.R.interpolator.fast_out_linear_in));
         mDisappearYTranslation = getResources().getDimensionPixelSize(
                 R.dimen.disappear_y_translation);
index 59a8ad5..3568429 100644 (file)
@@ -109,7 +109,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit
                         mContext, android.R.interpolator.linear_out_slow_in));
         mDisappearAnimationUtils = new DisappearAnimationUtils(context,
                 125, 1.2f /* translationScale */,
-                0.8f /* delayScale */, AnimationUtils.loadInterpolator(
+                0.6f /* delayScale */, AnimationUtils.loadInterpolator(
                         mContext, android.R.interpolator.fast_out_linear_in));
         mDisappearYTranslation = getResources().getDimensionPixelSize(
                 R.dimen.disappear_y_translation);
index 0180a30..9325246 100644 (file)
@@ -387,7 +387,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
         } else if (isActive()) {
             // This is the active connection on non-passpoint network
             summary.append(getSummary(mContext, getDetailedState(),
-                    networkId == WifiConfiguration.INVALID_NETWORK_ID));
+                    mInfo != null && mInfo.isEphemeral()));
         } else if (mConfig != null && mConfig.isPasspoint()) {
             String format = mContext.getString(R.string.available_via_passpoint);
             summary.append(String.format(format, mConfig.providerFriendlyName));
@@ -620,7 +620,8 @@ public class AccessPoint implements Comparable<AccessPoint> {
     }
 
     public boolean isEphemeral() {
-        return !isSaved() && mNetworkInfo != null && mNetworkInfo.getState() != State.DISCONNECTED;
+        return mInfo != null && mInfo.isEphemeral() &&
+                mNetworkInfo != null && mNetworkInfo.getState() != State.DISCONNECTED;
     }
 
     /** Return whether the given {@link WifiInfo} is for this access point. */
@@ -757,7 +758,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
             mAccessPointListener.onAccessPointChanged(this);
         }
     }
-    
+
     public static String getSummary(Context context, String ssid, DetailedState state,
             boolean isEphemeral, String passpointProvider) {
         if (state == DetailedState.CONNECTED && ssid == null) {
index 44b9d8b..41043eb 100644 (file)
@@ -905,7 +905,9 @@ public class SettingsProvider extends ContentProvider {
     private boolean mutateSystemSetting(String name, String value, int runAsUserId,
             int operation) {
         // Check for permissions first.
-        hasPermissionsToMutateSystemSettings();
+        if (!hasPermissionsToMutateSystemSettings()) {
+            return false;
+        }
 
         // Verify whether this operation is allowed for the calling package.
         if (!isAppOpWriteSettingsAllowedForCallingPackage()) {
index 005077f..155f5ea 100644 (file)
     <dimen name="managed_profile_toast_padding">4dp</dimen>
 
     <!-- Thickness of the assist disclosure beams -->
-    <dimen name="assist_disclosure_thickness">4dp</dimen>
+    <dimen name="assist_disclosure_thickness">3dp</dimen>
 
     <!-- Thickness of the shadows of the assist disclosure beams -->
     <dimen name="assist_disclosure_shadow_thickness">1.5dp</dimen>
index 29d2a01..3657cf2 100644 (file)
@@ -44,6 +44,7 @@ public final class Prefs {
     })
     public @interface Key {
         String SEARCH_APP_WIDGET_ID = "searchAppWidgetId";
+        String SEARCH_APP_WIDGET_PACKAGE = "searchAppWidgetPackage";
         String DEBUG_MODE_ENABLED = "debugModeEnabled";
         String HOTSPOT_TILE_LAST_USED = "HotspotTileLastUsed";
         String COLOR_INVERSION_TILE_LAST_USED = "ColorInversionTileLastUsed";
@@ -80,6 +81,14 @@ public final class Prefs {
         get(context).edit().putLong(key, value).apply();
     }
 
+    public static String getString(Context context, @Key String key, String defaultValue) {
+        return get(context).getString(key, defaultValue);
+    }
+
+    public static void putString(Context context, @Key String key, String value) {
+        get(context).edit().putString(key, value).apply();
+    }
+
     public static Map<String, ?> getAll(Context context) {
         return get(context).getAll();
     }
index 7838119..6ba5626 100644 (file)
@@ -125,14 +125,11 @@ public class AssistManager {
         }
     }
 
-    public void onGestureInvoked(boolean vibrate) {
+    public void onGestureInvoked() {
         if (mAssistComponent == null) {
             return;
         }
 
-        if (vibrate) {
-            vibrate();
-        }
         final boolean isService = isAssistantService();
         if (isService || !isVoiceSessionRunning()) {
             showOrb();
@@ -290,10 +287,6 @@ public class AssistManager {
         v.setImageDrawable(null);
     }
 
-    private void vibrate() {
-        mView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
-    }
-
     private boolean isAssistantService() {
         return mAssistComponent == null ?
                 false : mAssistComponent.equals(getVoiceInteractorComponentName());
index 89c456c..6a45369 100644 (file)
@@ -20,7 +20,6 @@ import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ITaskStackListener;
-import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -37,11 +36,10 @@ import android.os.Handler;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.MutableBoolean;
-import android.util.Pair;
 import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
-
+import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SystemUI;
@@ -170,6 +168,7 @@ public class Recents extends SystemUI
     Handler mHandler;
     TaskStackListenerImpl mTaskStackListener;
     RecentsOwnerEventProxyReceiver mProxyBroadcastReceiver;
+    RecentsAppWidgetHost mAppWidgetHost;
     boolean mBootCompleted;
     boolean mStartAnimationTriggered;
     boolean mCanReuseTaskStackViews = true;
@@ -235,6 +234,7 @@ public class Recents extends SystemUI
         mSystemServicesProxy = new SystemServicesProxy(mContext);
         mHandler = new Handler();
         mTaskStackBounds = new Rect();
+        mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId);
 
         // Register the task stack listener
         mTaskStackListener = new TaskStackListenerImpl(mHandler);
@@ -255,7 +255,7 @@ public class Recents extends SystemUI
         // Initialize some static datastructures
         TaskStackViewLayoutAlgorithm.initializeCurve();
         // Load the header bar layout
-        reloadHeaderBarLayout(true);
+        reloadHeaderBarLayout();
 
         // When we start, preload the data associated with the previous recent tasks.
         // We can use a new plan since the caches will be the same.
@@ -488,11 +488,11 @@ public class Recents extends SystemUI
         // Don't reuse task stack views if the configuration changes
         mCanReuseTaskStackViews = false;
         // Reload the header bar layout
-        reloadHeaderBarLayout(false);
+        reloadHeaderBarLayout();
     }
 
     /** Prepares the header bar layout. */
-    void reloadHeaderBarLayout(boolean reloadWidget) {
+    void reloadHeaderBarLayout() {
         Resources res = mContext.getResources();
         mWindowRect = mSystemServicesProxy.getWindowRect();
         mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
@@ -500,12 +500,16 @@ public class Recents extends SystemUI
         mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
         mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
         mConfig.updateOnConfigurationChange();
-        if (reloadWidget) {
-            // Reload the widget id before we get the task stack bounds
-            reloadSearchBarAppWidget(mContext, mSystemServicesProxy);
+        Rect searchBarBounds = new Rect();
+        // Try and pre-emptively bind the search widget on startup to ensure that we
+        // have the right thumbnail bounds to animate to.
+        // Note: We have to reload the widget id before we get the task stack bounds below
+        if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
+            mConfig.getSearchBarBounds(mWindowRect.width(), mWindowRect.height(),
+                    mStatusBarHeight, searchBarBounds);
         }
         mConfig.getAvailableTaskStackBounds(mWindowRect.width(), mWindowRect.height(),
-                mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
+                mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0), searchBarBounds,
                 mTaskStackBounds);
         if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
             mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
@@ -532,24 +536,6 @@ public class Recents extends SystemUI
         }
     }
 
-    /** Prepares the search bar app widget */
-    void reloadSearchBarAppWidget(Context context, SystemServicesProxy ssp) {
-        // Try and pre-emptively bind the search widget on startup to ensure that we
-        // have the right thumbnail bounds to animate to.
-        if (Constants.DebugFlags.App.EnableSearchLayout) {
-            // If there is no id, then bind a new search app widget
-            if (mConfig.searchBarAppWidgetId < 0) {
-                AppWidgetHost host = new RecentsAppWidgetHost(context,
-                        Constants.Values.App.AppWidgetHostId);
-                Pair<Integer, AppWidgetProviderInfo> widgetInfo = ssp.bindSearchAppWidget(host);
-                if (widgetInfo != null) {
-                    // Save the app widget id into the settings
-                    mConfig.updateSearchBarAppWidgetId(context, widgetInfo.first);
-                }
-            }
-        }
-    }
-
     /** Toggles the recents activity */
     void toggleRecentsActivity() {
         // If the user has toggled it too quickly, then just eat up the event here (it's better than
@@ -799,27 +785,13 @@ public class Recents extends SystemUI
             // If there is no thumbnail transition, but is launching from home into recents, then
             // use a quick home transition and do the animation from home
             if (hasRecentTasks) {
-                // Get the home activity info
                 String homeActivityPackage = mSystemServicesProxy.getHomeActivityPackageName();
-                // Get the search widget info
-                AppWidgetProviderInfo searchWidget = null;
-                String searchWidgetPackage = null;
-                if (mConfig.hasSearchBarAppWidget()) {
-                    searchWidget = mSystemServicesProxy.getAppWidgetInfo(
-                            mConfig.searchBarAppWidgetId);
-                } else {
-                    searchWidget = mSystemServicesProxy.resolveSearchAppWidget();
-                }
-                if (searchWidget != null && searchWidget.provider != null) {
-                    searchWidgetPackage = searchWidget.provider.getPackageName();
-                }
-                // Determine whether we are coming from a search owned home activity
-                boolean fromSearchHome = false;
-                if (homeActivityPackage != null && searchWidgetPackage != null &&
-                        homeActivityPackage.equals(searchWidgetPackage)) {
-                    fromSearchHome = true;
-                }
+                String searchWidgetPackage =
+                        Prefs.getString(mContext, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null);
 
+                // Determine whether we are coming from a search owned home activity
+                boolean fromSearchHome = (homeActivityPackage != null) &&
+                        homeActivityPackage.equals(searchWidgetPackage);
                 ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
                 startAlternateRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
                         false /* fromThumbnail */, stackVr);
index da7247c..bf15c68 100644 (file)
@@ -28,7 +28,6 @@ import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.util.Pair;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewStub;
@@ -50,7 +49,6 @@ import com.android.systemui.recents.views.SystemBarScrimViews;
 import com.android.systemui.recents.views.ViewAnimation;
 
 import java.lang.ref.WeakReference;
-import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 
 /**
@@ -75,9 +73,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
     RecentsResizeTaskDialog mResizeTaskDebugDialog;
 
     // Search AppWidget
-    AppWidgetProviderInfo mSearchAppWidgetInfo;
+    AppWidgetProviderInfo mSearchWidgetInfo;
     RecentsAppWidgetHost mAppWidgetHost;
-    RecentsAppWidgetHostView mSearchAppWidgetHostView;
+    RecentsAppWidgetHostView mSearchWidgetHostView;
 
     // Runnables to finish the Recents activity
     FinishRecentsRunnable mFinishLaunchHomeRunnable;
@@ -168,8 +166,10 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
                 // When the screen turns off, dismiss Recents to Home
                 dismissRecentsToHome(false);
             } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
-                // When the search activity changes, update the Search widget
-                refreshSearchWidget();
+                // When the search activity changes, update the search widget view
+                SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+                mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(context, mAppWidgetHost);
+                refreshSearchWidgetView();
             }
         }
     };
@@ -253,7 +253,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
             if (mRecentsView.hasValidSearchBar()) {
                 mRecentsView.setSearchBarVisibility(View.VISIBLE);
             } else {
-                addSearchBarAppWidgetView();
+                refreshSearchWidgetView();
             }
         }
 
@@ -261,60 +261,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
         mScrimViews.prepareEnterRecentsAnimation();
     }
 
-    /** Attempts to allocate and bind the search bar app widget */
-    void bindSearchBarAppWidget() {
-        if (Constants.DebugFlags.App.EnableSearchLayout) {
-            SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
-
-            // Reset the host view and widget info
-            mSearchAppWidgetHostView = null;
-            mSearchAppWidgetInfo = null;
-
-            // Try and load the app widget id from the settings
-            int appWidgetId = mConfig.searchBarAppWidgetId;
-            if (appWidgetId >= 0) {
-                mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId);
-                if (mSearchAppWidgetInfo == null) {
-                    // If there is no actual widget associated with that id, then delete it and
-                    // prepare to bind another app widget in its place
-                    ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
-                    appWidgetId = -1;
-                }
-            }
-
-            // If there is no id, then bind a new search app widget
-            if (appWidgetId < 0) {
-                Pair<Integer, AppWidgetProviderInfo> widgetInfo =
-                        ssp.bindSearchAppWidget(mAppWidgetHost);
-                if (widgetInfo != null) {
-                    // Save the app widget id into the settings
-                    mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first);
-                    mSearchAppWidgetInfo = widgetInfo.second;
-                }
-            }
-        }
-    }
-
-    /** Creates the search bar app widget view */
-    void addSearchBarAppWidgetView() {
-        if (Constants.DebugFlags.App.EnableSearchLayout) {
-            int appWidgetId = mConfig.searchBarAppWidgetId;
-            if (appWidgetId >= 0) {
-                mSearchAppWidgetHostView = (RecentsAppWidgetHostView) mAppWidgetHost.createView(
-                        this, appWidgetId, mSearchAppWidgetInfo);
-                Bundle opts = new Bundle();
-                opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
-                        AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
-                mSearchAppWidgetHostView.updateAppWidgetOptions(opts);
-                // Set the padding to 0 for this search widget
-                mSearchAppWidgetHostView.setPadding(0, 0, 0, 0);
-                mRecentsView.setSearchBar(mSearchAppWidgetHostView);
-            } else {
-                mRecentsView.setSearchBar(null);
-            }
-        }
-    }
-
     /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
     boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
         SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
@@ -393,7 +339,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
         inflateDebugOverlay();
 
         // Bind the search app widget when we first start up
-        bindSearchBarAppWidget();
+        mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
 
         // Register the broadcast receiver to handle messages when the screen is turned off
         IntentFilter filter = new IntentFilter();
@@ -498,7 +444,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
         ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
         ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
         mRecentsView.startEnterRecentsAnimation(ctx);
-        if (mConfig.searchBarAppWidgetId >= 0) {
+
+        if (mSearchWidgetInfo != null) {
             final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> cbRef =
                     new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>(
                             RecentsActivity.this);
@@ -654,9 +601,22 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
     /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/
 
     @Override
-    public void refreshSearchWidget() {
-        bindSearchBarAppWidget();
-        addSearchBarAppWidgetView();
+    public void refreshSearchWidgetView() {
+        if (mSearchWidgetInfo != null) {
+            SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+            int searchWidgetId = ssp.getSearchAppWidgetId(this);
+            mSearchWidgetHostView = (RecentsAppWidgetHostView) mAppWidgetHost.createView(
+                    this, searchWidgetId, mSearchWidgetInfo);
+            Bundle opts = new Bundle();
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+                    AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
+            mSearchWidgetHostView.updateAppWidgetOptions(opts);
+            // Set the padding to 0 for this search widget
+            mSearchWidgetHostView.setPadding(0, 0, 0, 0);
+            mRecentsView.setSearchBar(mSearchWidgetHostView);
+        } else {
+            mRecentsView.setSearchBar(null);
+        }
     }
 
     /**** DebugOverlayView.DebugOverlayViewCallbacks ****/
index d4e50f8..0102332 100644 (file)
@@ -20,26 +20,20 @@ import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoader;
 
 /** Our special app widget host for the Search widget */
 public class RecentsAppWidgetHost extends AppWidgetHost {
 
     /* Callbacks to notify when an app package changes */
     interface RecentsAppWidgetHostCallbacks {
-        public void refreshSearchWidget();
+        void refreshSearchWidgetView();
     }
 
-    Context mContext;
     RecentsAppWidgetHostCallbacks mCb;
-    RecentsConfiguration mConfig;
     boolean mIsListening;
 
     public RecentsAppWidgetHost(Context context, int hostId) {
         super(context, hostId);
-        mContext = context;
-        mConfig = RecentsConfiguration.getInstance();
     }
 
     public void startListening(RecentsAppWidgetHostCallbacks cb) {
@@ -57,7 +51,6 @@ public class RecentsAppWidgetHost extends AppWidgetHost {
         }
         // Ensure that we release any references to the callbacks
         mCb = null;
-        mContext = null;
         mIsListening = false;
     }
 
@@ -67,18 +60,14 @@ public class RecentsAppWidgetHost extends AppWidgetHost {
         return new RecentsAppWidgetHostView(context);
     }
 
+    /**
+     * Note: this is only called for packages that have updated, not removed.
+     */
     @Override
     protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
-        if (mCb == null) return;
-        if (mContext == null) return;
-
-        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
-        if (appWidgetId > -1 && appWidgetId == mConfig.searchBarAppWidgetId) {
-            // The search provider may have changed, so just delete the old widget and bind it again
-            ssp.unbindSearchAppWidget(this, appWidgetId);
-            // Update the search widget
-            mConfig.updateSearchBarAppWidgetId(mContext, -1);
-            mCb.refreshSearchWidget();
+        super.onProviderChanged(appWidgetId, appWidgetInfo);
+        if (mIsListening && mCb != null) {
+            mCb.refreshSearchWidgetView();
         }
     }
 }
index 1ed755a..672d602 100644 (file)
@@ -18,6 +18,7 @@ package com.android.systemui.recents;
 
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
+import android.view.View;
 import android.widget.RemoteViews;
 
 public class RecentsAppWidgetHostView extends AppWidgetHostView {
@@ -37,6 +38,14 @@ public class RecentsAppWidgetHostView extends AppWidgetHostView {
         super.updateAppWidget(remoteViews);
     }
 
+    @Override
+    protected View getErrorView() {
+        // Just return an empty view as the error view when failing to inflate the Recents search
+        // bar widget (this is mainly to catch the case where we try and inflate the widget view
+        // while the search provider is updating)
+        return new View(mContext);
+    }
+
     /**
      * Updates the last orientation that this widget was inflated.
      */
index 244fada..dfe7e96 100644 (file)
@@ -73,7 +73,6 @@ public class RecentsConfiguration {
     public int maxNumTasksToLoad;
 
     /** Search bar */
-    int searchBarAppWidgetId = -1;
     public int searchBarSpaceHeightPx;
 
     /** Task stack */
@@ -207,8 +206,6 @@ public class RecentsConfiguration {
 
         // Search Bar
         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
-        searchBarAppWidgetId = Prefs.getInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID,
-                -1 /* defaultValue */);
 
         // Task stack
         taskStackScrollDuration =
@@ -279,12 +276,6 @@ public class RecentsConfiguration {
         systemInsets.set(insets);
     }
 
-    /** Updates the search bar app widget */
-    public void updateSearchBarAppWidgetId(Context context, int appWidgetId) {
-        searchBarAppWidgetId = appWidgetId;
-        Prefs.putInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, appWidgetId);
-    }
-
     /** Updates the states that need to be re-read whenever we re-initialize. */
     void updateOnReinitialize(Context context, SystemServicesProxy ssp) {
         // Check if the developer options are enabled
@@ -304,11 +295,6 @@ public class RecentsConfiguration {
         launchedHasConfigurationChanged = true;
     }
 
-    /** Returns whether the search bar app widget exists. */
-    public boolean hasSearchBarAppWidget() {
-        return searchBarAppWidgetId >= 0;
-    }
-
     /** Returns whether the status bar scrim should be animated when shown for the first time. */
     public boolean shouldAnimateStatusBarScrim() {
         return launchedFromHome;
@@ -335,9 +321,7 @@ public class RecentsConfiguration {
      * the system insets.
      */
     public void getAvailableTaskStackBounds(int windowWidth, int windowHeight, int topInset,
-            int rightInset, Rect taskStackBounds) {
-        Rect searchBarBounds = new Rect();
-        getSearchBarBounds(windowWidth, windowHeight, topInset, searchBarBounds);
+            int rightInset, Rect searchBarBounds, Rect taskStackBounds) {
         if (isLandscape && hasTransposedSearchBar) {
             // In landscape, the search bar appears on the left, but we overlay it on top
             taskStackBounds.set(0, topInset, windowWidth - rightInset, windowHeight);
@@ -355,10 +339,6 @@ public class RecentsConfiguration {
             Rect searchBarSpaceBounds) {
         // Return empty rects if search is not enabled
         int searchBarSize = searchBarSpaceHeightPx;
-        if (!Constants.DebugFlags.App.EnableSearchLayout || !hasSearchBarAppWidget()) {
-            searchBarSize = 0;
-        }
-
         if (isLandscape && hasTransposedSearchBar) {
             // In landscape, the search bar appears on the left
             searchBarSpaceBounds.set(0, topInset, searchBarSize, windowHeight);
index 272d39a..b60c66f 100644 (file)
@@ -62,9 +62,11 @@ import android.view.DisplayInfo;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
+import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsAppWidgetHost;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -527,14 +529,57 @@ public class SystemServicesProxy {
     }
 
     /**
-     * Resolves and returns the first Recents widget from the same package as the global
-     * assist activity.
+     * Returns the current search widget id.
      */
-    public AppWidgetProviderInfo resolveSearchAppWidget() {
-        if (mAwm == null) return null;
-        if (mAssistComponent == null) return null;
+    public int getSearchAppWidgetId(Context context) {
+        return Prefs.getInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, -1);
+    }
+
+    /**
+     * Returns the current search widget info, binding a new one if necessary.
+     */
+    public AppWidgetProviderInfo getOrBindSearchAppWidget(Context context, AppWidgetHost host) {
+        int searchWidgetId = Prefs.getInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, -1);
+        AppWidgetProviderInfo searchWidgetInfo = mAwm.getAppWidgetInfo(searchWidgetId);
+        AppWidgetProviderInfo resolvedSearchWidgetInfo = resolveSearchAppWidget();
+
+        // Return the search widget info if it hasn't changed
+        if (searchWidgetInfo != null && resolvedSearchWidgetInfo != null &&
+                searchWidgetInfo.provider.equals(resolvedSearchWidgetInfo.provider)) {
+            if (Prefs.getString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null) == null) {
+                Prefs.putString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE,
+                        searchWidgetInfo.provider.getPackageName());
+            }
+            return searchWidgetInfo;
+        }
+
+        // Delete the old widget
+        if (searchWidgetId != -1) {
+            host.deleteAppWidgetId(searchWidgetId);
+        }
+
+        // And rebind a new search widget
+        if (resolvedSearchWidgetInfo != null) {
+            Pair<Integer, AppWidgetProviderInfo> widgetInfo = bindSearchAppWidget(host,
+                    resolvedSearchWidgetInfo);
+            if (widgetInfo != null) {
+                Prefs.putInt(context, Prefs.Key.SEARCH_APP_WIDGET_ID, widgetInfo.first);
+                Prefs.putString(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE,
+                        widgetInfo.second.provider.getPackageName());
+                return widgetInfo.second;
+            }
+        }
+
+        // If we fall through here, then there is no resolved search widget, so clear the state
+        Prefs.remove(context, Prefs.Key.SEARCH_APP_WIDGET_ID);
+        Prefs.remove(context, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE);
+        return null;
+    }
 
-        // Find the first Recents widget from the same package as the global assist activity
+    /**
+     * Returns the first Recents widget from the same package as the global assist activity.
+     */
+    private AppWidgetProviderInfo resolveSearchAppWidget() {
         List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders(
                 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
         for (AppWidgetProviderInfo info : widgets) {
@@ -548,45 +593,21 @@ public class SystemServicesProxy {
     /**
      * Resolves and binds the search app widget that is to appear in the recents.
      */
-    public Pair<Integer, AppWidgetProviderInfo> bindSearchAppWidget(AppWidgetHost host) {
+    private Pair<Integer, AppWidgetProviderInfo> bindSearchAppWidget(AppWidgetHost host,
+            AppWidgetProviderInfo resolvedSearchWidgetInfo) {
         if (mAwm == null) return null;
         if (mAssistComponent == null) return null;
 
-        // Find the first Recents widget from the same package as the global assist activity
-        AppWidgetProviderInfo searchWidgetInfo = resolveSearchAppWidget();
-
-        // Return early if there is no search widget
-        if (searchWidgetInfo == null) return null;
-
         // Allocate a new widget id and try and bind the app widget (if that fails, then just skip)
         int searchWidgetId = host.allocateAppWidgetId();
         Bundle opts = new Bundle();
         opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
                 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
-        if (!mAwm.bindAppWidgetIdIfAllowed(searchWidgetId, searchWidgetInfo.provider, opts)) {
+        if (!mAwm.bindAppWidgetIdIfAllowed(searchWidgetId, resolvedSearchWidgetInfo.provider, opts)) {
             host.deleteAppWidgetId(searchWidgetId);
             return null;
         }
-        return new Pair<Integer, AppWidgetProviderInfo>(searchWidgetId, searchWidgetInfo);
-    }
-
-    /**
-     * Returns the app widget info for the specified app widget id.
-     */
-    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
-        if (mAwm == null) return null;
-
-        return mAwm.getAppWidgetInfo(appWidgetId);
-    }
-
-    /**
-     * Destroys the specified app widget.
-     */
-    public void unbindSearchAppWidget(AppWidgetHost host, int appWidgetId) {
-        if (mAwm == null) return;
-
-        // Delete the app widget
-        host.deleteAppWidgetId(appWidgetId);
+        return new Pair<>(searchWidgetId, resolvedSearchWidgetInfo);
     }
 
     /**
index fa97a86..6cb11b1 100644 (file)
@@ -287,17 +287,14 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
 
     /** Adds the search bar */
     public void setSearchBar(RecentsAppWidgetHostView searchBar) {
-        // Create the search bar (and hide it if we have no recent tasks)
-        if (Constants.DebugFlags.App.EnableSearchLayout) {
-            // Remove the previous search bar if one exists
-            if (mSearchBar != null && indexOfChild(mSearchBar) > -1) {
-                removeView(mSearchBar);
-            }
-            // Add the new search bar
-            if (searchBar != null) {
-                mSearchBar = searchBar;
-                addView(mSearchBar);
-            }
+        // Remove the previous search bar if one exists
+        if (mSearchBar != null && indexOfChild(mSearchBar) > -1) {
+            removeView(mSearchBar);
+        }
+        // Add the new search bar
+        if (searchBar != null) {
+            mSearchBar = searchBar;
+            addView(mSearchBar);
         }
     }
 
@@ -324,8 +321,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
         int height = MeasureSpec.getSize(heightMeasureSpec);
 
         // Get the search bar bounds and measure the search bar layout
+        Rect searchBarSpaceBounds = new Rect();
         if (mSearchBar != null) {
-            Rect searchBarSpaceBounds = new Rect();
             mConfig.getSearchBarBounds(width, height, mConfig.systemInsets.top, searchBarSpaceBounds);
             mSearchBar.measure(
                     MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
@@ -334,7 +331,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
 
         Rect taskStackBounds = new Rect();
         mConfig.getAvailableTaskStackBounds(width, height, mConfig.systemInsets.top,
-                mConfig.systemInsets.right, taskStackBounds);
+                mConfig.systemInsets.right, searchBarSpaceBounds, taskStackBounds);
 
         // Measure each TaskStackView with the full width and height of the window since the
         // transition view is a child of that stack view
index 8d20772..3e66907 100644 (file)
@@ -303,6 +303,7 @@ public abstract class BaseStatusBar extends SystemUI implements
                             try {
                                 ActivityManagerNative.getDefault()
                                         .keyguardWaitingForActivityDrawn();
+                                ActivityManagerNative.getDefault().resumeAppSwitches();
                             } catch (RemoteException e) {
                             }
                         }
@@ -315,7 +316,9 @@ public abstract class BaseStatusBar extends SystemUI implements
                             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
                                     true /* force */);
                             visibilityChanged(false);
+                            mAssistManager.hideAssist();
                         }
+
                         // Wait for activity start.
                         return handled;
                     }
index f5fdf48..6bcb766 100644 (file)
@@ -213,7 +213,8 @@ public class LockIcon extends KeyguardAffordanceView {
             return R.drawable.lockscreen_fingerprint_fp_to_error_state_animation;
         } else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
             return R.drawable.lockscreen_fingerprint_error_state_to_fp_animation;
-        } else if (oldState == STATE_FINGERPRINT && newState == STATE_LOCK_OPEN) {
+        } else if (oldState == STATE_FINGERPRINT && newState == STATE_LOCK_OPEN
+                && !mUnlockMethodCache.isCurrentlyInsecure()) {
             return R.drawable.lockscreen_fingerprint_draw_off_animation;
         } else if (newState == STATE_FINGERPRINT && !oldScreenOn && screenOn) {
             return R.drawable.lockscreen_fingerprint_draw_on_animation;
@@ -225,14 +226,14 @@ public class LockIcon extends KeyguardAffordanceView {
     private int getState() {
         boolean fingerprintRunning =
                 KeyguardUpdateMonitor.getInstance(mContext).isFingerprintDetectionRunning();
-        if (mTransientFpError) {
+        if (mUnlockMethodCache.isCurrentlyInsecure()) {
+            return STATE_LOCK_OPEN;
+        } else if (mTransientFpError) {
             return STATE_FINGERPRINT_ERROR;
         } else if (fingerprintRunning) {
             return STATE_FINGERPRINT;
         } else if (mUnlockMethodCache.isFaceUnlockRunning()) {
             return STATE_FACE_UNLOCK;
-        } else if (mUnlockMethodCache.isCurrentlyInsecure()) {
-            return STATE_LOCK_OPEN;
         } else {
             return STATE_LOCKED;
         }
index 7f87485..3678cf1 100644 (file)
@@ -106,6 +106,7 @@ import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.SwipeHelper;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.doze.DozeLog;
@@ -633,7 +634,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
 
         mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
                 R.layout.super_status_bar, null);
-        mStatusBarWindow.mService = this;
+        mStatusBarWindow.setService(this);
         mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
             @Override
             public boolean onTouch(View v, MotionEvent event) {
@@ -676,8 +677,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
             mNotificationPanelDebugText.setVisibility(View.VISIBLE);
         }
 
-        updateShowSearchHoldoff();
-
         try {
             boolean showNav = mWindowManagerService.hasNavigationBar();
             if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
@@ -1013,11 +1012,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         return mStatusBarWindow;
     }
 
-    public void invokeAssistGesture(boolean vibrate) {
-        mHandler.removeCallbacks(mInvokeAssist);
-        mAssistManager.onGestureInvoked(vibrate);
-    }
-
     public int getStatusBarHeight() {
         if (mNaturalBarHeight < 0) {
             final Resources res = mContext.getResources();
@@ -1044,31 +1038,28 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         }
     };
 
-    private int mShowSearchHoldoff = 0;
-    private Runnable mInvokeAssist = new Runnable() {
-        public void run() {
+    private final View.OnLongClickListener mLongPressHomeListener
+            = new View.OnLongClickListener() {
+        @Override
+        public boolean onLongClick(View v) {
+            if (shouldDisableNavbarGestures()) {
+                return false;
+            }
             mAssistManager.prepareBeforeInvocation();
-            invokeAssistGesture(true /* vibrate */);
+            mAssistManager.onGestureInvoked();
             awakenDreams();
             if (mNavigationBarView != null) {
                 mNavigationBarView.abortCurrentGesture();
             }
+            return true;
         }
     };
 
-    View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
+    private final View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
         public boolean onTouch(View v, MotionEvent event) {
             switch (event.getAction()) {
-                case MotionEvent.ACTION_DOWN:
-                    if (!shouldDisableNavbarGestures()) {
-                        mHandler.removeCallbacks(mInvokeAssist);
-                        mHandler.postDelayed(mInvokeAssist, mShowSearchHoldoff);
-                    }
-                    break;
-
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
-                    mHandler.removeCallbacks(mInvokeAssist);
                     awakenDreams();
                     break;
             }
@@ -1096,6 +1087,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         mNavigationBarView.getBackButton().setLongClickable(true);
         mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
         mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
+        mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener);
         mAssistManager.onConfigurationChanged();
     }
 
@@ -1236,10 +1228,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         }
     }
 
-    private void updateShowSearchHoldoff() {
-        mShowSearchHoldoff = ViewConfiguration.getLongPressTimeout();
-    }
-
     private void updateNotificationShade() {
         if (mStackScroller == null) return;
 
@@ -2922,7 +2910,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
 
         updateResources();
         repositionNavigationBar();
-        updateShowSearchHoldoff();
         updateRowStates();
         mIconController.updateResources();
         mScreenPinningRequest.onConfigurationChanged();
@@ -2944,6 +2931,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
         if (mZenModeController != null) {
             mZenModeController.setUserId(mCurrentUserId);
         }
+        if (mSecurityController != null) {
+            mSecurityController.onUserSwitched(mCurrentUserId);
+        }
     }
 
     private void resetUserSetupObserver() {
index 634270c..0e22aa8 100644 (file)
@@ -53,7 +53,7 @@ public class StatusBarWindowView extends FrameLayout {
 
     private int mRightInset = 0;
 
-    PhoneStatusBar mService;
+    private PhoneStatusBar mService;
     private final Paint mTransparentSrcPaint = new Paint();
 
     public StatusBarWindowView(Context context, AttributeSet attrs) {
@@ -124,14 +124,22 @@ public class StatusBarWindowView extends FrameLayout {
     }
 
     @Override
-    protected void onAttachedToWindow () {
-        super.onAttachedToWindow();
-
+    protected void onFinishInflate() {
+        super.onFinishInflate();
         mStackScrollLayout = (NotificationStackScrollLayout) findViewById(
                 R.id.notification_stack_scroller);
         mNotificationPanel = (NotificationPanelView) findViewById(R.id.notification_panel);
-        mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService);
         mBrightnessMirror = findViewById(R.id.brightness_mirror);
+    }
+
+    public void setService(PhoneStatusBar service) {
+        mService = service;
+        mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService);
+    }
+
+    @Override
+    protected void onAttachedToWindow () {
+        super.onAttachedToWindow();
 
         // We really need to be able to animate while window animations are going on
         // so that activities may be started asynchronously from panel animations
index 6bc51fa..4c99792 100644 (file)
@@ -57,7 +57,7 @@ public class KeyButtonView extends ImageView {
                 if (isLongClickable()) {
                     // Just an old-fashioned ImageView
                     performLongClick();
-                } else {
+                } else if (mSupportsLongpress) {
                     sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
                     sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
                 }
@@ -92,7 +92,7 @@ public class KeyButtonView extends ImageView {
         super.onInitializeAccessibilityNodeInfo(info);
         if (mCode != 0) {
             info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, null));
-            if (mSupportsLongpress) {
+            if (mSupportsLongpress || isLongClickable()) {
                 info.addAction(
                         new AccessibilityNodeInfo.AccessibilityAction(ACTION_LONG_CLICK, null));
             }
@@ -115,7 +115,7 @@ public class KeyButtonView extends ImageView {
             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
             playSoundEffect(SoundEffectConstants.CLICK);
             return true;
-        } else if (action == ACTION_LONG_CLICK && mCode != 0 && mSupportsLongpress) {
+        } else if (action == ACTION_LONG_CLICK && mCode != 0) {
             sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
             sendEvent(KeyEvent.ACTION_UP, 0);
             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
@@ -144,10 +144,8 @@ public class KeyButtonView extends ImageView {
                     // Provide the same haptic feedback that the system offers for virtual keys.
                     performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
                 }
-                if (mSupportsLongpress) {
-                    removeCallbacks(mCheckLongPress);
-                    postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
-                }
+                removeCallbacks(mCheckLongPress);
+                postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
                 break;
             case MotionEvent.ACTION_MOVE:
                 x = (int)ev.getX();
@@ -162,9 +160,7 @@ public class KeyButtonView extends ImageView {
                 if (mCode != 0) {
                     sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
                 }
-                if (mSupportsLongpress) {
-                    removeCallbacks(mCheckLongPress);
-                }
+                removeCallbacks(mCheckLongPress);
                 break;
             case MotionEvent.ACTION_UP:
                 final boolean doIt = isPressed();
@@ -183,9 +179,7 @@ public class KeyButtonView extends ImageView {
                         performClick();
                     }
                 }
-                if (mSupportsLongpress) {
-                    removeCallbacks(mCheckLongPress);
-                }
+                removeCallbacks(mCheckLongPress);
                 break;
         }
 
index 962000a..b505d9d 100644 (file)
@@ -64,6 +64,7 @@ public class SecurityControllerImpl implements SecurityController {
 
     private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>();
     private int mCurrentUserId;
+    private int mVpnUserId;
 
     public SecurityControllerImpl(Context context) {
         mContext = context;
@@ -78,7 +79,7 @@ public class SecurityControllerImpl implements SecurityController {
 
         // TODO: re-register network callback on user change.
         mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
-        mCurrentUserId = ActivityManager.getCurrentUser();
+        onUserSwitched(ActivityManager.getCurrentUser());
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -123,9 +124,9 @@ public class SecurityControllerImpl implements SecurityController {
 
     @Override
     public String getPrimaryVpnName() {
-        VpnConfig cfg = mCurrentVpns.get(mCurrentUserId);
+        VpnConfig cfg = mCurrentVpns.get(mVpnUserId);
         if (cfg != null) {
-            return getNameForVpnConfig(cfg, new UserHandle(mCurrentUserId));
+            return getNameForVpnConfig(cfg, new UserHandle(mVpnUserId));
         } else {
             return null;
         }
@@ -133,8 +134,8 @@ public class SecurityControllerImpl implements SecurityController {
 
     @Override
     public String getProfileVpnName() {
-        for (UserInfo profile : mUserManager.getProfiles(mCurrentUserId)) {
-            if (profile.id == mCurrentUserId) {
+        for (UserInfo profile : mUserManager.getProfiles(mVpnUserId)) {
+            if (profile.id == mVpnUserId) {
                 continue;
             }
             VpnConfig cfg = mCurrentVpns.get(profile.id);
@@ -147,7 +148,7 @@ public class SecurityControllerImpl implements SecurityController {
 
     @Override
     public boolean isVpnEnabled() {
-        for (UserInfo profile : mUserManager.getProfiles(mCurrentUserId)) {
+        for (UserInfo profile : mUserManager.getProfiles(mVpnUserId)) {
             if (mCurrentVpns.get(profile.id) != null) {
                 return true;
             }
@@ -172,6 +173,12 @@ public class SecurityControllerImpl implements SecurityController {
     @Override
     public void onUserSwitched(int newUserId) {
         mCurrentUserId = newUserId;
+        if (mUserManager.getUserInfo(newUserId).isRestricted()) {
+            // VPN for a restricted profile is routed through its owner user
+            mVpnUserId = UserHandle.USER_OWNER;
+        } else {
+            mVpnUserId = mCurrentUserId;
+        }
         fireCallbacks();
     }
 
index 5700732..f98840b 100644 (file)
@@ -1898,15 +1898,23 @@ public class NotificationStackScrollLayout extends ViewGroup
             boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
             if (!mIsExpanded && !isHeadsUp) {
                 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
-            } else if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
-                if (pinnedAndClosed || shouldHunAppearFromBottom(row)) {
-                    // Our custom add animation
-                    type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
-                } else {
-                    // Normal add animation
-                    type = AnimationEvent.ANIMATION_TYPE_ADD;
+            } else {
+                StackViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
+                if (viewState == null) {
+                    // A view state was never generated for this view, so we don't need to animate
+                    // this. This may happen with notification children.
+                    continue;
+                }
+                if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
+                    if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
+                        // Our custom add animation
+                        type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
+                    } else {
+                        // Normal add animation
+                        type = AnimationEvent.ANIMATION_TYPE_ADD;
+                    }
+                    onBottom = !pinnedAndClosed;
                 }
-                onBottom = !pinnedAndClosed;
             }
             AnimationEvent event = new AnimationEvent(row, type);
             event.headsUpFromBottom = onBottom;
@@ -1916,8 +1924,7 @@ public class NotificationStackScrollLayout extends ViewGroup
         mAddedHeadsUpChildren.clear();
     }
 
-    private boolean shouldHunAppearFromBottom(ExpandableNotificationRow row) {
-        StackViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
+    private boolean shouldHunAppearFromBottom(StackViewState viewState) {
         if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
             return false;
         }
index 17e6e3d..5b8fe89 100644 (file)
@@ -893,13 +893,12 @@ public class StackStateAnimator {
                 if (mHostLayout.indexOfChild(changingView) == -1) {
                     // This notification was actually removed, so we need to add it to the overlay
                     mHostLayout.getOverlay().add(changingView);
-                    ViewState viewState = new ViewState();
-                    viewState.initFrom(changingView);
-                    viewState.yTranslation = -changingView.getActualHeight();
+                    mTmpState.initFrom(changingView);
+                    mTmpState.yTranslation = -changingView.getActualHeight();
                     // We temporarily enable Y animations, the real filter will be combined
                     // afterwards anyway
                     mAnimationFilter.animateY = true;
-                    startViewAnimations(changingView, viewState, 0,
+                    startViewAnimations(changingView, mTmpState, 0,
                             ANIMATION_DURATION_HEADS_UP_DISAPPEAR);
                     mChildrenToClearFromOverlay.add(changingView);
                 }
index 839b87a..d58d372 100644 (file)
@@ -1611,8 +1611,7 @@ class AlarmManagerService extends SystemService {
         if (mAlarmBatches.size() > 0) {
             final Batch firstWakeup = findFirstWakeupBatchLocked();
             final Batch firstBatch = mAlarmBatches.get(0);
-            // always update the kernel alarms, as a backstop against missed wakeups
-            if (firstWakeup != null) {
+            if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
                 mNextWakeup = firstWakeup.start;
                 setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
             }
@@ -1625,8 +1624,7 @@ class AlarmManagerService extends SystemService {
                 nextNonWakeup = mNextNonWakeupDeliveryTime;
             }
         }
-        // always update the kernel alarm, as a backstop against missed wakeups
-        if (nextNonWakeup != 0) {
+        if (nextNonWakeup != 0 && mNextNonWakeup != nextNonWakeup) {
             mNextNonWakeup = nextNonWakeup;
             setLocked(ELAPSED_REALTIME, nextNonWakeup);
         }
index 2d5141e..47971a1 100644 (file)
@@ -93,6 +93,9 @@ import android.security.Credentials;
 import android.security.KeyStore;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.LocalLog.ReadOnlyLocalLog;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -140,6 +143,7 @@ import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -415,6 +419,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
     // sequence number of NetworkRequests
     private int mNextNetworkRequestId = 1;
 
+    // Array of <Network,ReadOnlyLocalLogs> tracking network validation and results
+    private static final int MAX_VALIDATION_LOGS = 10;
+    private final ArrayDeque<Pair<Network,ReadOnlyLocalLog>> mValidationLogs =
+            new ArrayDeque<Pair<Network,ReadOnlyLocalLog>>(MAX_VALIDATION_LOGS);
+
+    private void addValidationLogs(ReadOnlyLocalLog log, Network network) {
+        synchronized(mValidationLogs) {
+            while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) {
+                mValidationLogs.removeLast();
+            }
+            mValidationLogs.addFirst(new Pair(network, log));
+        }
+    }
+
     /**
      * Implements support for the legacy "one network per network type" model.
      *
@@ -1716,11 +1734,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
         return ret;
     }
 
-    private boolean shouldPerformDiagnostics(String[] args) {
+    private boolean argsContain(String[] args, String target) {
         for (String arg : args) {
-            if (arg.equals("--diag")) {
-                return true;
-            }
+            if (arg.equals(target)) return true;
         }
         return false;
     }
@@ -1738,7 +1754,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
         }
 
         final List<NetworkDiagnostics> netDiags = new ArrayList<NetworkDiagnostics>();
-        if (shouldPerformDiagnostics(args)) {
+        if (argsContain(args, "--diag")) {
             final long DIAG_TIME_MS = 5000;
             for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
                 // Start gathering diagnostic information.
@@ -1825,6 +1841,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
             }
             pw.decreaseIndent();
         }
+
+        if (argsContain(args, "--short") == false) {
+            pw.println();
+            synchronized (mValidationLogs) {
+                pw.println("mValidationLogs (most recent first):");
+                for (Pair<Network,ReadOnlyLocalLog> p : mValidationLogs) {
+                    pw.println(p.first);
+                    pw.increaseIndent();
+                    p.second.dump(fd, pw, args);
+                    pw.decreaseIndent();
+                }
+            }
+        }
     }
 
     private boolean isLiveNetworkAgent(NetworkAgentInfo nai, String msg) {
@@ -3841,6 +3870,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
         synchronized (this) {
             nai.networkMonitor.systemReady = mSystemReady;
         }
+        addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network);
         if (DBG) log("registerNetworkAgent " + nai);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
         return nai.network.netId;
index f1d5aa3..1653db9 100644 (file)
 
 package com.android.server;
 
+import android.app.ProgressDialog;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.AsyncTask;
 import android.os.RecoverySystem;
+import android.os.storage.StorageManager;
 import android.util.Log;
 import android.util.Slog;
+import android.view.WindowManager;
+
+import com.android.internal.R;
 
 import java.io.IOException;
 
@@ -39,6 +45,8 @@ public class MasterClearReceiver extends BroadcastReceiver {
 
         final boolean shutdown = intent.getBooleanExtra("shutdown", false);
         final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
+        final boolean wipeExternalStorage = intent.getBooleanExtra(
+                Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
 
         Slog.w(TAG, "!!! FACTORY RESET !!!");
         // The reboot call is blocking, so we need to do it on another thread.
@@ -55,6 +63,48 @@ public class MasterClearReceiver extends BroadcastReceiver {
                 }
             }
         };
-        thr.start();
+
+        if (wipeExternalStorage) {
+            // thr will be started at the end of this task.
+            new WipeAdoptableDisksTask(context, thr).execute();
+        } else {
+            thr.start();
+        }
+    }
+
+    private class WipeAdoptableDisksTask extends AsyncTask<Void, Void, Void> {
+        private final Thread mChainedTask;
+        private final Context mContext;
+        private final ProgressDialog mProgressDialog;
+
+        public WipeAdoptableDisksTask(Context context, Thread chainedTask) {
+            mContext = context;
+            mChainedTask = chainedTask;
+            mProgressDialog = new ProgressDialog(context);
+        }
+
+        @Override
+        protected void onPreExecute() {
+            mProgressDialog.setIndeterminate(true);
+            mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+            mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));
+            mProgressDialog.show();
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            Slog.w(TAG, "Wiping adoptable disks");
+            StorageManager sm = (StorageManager) mContext.getSystemService(
+                    Context.STORAGE_SERVICE);
+            sm.wipeAdoptableDisks();
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            mProgressDialog.dismiss();
+            mChainedTask.start();
+        }
+
     }
 }
index 4fe8fb9..45a7767 100644 (file)
@@ -50,7 +50,9 @@ import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -84,6 +86,7 @@ import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
@@ -675,13 +678,15 @@ class MountService extends IMountService.Stub
     }
 
     private void handleSystemReady() {
-        resetIfReadyAndConnected();
+        synchronized (mLock) {
+            resetIfReadyAndConnectedLocked();
+        }
 
         // Start scheduling nominally-daily fstrim operations
         MountServiceIdler.scheduleIdlePass(mContext);
     }
 
-    private void resetIfReadyAndConnected() {
+    private void resetIfReadyAndConnectedLocked() {
         Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
                 + ", mDaemonConnected=" + mDaemonConnected);
         if (mSystemReady && mDaemonConnected) {
@@ -780,7 +785,9 @@ class MountService extends IMountService.Stub
     }
 
     private void handleDaemonConnected() {
-        resetIfReadyAndConnected();
+        synchronized (mLock) {
+            resetIfReadyAndConnectedLocked();
+        }
 
         /*
          * Now that we've done our initialization, release
@@ -1600,7 +1607,7 @@ class MountService extends IMountService.Stub
             // reset vold so we bind into new volume into place.
             if (Objects.equals(mPrimaryStorageUuid, fsUuid)) {
                 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
-                resetIfReadyAndConnected();
+                resetIfReadyAndConnectedLocked();
             }
 
             writeSettingsLocked();
@@ -1628,7 +1635,7 @@ class MountService extends IMountService.Stub
             }
 
             writeSettingsLocked();
-            resetIfReadyAndConnected();
+            resetIfReadyAndConnectedLocked();
         }
     }
 
@@ -1641,6 +1648,30 @@ class MountService extends IMountService.Stub
     }
 
     @Override
+    public void remountUid(int uid) {
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        waitForReady();
+
+        final int mountExternal = mPms.getMountExternalMode(uid);
+        final String mode;
+        if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
+            mode = "default";
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
+            mode = "read";
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
+            mode = "write";
+        } else {
+            mode = "none";
+        }
+
+        try {
+            mConnector.execute("volume", "remount_uid", uid, mode);
+        } catch (NativeDaemonConnectorException e) {
+            Slog.w(TAG, "Failed to remount UID " + uid + " as " + mode + ": " + e);
+        }
+    }
+
+    @Override
     public void setDebugFlags(int flags, int mask) {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         waitForReady();
@@ -1651,7 +1682,7 @@ class MountService extends IMountService.Stub
             }
 
             writeSettingsLocked();
-            resetIfReadyAndConnected();
+            resetIfReadyAndConnectedLocked();
         }
     }
 
@@ -1688,7 +1719,7 @@ class MountService extends IMountService.Stub
                 Slog.d(TAG, "Skipping move to/from primary physical");
                 onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED);
                 onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED);
-                resetIfReadyAndConnected();
+                resetIfReadyAndConnectedLocked();
 
             } else {
                 final VolumeInfo from = Preconditions.checkNotNull(
@@ -2022,7 +2053,7 @@ class MountService extends IMountService.Stub
 
     @Override
     public void finishMediaUpdate() {
-        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
             throw new SecurityException("no permission to call finishMediaUpdate()");
         }
         if (mUnmountSignal != null) {
index f031694..7204a6e 100644 (file)
@@ -18,7 +18,9 @@ package com.android.server.am;
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -3209,13 +3211,14 @@ public final class ActivityManagerService extends ActivityManagerNative
 
             int uid = app.uid;
             int[] gids = null;
-            int mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
+            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
             if (!app.isolated) {
                 int[] permGids = null;
                 try {
                     checkTime(startTime, "startProcess: getting gids from package manager");
-                    permGids = AppGlobals.getPackageManager().getPackageGids(app.info.packageName,
-                            app.userId);
+                    final IPackageManager pm = AppGlobals.getPackageManager();
+                    permGids = pm.getPackageGids(app.info.packageName, app.userId);
+                    mountExternal = pm.getMountExternalMode(uid);
                 } catch (RemoteException e) {
                     throw e.rethrowAsRuntimeException();
                 }
index f967aef..4ce5c7e 100644 (file)
@@ -25,7 +25,6 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
@@ -39,11 +38,13 @@ import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
 
+import android.Manifest;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
+import android.app.AppOpsManager;
 import android.app.IActivityContainer;
 import android.app.IActivityContainerCallback;
 import android.app.IActivityManager;
@@ -62,6 +63,7 @@ import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
@@ -90,9 +92,11 @@ import android.os.SystemClock;
 import android.os.TransactionTooLargeException;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.provider.MediaStore;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.voice.IVoiceInteractionSession;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Slog;
@@ -108,6 +112,7 @@ import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
 import com.android.server.am.ActivityStack.ActivityState;
@@ -170,6 +175,25 @@ public final class ActivityStackSupervisor implements DisplayListener {
 
     private static final String LOCK_TASK_TAG = "Lock-to-App";
 
+    // Activity actions an app cannot start if it uses a permission which is not granted.
+    private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
+            new ArrayMap<>();
+    static {
+        ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
+                Manifest.permission.CAMERA);
+        ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE,
+                Manifest.permission.CAMERA);
+        ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL,
+                Manifest.permission.CALL_PHONE);
+    }
+
+    /** Action not restricted for the calling package. */
+    private static final int ACTION_RESTRICTION_NONE = 0;
+    /** Action restricted for the calling package by not granting a used permission. */
+    private static final int ACTION_RESTRICTION_PERMISSION = 1;
+    /** Action restricted for the calling package by not allowing a used permission's app op. */
+    private static final int ACTION_RESTRICTION_APPOP = 2;
+
     /** Status Bar Service **/
     private IBinder mToken = new Binder();
     private IStatusBarService mStatusBarService;
@@ -1519,14 +1543,23 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 START_ANY_ACTIVITY, callingPid, callingUid);
         final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
                 callingUid, aInfo.applicationInfo.uid, aInfo.exported);
-        if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {
+        final int actionRestriction = getActionRestrictionForCallingPackage(
+                intent.getAction(), callingPackage, callingPid, callingUid);
+
+        if (startAnyPerm != PERMISSION_GRANTED && (componentPerm != PERMISSION_GRANTED
+                || actionRestriction == ACTION_RESTRICTION_PERMISSION)) {
             if (resultRecord != null) {
                 resultStack.sendActivityResultLocked(-1,
                     resultRecord, resultWho, requestCode,
                     Activity.RESULT_CANCELED, null);
             }
             String msg;
-            if (!aInfo.exported) {
+            if (actionRestriction == ACTION_RESTRICTION_PERMISSION) {
+                msg = "Permission Denial: starting " + intent.toString()
+                        + " from " + callerApp + " (pid=" + callingPid
+                        + ", uid=" + callingUid + ")" + " with revoked permission "
+                        + ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());
+            } else if (!aInfo.exported) {
                 msg = "Permission Denial: starting " + intent.toString()
                         + " from " + callerApp + " (pid=" + callingPid
                         + ", uid=" + callingUid + ")"
@@ -1541,7 +1574,19 @@ public final class ActivityStackSupervisor implements DisplayListener {
             throw new SecurityException(msg);
         }
 
-        boolean abort = !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
+        boolean abort = false;
+
+        if (startAnyPerm != PERMISSION_GRANTED
+                && actionRestriction == ACTION_RESTRICTION_APPOP) {
+            String msg = "Permission Denial: starting " + intent.toString()
+                    + " from " + callerApp + " (pid=" + callingPid
+                    + ", uid=" + callingUid + ")"
+                    + " requires " + aInfo.permission;
+            Slog.w(TAG, msg);
+            abort = true;
+        }
+
+        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
 
         if (mService.mController != null) {
@@ -1619,6 +1664,48 @@ public final class ActivityStackSupervisor implements DisplayListener {
         return err;
     }
 
+    private int getActionRestrictionForCallingPackage(String action,
+            String callingPackage, int callingPid, int callingUid) {
+        if (action == null) {
+            return ACTION_RESTRICTION_NONE;
+        }
+
+        String permission = ACTION_TO_RUNTIME_PERMISSION.get(action);
+        if (permission == null) {
+            return ACTION_RESTRICTION_NONE;
+        }
+
+        final PackageInfo packageInfo;
+        try {
+            packageInfo = mService.mContext.getPackageManager()
+                    .getPackageInfo(callingPackage, PackageManager.GET_PERMISSIONS);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.i(TAG, "Cannot find package info for " + callingPackage);
+            return ACTION_RESTRICTION_NONE;
+        }
+
+        if (!ArrayUtils.contains(packageInfo.requestedPermissions, permission)) {
+            return ACTION_RESTRICTION_NONE;
+        }
+
+        if (mService.checkPermission(permission, callingPid, callingUid) ==
+                PackageManager.PERMISSION_DENIED) {
+            return ACTION_RESTRICTION_PERMISSION;
+        }
+
+        final int opCode = AppOpsManager.permissionToOpCode(permission);
+        if (opCode == AppOpsManager.OP_NONE) {
+            return ACTION_RESTRICTION_NONE;
+        }
+
+        if (mService.mAppOpsService.noteOperation(opCode, callingUid,
+                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+            return ACTION_RESTRICTION_APPOP;
+        }
+
+        return ACTION_RESTRICTION_NONE;
+    }
+
     ActivityStack computeStackFocus(ActivityRecord r, boolean newTask) {
         final TaskRecord task = r.task;
 
index 7855763..6cc1b11 100644 (file)
@@ -864,8 +864,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
     }
 
     final class WakeupReasonThread extends Thread {
-        final int[] mIrqs = new int[32];
-        final String[] mReasons = new String[32];
+        final String[] mReason = new String[1];
 
         WakeupReasonThread() {
             super("BatteryStats_wakeupReason");
@@ -876,12 +875,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub
 
             try {
                 int num;
-                while ((num=nativeWaitWakeup(mIrqs, mReasons)) >= 0) {
+                while ((num = nativeWaitWakeup(mReason)) >= 0) {
                     synchronized (mStats) {
+                        // num will be either 0 or 1.
                         if (num > 0) {
-                            for (int i=0; i<num; i++) {
-                                mStats.noteWakeupReasonLocked(mReasons[i]);
-                            }
+                            mStats.noteWakeupReasonLocked(mReason[0]);
                         } else {
                             mStats.noteWakeupReasonLocked("unknown");
                         }
@@ -893,7 +891,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
         }
     }
 
-    private static native int nativeWaitWakeup(int[] outIrqs, String[] outReasons);
+    private static native int nativeWaitWakeup(String[] outReason);
 
     private void dumpHelp(PrintWriter pw) {
         pw.println("Battery stats (batterystats) dump options:");
index 99a0567..310e361 100644 (file)
@@ -47,6 +47,8 @@ import android.telephony.CellInfoGsm;
 import android.telephony.CellInfoLte;
 import android.telephony.CellInfoWcdma;
 import android.telephony.TelephonyManager;
+import android.util.LocalLog;
+import android.util.LocalLog.ReadOnlyLocalLog;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -232,6 +234,8 @@ public class NetworkMonitor extends StateMachine {
     private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
     private String mCaptivePortalLoggedInResponseToken = null;
 
+    private final LocalLog validationLogs = new LocalLog(20); // 20 lines
+
     public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
             NetworkRequest defaultRequest) {
         // Add suffix indicating which NetworkMonitor we're talking about.
@@ -272,6 +276,15 @@ public class NetworkMonitor extends StateMachine {
         Log.d(TAG + "/" + mNetworkAgentInfo.name(), s);
     }
 
+    private void validationLog(String s) {
+        if (DBG) log(s);
+        validationLogs.log(s);
+    }
+
+    public ReadOnlyLocalLog getValidationLogs() {
+        return validationLogs.readOnlyLocalLog();
+    }
+
     // DefaultState is the parent of all States.  It exists only to handle CMD_* messages but
     // does not entail any real state (hence no enter() or exit() routines).
     private class DefaultState extends State {
@@ -649,10 +662,8 @@ public class NetworkMonitor extends StateMachine {
                     fetchPac = true;
                 }
             }
-            if (DBG) {
-                log("Checking " + url.toString() + " on " +
-                        mNetworkAgentInfo.networkInfo.getExtraInfo());
-            }
+            validationLog("Checking " + url.toString() + " on " +
+                    mNetworkAgentInfo.networkInfo.getExtraInfo());
             urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
             urlConnection.setInstanceFollowRedirects(fetchPac);
             urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
@@ -668,10 +679,8 @@ public class NetworkMonitor extends StateMachine {
             long responseTimestamp = SystemClock.elapsedRealtime();
 
             httpResponseCode = urlConnection.getResponseCode();
-            if (DBG) {
-                log("isCaptivePortal: ret=" + httpResponseCode +
-                        " headers=" + urlConnection.getHeaderFields());
-            }
+            validationLog("isCaptivePortal: ret=" + httpResponseCode +
+                    " headers=" + urlConnection.getHeaderFields());
             // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
             // portal.  The only example of this seen so far was a captive portal.  For
             // the time being go with prior behavior of assuming it's not a captive
@@ -684,12 +693,12 @@ public class NetworkMonitor extends StateMachine {
             // sign-in to an empty page.  Probably the result of a broken transparent proxy.
             // See http://b/9972012.
             if (httpResponseCode == 200 && urlConnection.getContentLength() == 0) {
-                if (DBG) log("Empty 200 response interpreted as 204 response.");
+                validationLog("Empty 200 response interpreted as 204 response.");
                 httpResponseCode = 204;
             }
 
             if (httpResponseCode == 200 && fetchPac) {
-                if (DBG) log("PAC fetch 200 response interpreted as 204 response.");
+                validationLog("PAC fetch 200 response interpreted as 204 response.");
                 httpResponseCode = 204;
             }
 
@@ -697,7 +706,7 @@ public class NetworkMonitor extends StateMachine {
                     httpResponseCode != 204 /* isCaptivePortal */,
                     requestTimestamp, responseTimestamp);
         } catch (IOException e) {
-            if (DBG) log("Probably not a portal: exception " + e);
+            validationLog("Probably not a portal: exception " + e);
             if (httpResponseCode == 599) {
                 // TODO: Ping gateway and DNS server and log results.
             }
index c52a1c1..9ee3bc2 100644 (file)
@@ -30,9 +30,11 @@ import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.MessageQueue;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SELinux;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Slog;
 
@@ -70,6 +72,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
     private static final int MSG_USER_SWITCHING = 10;
     private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
 
+    private boolean mIsKeyguard; // true if the authentication client is keyguard
     private ClientMonitor mAuthClient = null;
     private ClientMonitor mEnrollClient = null;
     private ClientMonitor mRemoveClient = null;
@@ -78,6 +81,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
     private static final long MS_PER_SEC = 1000;
     private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
     private static final int MAX_FAILED_ATTEMPTS = 5;
+    private static final int FINGERPRINT_ACQUIRED_GOOD = 0;
 
     Handler mHandler = new Handler() {
         public void handleMessage(android.os.Message msg) {
@@ -97,6 +101,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
     private long mHalDeviceId;
     private int mFailedAttempts;
     private IFingerprintDaemon mDaemon;
+    private PowerManager mPowerManager;
 
     private final Runnable mLockoutReset = new Runnable() {
         @Override
@@ -109,6 +114,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
         super(context);
         mContext = context;
         mAppOps = context.getSystemService(AppOpsManager.class);
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
     }
 
     @Override
@@ -191,7 +197,11 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
                 removeClient(mAuthClient);
             }
         }
+    }
 
+    private void userActivity() {
+        long now = SystemClock.uptimeMillis();
+        mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
     }
 
     void handleUserSwitching(int userId) {
@@ -498,9 +508,10 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
          */
         private boolean sendAuthenticated(int fpId, int groupId) {
             boolean result = false;
+            boolean authenticated = fpId != 0;
             if (receiver != null) {
                 try {
-                    if (fpId == 0) {
+                    if (!authenticated) {
                         receiver.onAuthenticationFailed(mHalDeviceId);
                     } else {
                         Fingerprint fp = !restricted ?
@@ -522,6 +533,11 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
                 result |= true; // we have a valid fingerprint
                 mLockoutReset.run();
             }
+            // For fingerprint devices that support touch-to-wake, this will ensure the device
+            // wakes up and turns the screen on when fingerprint is authenticated.
+            if (mIsKeyguard && authenticated) {
+                mPowerManager.wakeUp(SystemClock.uptimeMillis());
+            }
             return result;
         }
 
@@ -537,6 +553,12 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
                 Slog.w(TAG, "Failed to invoke sendAcquired:", e);
                 return true; // client failed
             }
+            finally {
+                // Good scans will keep the device awake
+                if (acquiredInfo == FINGERPRINT_ACQUIRED_GOOD) {
+                    userActivity();
+                }
+            }
         }
 
         /*
@@ -589,6 +611,8 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
     };
 
     private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+        private static final String KEYGUARD_PACKAGE = "com.android.systemui";
+
         @Override // Binder call
         public long preEnroll(IBinder token) {
             checkPermission(MANAGE_FINGERPRINT);
@@ -638,7 +662,8 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
 
         @Override // Binder call
         public void authenticate(final IBinder token, final long opId, final int groupId,
-                final IFingerprintServiceReceiver receiver, final int flags, String opPackageName) {
+                final IFingerprintServiceReceiver receiver, final int flags,
+                final String opPackageName) {
 
             if (!canUseFingerprint(opPackageName)) {
                 return;
@@ -647,6 +672,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
+                    mIsKeyguard = KEYGUARD_PACKAGE.equals(opPackageName);
                     startAuthentication(token, opId, groupId, receiver, flags, restricted);
                 }
             });
index 3850306..45a4829 100644 (file)
@@ -281,8 +281,16 @@ public class GpsLocationProvider implements LocationProviderInterface {
     // current setting 24 hours
     private static final long NTP_INTERVAL = 24*60*60*1000;
     // how long to wait if we have a network error in NTP or XTRA downloading
+    // the initial value of the exponential backoff
     // current setting - 5 minutes
     private static final long RETRY_INTERVAL = 5*60*1000;
+    // how long to wait if we have a network error in NTP or XTRA downloading
+    // the max value of the exponential backoff
+    // current setting - 4 hours
+    private static final long MAX_RETRY_INTERVAL = 4*60*60*1000;
+
+    private BackOff mNtpBackOff = new BackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL);
+    private BackOff mXtraBackOff = new BackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL);
 
     // true if we are enabled, protected by this
     private boolean mEnabled;
@@ -832,9 +840,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
 
                     native_inject_time(time, timeReference, (int) certainty);
                     delay = NTP_INTERVAL;
+                    mNtpBackOff.reset();
                 } else {
                     if (DEBUG) Log.d(TAG, "requestTime failed");
-                    delay = RETRY_INTERVAL;
+                    delay = mNtpBackOff.nextBackoffMillis();
                 }
 
                 sendMessage(INJECT_NTP_TIME_FINISHED, 0, null);
@@ -875,6 +884,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
                         Log.d(TAG, "calling native_inject_xtra_data");
                     }
                     native_inject_xtra_data(data, data.length);
+                    mXtraBackOff.reset();
                 }
 
                 sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
@@ -882,7 +892,8 @@ public class GpsLocationProvider implements LocationProviderInterface {
                 if (data == null) {
                     // try again later
                     // since this is delayed and not urgent we do not hold a wake lock here
-                    mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA, RETRY_INTERVAL);
+                    mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
+                            mXtraBackOff.nextBackoffMillis());
                 }
 
                 // release wake lock held by task
@@ -2190,6 +2201,36 @@ public class GpsLocationProvider implements LocationProviderInterface {
         pw.append(s);
     }
 
+    /**
+     * A simple implementation of exponential backoff.
+     */
+    private static final class BackOff {
+        private static final int MULTIPLIER = 2;
+        private final long mInitIntervalMillis;
+        private final long mMaxIntervalMillis;
+        private long mCurrentIntervalMillis;
+
+        public BackOff(long initIntervalMillis, long maxIntervalMillis) {
+            mInitIntervalMillis = initIntervalMillis;
+            mMaxIntervalMillis = maxIntervalMillis;
+
+            mCurrentIntervalMillis = mInitIntervalMillis / MULTIPLIER;
+        }
+
+        public long nextBackoffMillis() {
+            if (mCurrentIntervalMillis > mMaxIntervalMillis) {
+                return mMaxIntervalMillis;
+            }
+
+            mCurrentIntervalMillis *= MULTIPLIER;
+            return mCurrentIntervalMillis;
+        }
+
+        public void reset() {
+            mCurrentIntervalMillis = mInitIntervalMillis / MULTIPLIER;
+        }
+    }
+
     // for GPS SV statistics
     private static final int MAX_SVS = 32;
     private static final int EPHEMERIS_MASK = 0;
index 847bcb5..d4b7256 100644 (file)
@@ -2093,8 +2093,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
         for (UserInfo user : users) {
             for (int i = mPowerSaveTempWhitelistAppIds.size() - 1; i >= 0; i--) {
                 int appId = mPowerSaveTempWhitelistAppIds.keyAt(i);
+                boolean isAllow = mPowerSaveTempWhitelistAppIds.valueAt(i);
                 int uid = UserHandle.getUid(user.id, appId);
                 updateRulesForUidLocked(uid);
+                setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, !isAllow);
             }
         }
     }
@@ -2190,11 +2192,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
         final boolean firewallReject = (uidRules & RULE_REJECT_ALL) != 0;
         if (oldFirewallReject != firewallReject) {
             setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, firewallReject);
-            if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE) && !firewallReject) {
-                // if the dozable chain is on, and we decide to allow this uid.  we need to punch
-                // a hole in the dozable chain.
-                setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, false);
-            }
         }
 
         // dispatch changed rule to existing listeners
index 4524ff8..d6a7bf9 100644 (file)
@@ -50,6 +50,7 @@ import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioAttributes;
@@ -72,6 +73,7 @@ import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.notification.Condition;
@@ -441,6 +443,12 @@ public class NotificationManagerService extends SystemService {
         return true;
     }
 
+    /** Use this to check if a package can post a notification or toast. */
+    private boolean checkNotificationOp(String pkg, int uid) {
+        return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
+                == AppOpsManager.MODE_ALLOWED;
+    }
+
     private static final class ToastRecord
     {
         final int pid;
@@ -1909,6 +1917,26 @@ public class NotificationManagerService extends SystemService {
                     r.dump(pw, "      ", getContext());
                 }
             }
+
+            try {
+                pw.println("\n  Banned Packages:");
+                for(UserInfo user : UserManager.get(getContext()).getUsers()) {
+                    final int userId = user.getUserHandle().getIdentifier();
+                    pw.println("    UserId " + userId);
+                    final PackageManager packageManager = getContext().getPackageManager();
+                    List<PackageInfo> packages = packageManager.getInstalledPackages(0, userId);
+                    final int packageCount = packages.size();
+                    for (int p = 0; p < packageCount; p++) {
+                        final String packageName = packages.get(p).packageName;
+                        final int uid = packageManager.getPackageUid(packageName, userId);
+                        if (!checkNotificationOp(packageName, uid)) {
+                            pw.println("       " + packageName);
+                        }
+                    }
+                }
+            } catch (NameNotFoundException e) {
+                // pass
+            }
         }
     }
 
index 3ea384c..c9555c4 100644 (file)
 package com.android.server.pm;
 
 import android.Manifest;
+import android.app.DownloadManager;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal.PackagesProvider;
 import android.content.pm.PackageParser;
+import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Build;
@@ -228,6 +230,7 @@ final class DefaultPermissionGrantPolicy {
             for (int i = 0; i < installerCount; i++) {
                 PackageParser.Package installPackage = installerPackages.get(i);
                 grantInstallPermissionsLPw(installPackage, INSTALLER_PERMISSIONS, userId);
+                grantRuntimePermissionsLPw(installPackage, STORAGE_PERMISSIONS, userId);
             }
 
             // Verifiers
@@ -239,6 +242,7 @@ final class DefaultPermissionGrantPolicy {
             for (int i = 0; i < verifierCount; i++) {
                 PackageParser.Package verifierPackage = verifierPackages.get(i);
                 grantInstallPermissionsLPw(verifierPackage, VERIFIER_PERMISSIONS, userId);
+                grantRuntimePermissionsLPw(verifierPackage, STORAGE_PERMISSIONS, userId);
             }
 
             // SetupWizard
@@ -273,6 +277,30 @@ final class DefaultPermissionGrantPolicy {
                     && doesPackageSupportRuntimePermissions(cameraPackage)) {
                 grantRuntimePermissionsLPw(cameraPackage, CAMERA_PERMISSIONS, userId);
                 grantRuntimePermissionsLPw(cameraPackage, MICROPHONE_PERMISSIONS, userId);
+                grantRuntimePermissionsLPw(cameraPackage, STORAGE_PERMISSIONS, userId);
+            }
+
+            // Media provider
+            PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackageLPr(
+                    MediaStore.AUTHORITY, userId);
+            if (mediaStorePackage != null) {
+                grantRuntimePermissionsLPw(mediaStorePackage, STORAGE_PERMISSIONS, userId);
+            }
+
+            // Downloads provider
+            PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackageLPr(
+                    "downloads", userId);
+            if (downloadsPackage != null) {
+                grantRuntimePermissionsLPw(downloadsPackage, STORAGE_PERMISSIONS, userId);
+            }
+
+            // Downloads UI
+            Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+            PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActvityPackageLPr(
+                    downloadsUiIntent, userId);
+            if (downloadsUiPackage != null
+                    && doesPackageSupportRuntimePermissions(downloadsUiPackage)) {
+                grantRuntimePermissionsLPw(downloadsUiPackage, STORAGE_PERMISSIONS, userId);
             }
 
             // Messaging
@@ -403,6 +431,8 @@ final class DefaultPermissionGrantPolicy {
                     }
                 }
             }
+
+            mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
         }
     }
 
@@ -452,6 +482,15 @@ final class DefaultPermissionGrantPolicy {
         return null;
     }
 
+    private PackageParser.Package getDefaultProviderAuthorityPackageLPr(
+            String authority, int userId) {
+        ProviderInfo provider = mService.resolveContentProvider(authority, 0, userId);
+        if (provider != null) {
+            return getSystemPackageLPr(provider.packageName);
+        }
+        return null;
+    }
+
     private PackageParser.Package getSystemPackageLPr(String packageName) {
         PackageParser.Package pkg = mService.mPackages.get(packageName);
         if (pkg != null && pkg.isSystemApp()) {
index be1afa8..43f80d4 100644 (file)
@@ -18,6 +18,7 @@ package com.android.server.pm;
 
 import static android.Manifest.permission.GRANT_REVOKE_PERMISSIONS;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -54,6 +55,7 @@ import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST;
 import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
 import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.PackageParser.isApkFile;
 import static android.os.Process.PACKAGE_INFO_GID;
 import static android.os.Process.SYSTEM_UID;
@@ -196,6 +198,7 @@ import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.os.IParcelFileDescriptorFactory;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -208,8 +211,8 @@ import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
-import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.PermissionsState.PermissionState;
+import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -2188,7 +2191,8 @@ public class PackageManagerService extends IPackageManager.Stub {
             // If this is the first boot, and it is a normal boot, then
             // we need to initialize the default preferred apps.
             if (!mRestoredSettings && !onlyCore) {
-                mSettings.readDefaultPreferredAppsLPw(this, 0);
+                mSettings.applyDefaultPreferredAppsLPw(this, UserHandle.USER_OWNER);
+                applyFactoryDefaultBrowserLPw(UserHandle.USER_OWNER);
             }
 
             // If this is first boot after an OTA, and a normal boot, then
@@ -2372,13 +2376,72 @@ public class PackageManagerService extends IPackageManager.Stub {
         if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "End priming domain verifications");
     }
 
+    private void applyFactoryDefaultBrowserLPw(int userId) {
+        // The default browser app's package name is stored in a string resource,
+        // with a product-specific overlay used for vendor customization.
+        String browserPkg = mContext.getResources().getString(
+                com.android.internal.R.string.default_browser);
+        if (browserPkg != null) {
+            // non-empty string => required to be a known package
+            PackageSetting ps = mSettings.mPackages.get(browserPkg);
+            if (ps == null) {
+                Slog.e(TAG, "Product default browser app does not exist: " + browserPkg);
+                browserPkg = null;
+            } else {
+                mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
+            }
+        }
+
+        // Nothing valid explicitly set? Make the factory-installed browser the explicit
+        // default.  If there's more than one, just leave everything alone.
+        if (browserPkg == null) {
+            calculateDefaultBrowserLPw(userId);
+        }
+    }
+
+    private void calculateDefaultBrowserLPw(int userId) {
+        List<String> allBrowsers = resolveAllBrowserApps(userId);
+        final String browserPkg = (allBrowsers.size() == 1) ? allBrowsers.get(0) : null;
+        mSettings.setDefaultBrowserPackageNameLPw(browserPkg, userId);
+    }
+
+    private List<String> resolveAllBrowserApps(int userId) {
+        // Match all generic http: browser apps
+        Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_VIEW);
+        intent.addCategory(Intent.CATEGORY_BROWSABLE);
+        intent.setData(Uri.parse("http:"));
+
+        // Resolve that intent and check that the handleAllWebDataURI boolean is set
+        List<ResolveInfo> list = queryIntentActivities(intent, null, 0, userId);
+
+        final int count = list.size();
+        List<String> result = new ArrayList<String>(count);
+        for (int i=0; i<count; i++) {
+            ResolveInfo info = list.get(i);
+            if (info.activityInfo == null
+                    || !info.handleAllWebDataURI
+                    || (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+                    || result.contains(info.activityInfo.packageName)) {
+                continue;
+            }
+            result.add(info.activityInfo.packageName);
+        }
+
+        return result;
+    }
+
     private void checkDefaultBrowser() {
         final int myUserId = UserHandle.myUserId();
         final String packageName = getDefaultBrowserPackageName(myUserId);
-        PackageInfo info = getPackageInfo(packageName, 0, myUserId);
-        if (info == null) {
-            Slog.w(TAG, "Default browser no longer installed: " + packageName);
-            setDefaultBrowserPackageName(null, myUserId);
+        if (packageName != null) {
+            PackageInfo info = getPackageInfo(packageName, 0, myUserId);
+            if (info == null) {
+                Slog.w(TAG, "Default browser no longer installed: " + packageName);
+                synchronized (mPackages) {
+                    applyFactoryDefaultBrowserLPw(myUserId);    // leaves ambiguous when > 1
+                }
+            }
         }
     }
 
@@ -2562,6 +2625,21 @@ public class PackageManagerService extends IPackageManager.Stub {
         return null;
     }
 
+    @Override
+    public int getMountExternalMode(int uid) {
+        if (Process.isIsolated(uid)) {
+            return Zygote.MOUNT_EXTERNAL_NONE;
+        } else {
+            if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED) {
+                return Zygote.MOUNT_EXTERNAL_WRITE;
+            } else if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED) {
+                return Zygote.MOUNT_EXTERNAL_READ;
+            } else {
+                return Zygote.MOUNT_EXTERNAL_DEFAULT;
+            }
+        }
+    }
+
     static PermissionInfo generatePermissionInfo(
             BasePermission bp, int flags) {
         if (bp.perm != null) {
@@ -3201,6 +3279,7 @@ public class PackageManagerService extends IPackageManager.Stub {
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
                 "grantRuntimePermission");
 
+        final int uid;
         final SettingBase sb;
 
         synchronized (mPackages) {
@@ -3216,6 +3295,7 @@ public class PackageManagerService extends IPackageManager.Stub {
 
             enforceDeclaredAsUsedAndRuntimePermission(pkg, bp);
 
+            uid = pkg.applicationInfo.uid;
             sb = (SettingBase) pkg.mExtras;
             if (sb == null) {
                 throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -3245,11 +3325,22 @@ public class PackageManagerService extends IPackageManager.Stub {
                 } break;
             }
 
-            mOnPermissionChangeListeners.onPermissionsChanged(pkg.applicationInfo.uid);
+            mOnPermissionChangeListeners.onPermissionsChanged(uid);
 
             // Not critical if that is lost - app has to request again.
             mSettings.writeRuntimePermissionsForUserLPr(userId, false);
         }
+
+        if (READ_EXTERNAL_STORAGE.equals(name)
+                || WRITE_EXTERNAL_STORAGE.equals(name)) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final StorageManager storage = mContext.getSystemService(StorageManager.class);
+                storage.remountUid(uid);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 
     @Override
@@ -3309,6 +3400,27 @@ public class PackageManagerService extends IPackageManager.Stub {
     }
 
     @Override
+    public void resetRuntimePermissions() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+                "revokeRuntimePermission");
+
+        int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    "resetRuntimePermissions");
+        }
+
+        synchronized (mPackages) {
+            updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL);
+            for (int userId : UserManagerService.getInstance().getUserIds()) {
+                mDefaultPermissionPolicy.grantDefaultPermissions(userId);
+            }
+        }
+    }
+
+    @Override
     public int getPermissionFlags(String name, String packageName, int userId) {
         if (!sUserManager.exists(userId)) {
             return 0;
@@ -4412,7 +4524,7 @@ public class PackageManagerService extends IPackageManager.Stub {
 
         synchronized (mPackages) {
             final int count = candidates.size();
-            // First, try to use the domain prefered App. Partition the candidates into four lists:
+            // First, try to use the domain preferred app. Partition the candidates into four lists:
             // one for the final results, one for the "do not use ever", one for "undefined status"
             // and finally one for "Browser App type".
             for (int n=0; n<count; n++) {
@@ -9546,6 +9658,9 @@ public class PackageManagerService extends IPackageManager.Stub {
 
     @Override
     public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+
         boolean result = false;
         synchronized (mPackages) {
             result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
@@ -9587,8 +9702,11 @@ public class PackageManagerService extends IPackageManager.Stub {
 
     @Override
     public boolean setDefaultBrowserPackageName(String packageName, int userId) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+
         synchronized (mPackages) {
-            boolean result = mSettings.setDefaultBrowserPackageNameLPr(packageName, userId);
+            boolean result = mSettings.setDefaultBrowserPackageNameLPw(packageName, userId);
             if (packageName != null) {
                 result |= updateIntentVerificationStatus(packageName,
                         PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
@@ -12020,7 +12138,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                 final int verificationId = mIntentFilterVerificationToken++;
                 for (PackageParser.Activity a : pkg.activities) {
                     for (ActivityIntentInfo filter : a.intents) {
-                        if (filter.hasOnlyWebDataURI() && needsNetworkVerificationLPr(filter)) {
+                        if (filter.handlesWebUris(true) && needsNetworkVerificationLPr(filter)) {
                             if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                                     "Verification needed for IntentFilter:" + filter.toString());
                             mIntentFilterVerifier.addOneIntentFilterVerification(
@@ -13348,15 +13466,15 @@ public class PackageManagerService extends IPackageManager.Stub {
 
     @Override
     public void resetPreferredActivities(int userId) {
-        /* TODO: Actually use userId. Why is it being passed in? */
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
         // writer
         synchronized (mPackages) {
-            int user = UserHandle.getCallingUserId();
-            clearPackagePreferredActivitiesLPw(null, user);
-            mSettings.readDefaultPreferredAppsLPw(this, user);
-            scheduleWritePackageRestrictionsLocked(user);
+            clearPackagePreferredActivitiesLPw(null, userId);
+            mSettings.applyDefaultPreferredAppsLPw(this, userId);
+            applyFactoryDefaultBrowserLPw(userId);
+
+            scheduleWritePackageRestrictionsLocked(userId);
         }
     }
 
@@ -13992,6 +14110,8 @@ public class PackageManagerService extends IPackageManager.Stub {
             Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);
         }
 
+        int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
+
         synchronized (mPackages) {
             // Verify that all of the preferred activity components actually
             // exist.  It is possible for applications to be updated and at
@@ -14021,15 +14141,19 @@ public class PackageManagerService extends IPackageManager.Stub {
                             mSettings.mPreferredActivities.keyAt(i));
                 }
             }
+
+            for (int userId : UserManagerService.getInstance().getUserIds()) {
+                if (!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
+                    grantPermissionsUserIds = ArrayUtils.appendInt(
+                            grantPermissionsUserIds, userId);
+                }
+            }
         }
         sUserManager.systemReady();
 
         // If we upgraded grant all default permissions before kicking off.
-        if (isFirstBoot() || (CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE && mIsUpgrade)) {
-            updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL);
-            for (int userId : UserManagerService.getInstance().getUserIds()) {
-                mDefaultPermissionPolicy.grantDefaultPermissions(userId);
-            }
+        for (int userId : grantPermissionsUserIds) {
+            mDefaultPermissionPolicy.grantDefaultPermissions(userId);
         }
 
         // Kick off any messages waiting for system ready
@@ -14159,6 +14283,7 @@ public class PackageManagerService extends IPackageManager.Stub {
         boolean checkin = false;
 
         String packageName = null;
+        ArraySet<String> permissionNames = null;
 
         int opti = 0;
         while (opti < args.length) {
@@ -14182,6 +14307,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                 pw.println("    k[eysets]: print known keysets");
                 pw.println("    r[esolvers]: dump intent resolvers");
                 pw.println("    perm[issions]: dump permissions");
+                pw.println("    permission [name ...]: dump declaration and use of given permission");
                 pw.println("    pref[erred]: print preferred package settings");
                 pw.println("    preferred-xml [--full]: print preferred package settings as xml");
                 pw.println("    prov[iders]: dump content providers");
@@ -14223,6 +14349,18 @@ public class PackageManagerService extends IPackageManager.Stub {
                 dumpState.setDump(DumpState.DUMP_RESOLVERS);
             } else if ("perm".equals(cmd) || "permissions".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_PERMISSIONS);
+            } else if ("permission".equals(cmd)) {
+                if (opti >= args.length) {
+                    pw.println("Error: permission requires permission name");
+                    return;
+                }
+                permissionNames = new ArraySet<>();
+                while (opti < args.length) {
+                    permissionNames.add(args[opti]);
+                    opti++;
+                }
+                dumpState.setDump(DumpState.DUMP_PERMISSIONS
+                        | DumpState.DUMP_PACKAGES | DumpState.DUMP_SHARED_USERS);
             } else if ("pref".equals(cmd) || "preferred".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_PREFERRED);
             } else if ("preferred-xml".equals(cmd)) {
@@ -14505,8 +14643,8 @@ public class PackageManagerService extends IPackageManager.Stub {
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
-                mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
-                if (packageName == null) {
+                mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState);
+                if (packageName == null && permissionNames == null) {
                     for (int iperm=0; iperm<mAppOpPermissionPackages.size(); iperm++) {
                         if (iperm == 0) {
                             if (dumpState.onTitlePrinted())
@@ -14566,11 +14704,11 @@ public class PackageManagerService extends IPackageManager.Stub {
             }
 
             if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
-                mSettings.dumpPackagesLPr(pw, packageName, dumpState, checkin);
+                mSettings.dumpPackagesLPr(pw, packageName, permissionNames, dumpState, checkin);
             }
 
             if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
-                mSettings.dumpSharedUsersLPr(pw, packageName, dumpState, checkin);
+                mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin);
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
@@ -15418,6 +15556,7 @@ public class PackageManagerService extends IPackageManager.Stub {
         if (mInstaller != null) {
             mInstaller.createUserConfig(userHandle);
             mSettings.createNewUserLILPw(this, mInstaller, userHandle, path);
+            applyFactoryDefaultBrowserLPw(userHandle);
         }
     }
 
index 04beafd..57ef284 100644 (file)
@@ -219,6 +219,22 @@ public final class PermissionsState {
     }
 
     /**
+     * Returns whether the state has any known request for the given permission name,
+     * whether or not it has been granted.
+     */
+    public boolean hasRequestedPermission(ArraySet<String> names) {
+        if (mPermissions == null) {
+            return false;
+        }
+        for (int i=names.size()-1; i>=0; i--) {
+            if (mPermissions.get(names.valueAt(i)) != null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Gets all permissions for a given device user id regardless if they
      * are install time or runtime permissions.
      *
@@ -446,7 +462,7 @@ public final class PermissionsState {
             }
         }
 
-        return  permissionStates;
+        return permissionStates;
     }
 
     private int grantPermission(BasePermission permission, int userId) {
index dcd7799..51ac81d 100644 (file)
@@ -195,6 +195,7 @@ final class Settings {
     private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
     private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
     private static final String ATTR_PACKAGE_NAME= "packageName";
+    private static final String ATTR_FINGERPRINT = "fingerprint";
 
     private final Object mLock;
 
@@ -1130,7 +1131,7 @@ final class Settings {
         return result;
     }
 
-    boolean setDefaultBrowserPackageNameLPr(String packageName, int userId) {
+    boolean setDefaultBrowserPackageNameLPw(String packageName, int userId) {
         if (userId == UserHandle.USER_ALL) {
             return false;
         }
@@ -1177,6 +1178,16 @@ final class Settings {
         }
     }
 
+    boolean areDefaultRuntimePermissionsGrantedLPr(int userId) {
+        return mRuntimePermissionsPersistence
+                .areDefaultRuntimPermissionsGrantedLPr(userId);
+    }
+
+    void onDefaultRuntimePermissionsGrantedLPr(int userId) {
+        mRuntimePermissionsPersistence
+                .onDefaultRuntimePermissionsGrantedLPr(userId);
+    }
+
     /**
      * Returns whether the current database has is older than {@code version}
      * for apps on internal storage.
@@ -2710,7 +2721,7 @@ final class Settings {
         return true;
     }
 
-    void readDefaultPreferredAppsLPw(PackageManagerService service, int userId) {
+    void applyDefaultPreferredAppsLPw(PackageManagerService service, int userId) {
         // First pull data from any pre-installed apps.
         for (PackageSetting ps : mPackages.values()) {
             if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0 && ps.pkg != null
@@ -3618,7 +3629,7 @@ final class Settings {
                     UserHandle.getUid(userHandle, ps.appId), userHandle,
                     ps.pkg.applicationInfo.seinfo);
         }
-        readDefaultPreferredAppsLPw(service, userHandle);
+        applyDefaultPreferredAppsLPw(service, userHandle);
         writePackageRestrictionsLPr(userHandle);
         writePackageListLPr(userHandle);
     }
@@ -3881,8 +3892,9 @@ final class Settings {
         ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
     };
 
-    void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag, PackageSetting ps,
-            SimpleDateFormat sdf, Date date, List<UserInfo> users) {
+    void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag,
+            ArraySet<String> permissionNames, PackageSetting ps, SimpleDateFormat sdf,
+            Date date, List<UserInfo> users) {
         if (checkinTag != null) {
             pw.print(checkinTag);
             pw.print(",");
@@ -3953,10 +3965,13 @@ final class Settings {
         }
         pw.print(prefix); pw.print("  pkg="); pw.println(ps.pkg);
         pw.print(prefix); pw.print("  codePath="); pw.println(ps.codePathString);
-        pw.print(prefix); pw.print("  resourcePath="); pw.println(ps.resourcePathString);
-        pw.print(prefix); pw.print("  legacyNativeLibraryDir="); pw.println(ps.legacyNativeLibraryPathString);
-        pw.print(prefix); pw.print("  primaryCpuAbi="); pw.println(ps.primaryCpuAbiString);
-        pw.print(prefix); pw.print("  secondaryCpuAbi="); pw.println(ps.secondaryCpuAbiString);
+        if (permissionNames == null) {
+            pw.print(prefix); pw.print("  resourcePath="); pw.println(ps.resourcePathString);
+            pw.print(prefix); pw.print("  legacyNativeLibraryDir=");
+            pw.println(ps.legacyNativeLibraryPathString);
+            pw.print(prefix); pw.print("  primaryCpuAbi="); pw.println(ps.primaryCpuAbiString);
+            pw.print(prefix); pw.print("  secondaryCpuAbi="); pw.println(ps.secondaryCpuAbiString);
+        }
         pw.print(prefix); pw.print("  versionCode="); pw.print(ps.versionCode);
         if (ps.pkg != null) {
             pw.print(" targetSdk="); pw.print(ps.pkg.applicationInfo.targetSdkVersion);
@@ -3969,8 +3984,10 @@ final class Settings {
                 pw.println(ps.pkg.applicationInfo.toString());
             pw.print(prefix); pw.print("  flags="); printFlags(pw, ps.pkg.applicationInfo.flags,
                     FLAG_DUMP_SPEC); pw.println();
-            pw.print(prefix); pw.print("  priavateFlags="); printFlags(pw,
-                    ps.pkg.applicationInfo.privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println();
+            if (ps.pkg.applicationInfo.privateFlags != 0) {
+                pw.print(prefix); pw.print("  privateFlags="); printFlags(pw,
+                        ps.pkg.applicationInfo.privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println();
+            }
             pw.print(prefix); pw.print("  dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
             pw.print(prefix); pw.print("  supportsScreens=[");
             boolean first = true;
@@ -4063,9 +4080,9 @@ final class Settings {
         pw.print(prefix); pw.print("  pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
                 pw.println();
 
-        if (ps.sharedUser == null) {
+        if (ps.sharedUser == null || permissionNames != null) {
             PermissionsState permissionsState = ps.getPermissionsState();
-            dumpInstallPermissionsLPr(pw, prefix + "  ", permissionsState);
+            dumpInstallPermissionsLPr(pw, prefix + "  ", permissionNames, permissionsState);
         }
 
         for (UserInfo user : users) {
@@ -4089,28 +4106,31 @@ final class Settings {
             if (ps.sharedUser == null) {
                 PermissionsState permissionsState = ps.getPermissionsState();
                 dumpGidsLPr(pw, prefix + "    ", permissionsState.computeGids(user.id));
-                dumpRuntimePermissionsLPr(pw, prefix + "    ", permissionsState
+                dumpRuntimePermissionsLPr(pw, prefix + "    ", permissionNames, permissionsState
                         .getRuntimePermissionStates(user.id));
             }
 
-            ArraySet<String> cmp = ps.getDisabledComponents(user.id);
-            if (cmp != null && cmp.size() > 0) {
-                pw.print(prefix); pw.println("    disabledComponents:");
-                for (String s : cmp) {
-                    pw.print(prefix); pw.print("    "); pw.println(s);
+            if (permissionNames == null) {
+                ArraySet<String> cmp = ps.getDisabledComponents(user.id);
+                if (cmp != null && cmp.size() > 0) {
+                    pw.print(prefix); pw.println("    disabledComponents:");
+                    for (String s : cmp) {
+                        pw.print(prefix); pw.print("    "); pw.println(s);
+                    }
                 }
-            }
-            cmp = ps.getEnabledComponents(user.id);
-            if (cmp != null && cmp.size() > 0) {
-                pw.print(prefix); pw.println("    enabledComponents:");
-                for (String s : cmp) {
-                    pw.print(prefix); pw.print("    "); pw.println(s);
+                cmp = ps.getEnabledComponents(user.id);
+                if (cmp != null && cmp.size() > 0) {
+                    pw.print(prefix); pw.println("    enabledComponents:");
+                    for (String s : cmp) {
+                        pw.print(prefix); pw.print("    "); pw.println(s);
+                    }
                 }
             }
         }
     }
 
-    void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState, boolean checkin) {
+    void dumpPackagesLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
+            DumpState dumpState, boolean checkin) {
         final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         final Date date = new Date();
         boolean printedSomething = false;
@@ -4120,6 +4140,10 @@ final class Settings {
                     && !packageName.equals(ps.name)) {
                 continue;
             }
+            if (permissionNames != null
+                    && !ps.getPermissionsState().hasRequestedPermission(permissionNames)) {
+                continue;
+            }
 
             if (!checkin && packageName != null) {
                 dumpState.setSharedUser(ps.sharedUser);
@@ -4131,11 +4155,11 @@ final class Settings {
                 pw.println("Packages:");
                 printedSomething = true;
             }
-            dumpPackageLPr(pw, "  ", checkin ? "pkg" : null, ps, sdf, date, users);
+            dumpPackageLPr(pw, "  ", checkin ? "pkg" : null, permissionNames, ps, sdf, date, users);
         }
 
         printedSomething = false;
-        if (!checkin && mRenamedPackages.size() > 0) {
+        if (!checkin && mRenamedPackages.size() > 0 && permissionNames == null) {
             for (final Map.Entry<String, String> e : mRenamedPackages.entrySet()) {
                 if (packageName != null && !packageName.equals(e.getKey())
                         && !packageName.equals(e.getValue())) {
@@ -4159,7 +4183,7 @@ final class Settings {
         }
 
         printedSomething = false;
-        if (mDisabledSysPackages.size() > 0) {
+        if (mDisabledSysPackages.size() > 0 && permissionNames == null) {
             for (final PackageSetting ps : mDisabledSysPackages.values()) {
                 if (packageName != null && !packageName.equals(ps.realName)
                         && !packageName.equals(ps.name)) {
@@ -4171,17 +4195,22 @@ final class Settings {
                     pw.println("Hidden system packages:");
                     printedSomething = true;
                 }
-                dumpPackageLPr(pw, "  ", checkin ? "dis" : null, ps, sdf, date, users);
+                dumpPackageLPr(pw, "  ", checkin ? "dis" : null, permissionNames, ps, sdf, date,
+                        users);
             }
         }
     }
 
-    void dumpPermissionsLPr(PrintWriter pw, String packageName, DumpState dumpState) {
+    void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
+            DumpState dumpState) {
         boolean printedSomething = false;
         for (BasePermission p : mPermissions.values()) {
             if (packageName != null && !packageName.equals(p.sourcePackage)) {
                 continue;
             }
+            if (permissionNames != null && !permissionNames.contains(p.name)) {
+                continue;
+            }
             if (!printedSomething) {
                 if (dumpState.onTitlePrinted())
                     pw.println();
@@ -4211,13 +4240,17 @@ final class Settings {
         }
     }
 
-    void dumpSharedUsersLPr(PrintWriter pw, String packageName, DumpState dumpState,
-            boolean checkin) {
+    void dumpSharedUsersLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames,
+            DumpState dumpState, boolean checkin) {
         boolean printedSomething = false;
         for (SharedUserSetting su : mSharedUsers.values()) {
             if (packageName != null && su != dumpState.getSharedUser()) {
                 continue;
             }
+            if (permissionNames != null
+                    && !su.getPermissionsState().hasRequestedPermission(permissionNames)) {
+                continue;
+            }
             if (!checkin) {
                 if (!printedSomething) {
                     if (dumpState.onTitlePrinted())
@@ -4235,7 +4268,7 @@ final class Settings {
                 pw.print(prefix); pw.print("userId="); pw.println(su.userId);
 
                 PermissionsState permissionsState = su.getPermissionsState();
-                dumpInstallPermissionsLPr(pw, prefix, permissionsState);
+                dumpInstallPermissionsLPr(pw, prefix, permissionNames, permissionsState);
 
                 for (int userId : UserManagerService.getInstance().getUserIds()) {
                     final int[] gids = permissionsState.computeGids(userId);
@@ -4244,7 +4277,7 @@ final class Settings {
                     if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
                         pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": ");
                         dumpGidsLPr(pw, prefix + "  ", gids);
-                        dumpRuntimePermissionsLPr(pw, prefix + "  ", permissions);
+                        dumpRuntimePermissionsLPr(pw, prefix + "  ", permissionNames, permissions);
                     }
                 }
             } else {
@@ -4289,11 +4322,15 @@ final class Settings {
         }
     }
 
-    void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix,
+    void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
             List<PermissionState> permissionStates) {
         if (!permissionStates.isEmpty()) {
             pw.print(prefix); pw.println("runtime permissions:");
             for (PermissionState permissionState : permissionStates) {
+                if (permissionNames != null
+                        && !permissionNames.contains(permissionState.getName())) {
+                    continue;
+                }
                 pw.print(prefix); pw.print("  "); pw.print(permissionState.getName());
                 pw.print(", granted="); pw.print(permissionState.isGranted());
                     pw.print(", flags=0x"); pw.println(Integer.toHexString(
@@ -4302,12 +4339,16 @@ final class Settings {
         }
     }
 
-    void dumpInstallPermissionsLPr(PrintWriter pw, String prefix,
+    void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
             PermissionsState permissionsState) {
         List<PermissionState> permissionStates = permissionsState.getInstallPermissionStates();
         if (!permissionStates.isEmpty()) {
             pw.print(prefix); pw.println("install permissions:");
             for (PermissionState permissionState : permissionStates) {
+                if (permissionNames != null
+                        && !permissionNames.contains(permissionState.getName())) {
+                    continue;
+                }
                 pw.print(prefix); pw.print("  "); pw.print(permissionState.getName());
                     pw.print(", granted="); pw.print(permissionState.isGranted());
                     pw.print(", flags=0x"); pw.println(Integer.toHexString(
@@ -4334,15 +4375,33 @@ final class Settings {
         private final Object mLock;
 
         @GuardedBy("mLock")
-        private SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
+        private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
+
+        @GuardedBy("mLock")
+        // The mapping keys are user ids.
+        private final SparseLongArray mLastNotWrittenMutationTimesMillis = new SparseLongArray();
+
+        @GuardedBy("mLock")
+        // The mapping keys are user ids.
+        private final SparseArray<String> mFingerprints = new SparseArray<>();
 
         @GuardedBy("mLock")
-        private SparseLongArray mLastNotWrittenMutationTimesMillis = new SparseLongArray();
+        // The mapping keys are user ids.
+        private final SparseBooleanArray mDefaultPermissionsGranted = new SparseBooleanArray();
 
         public RuntimePermissionPersistence(Object lock) {
             mLock = lock;
         }
 
+        public boolean areDefaultRuntimPermissionsGrantedLPr(int userId) {
+            return mDefaultPermissionsGranted.get(userId);
+        }
+
+        public void onDefaultRuntimePermissionsGrantedLPr(int userId) {
+            mFingerprints.put(userId, Build.FINGERPRINT);
+            writePermissionsForUserAsyncLPr(userId);
+        }
+
         public void writePermissionsForUserSyncLPr(int userId) {
             mHandler.removeMessages(userId);
             writePermissionsSync(userId);
@@ -4427,6 +4486,9 @@ final class Settings {
                 serializer.startDocument(null, true);
                 serializer.startTag(null, TAG_RUNTIME_PERMISSIONS);
 
+                String fingerprint = mFingerprints.get(userId);
+                serializer.attribute(null, ATTR_FINGERPRINT, fingerprint);
+
                 final int packageCount = permissionsForPackage.size();
                 for (int i = 0; i < packageCount; i++) {
                     String packageName = permissionsForPackage.keyAt(i);
@@ -4451,7 +4513,10 @@ final class Settings {
                 serializer.endDocument();
                 destination.finishWrite(out);
 
-                // Any error while writing is fatal.
+                if (Build.FINGERPRINT.equals(fingerprint)) {
+                    mDefaultPermissionsGranted.put(userId, true);
+                }
+            // Any error while writing is fatal.
             } catch (Throwable t) {
                 Slog.wtf(PackageManagerService.TAG,
                         "Failed to write settings, restoring backup", t);
@@ -4529,6 +4594,13 @@ final class Settings {
                 }
 
                 switch (parser.getName()) {
+                    case TAG_RUNTIME_PERMISSIONS: {
+                        String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT);
+                        mFingerprints.put(userId, fingerprint);
+                        final boolean defaultsGranted = Build.FINGERPRINT.equals(fingerprint);
+                        mDefaultPermissionsGranted.put(userId, defaultsGranted);
+                    } break;
+
                     case TAG_PACKAGE: {
                         String name = parser.getAttributeValue(null, ATTR_NAME);
                         PackageSetting ps = mPackages.get(name);
index 51bb36f..5a0bee9 100644 (file)
@@ -3117,7 +3117,7 @@ public final class PowerManagerService extends SystemService
                 return;
             }
 
-            if (eventTime > SystemClock.uptimeMillis()) {
+            if (eventTime > now) {
                 throw new IllegalArgumentException("event time must not be in the future");
             }
 
index d7b202d..76baaa7 100644 (file)
@@ -343,7 +343,9 @@ public class WindowAnimator {
                         boolean applyExistingExitAnimation = mPostKeyguardExitAnimation != null
                                 && !winAnimator.mKeyguardGoingAwayAnimation
                                 && win.hasDrawnLw()
-                                && win.mAttachedWindow == null;
+                                && win.mAttachedWindow == null
+                                && !win.mIsImWindow
+                                && displayId == Display.DEFAULT_DISPLAY;
 
                         // If the window is already showing and we don't need to apply an existing
                         // Keyguard exit animation, skip.
index 3b9cc9d..e257e89 100644 (file)
@@ -48,9 +48,9 @@ namespace android
 static bool wakeup_init = false;
 static sem_t wakeup_sem;
 
-static void wakeup_callback(void)
+static void wakeup_callback(bool success)
 {
-    ALOGV("In wakeup_callback");
+    ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
     int ret = sem_post(&wakeup_sem);
     if (ret < 0) {
         char buf[80];
@@ -59,10 +59,9 @@ static void wakeup_callback(void)
     }
 }
 
-static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jintArray outIrqs,
-        jobjectArray outReasons)
+static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobjectArray outReasons)
 {
-    if (outIrqs == NULL || outReasons == NULL) {
+    if (outReasons == NULL) {
         jniThrowException(env, "java/lang/NullPointerException", "null argument");
         return -1;
     }
@@ -100,32 +99,47 @@ static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jintArray outIrqs,
         return -1;
     }
 
-    int numOut = env->GetArrayLength(outIrqs);
-    ScopedIntArrayRW irqs(env, outIrqs);
-
-    ALOGV("Reading up to %d wakeup reasons", numOut);
+    ALOGV("Reading wakeup reasons");
 
     char mergedreason[MAX_REASON_SIZE];
     char* mergedreasonpos = mergedreason;
     int remainreasonlen = MAX_REASON_SIZE;
-    int firstirq = 0;
     char reasonline[128];
     int i = 0;
-    while (fgets(reasonline, sizeof(reasonline), fp) != NULL && i < numOut) {
+    while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {
         char* pos = reasonline;
         char* endPos;
-        // First field is the index.
+        int len;
+        // First field is the index or 'Abort'.
         int irq = (int)strtol(pos, &endPos, 10);
-        if (pos == endPos) {
-            // Ooops.
-            ALOGE("Bad reason line: %s", reasonline);
-            continue;
+        if (pos != endPos) {
+            // Write the irq number to the merged reason string.
+            len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%d" : ":%d", irq);
+        } else {
+            // The first field is not an irq, it may be the word Abort.
+            const size_t abortPrefixLen = strlen("Abort:");
+            if (strncmp(pos, "Abort:", abortPrefixLen) != 0) {
+                // Ooops.
+                ALOGE("Bad reason line: %s", reasonline);
+                continue;
+            }
+
+            // Write 'Abort' to the merged reason string.
+            len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "Abort" : ":Abort");
+            endPos = pos + abortPrefixLen;
         }
         pos = endPos;
+
+        if (len >= 0 && len < remainreasonlen) {
+            mergedreasonpos += len;
+            remainreasonlen -= len;
+        }
+
         // Skip whitespace; rest of the buffer is the reason string.
         while (*pos == ' ') {
             pos++;
         }
+
         // Chop newline at end.
         char* endpos = pos;
         while (*endpos != 0) {
@@ -135,38 +149,17 @@ static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jintArray outIrqs,
             }
             endpos++;
         }
-        // For now we are not separating out the first irq.
-        // This is because in practice there are always multiple
-        // lines of wakeup reasons, so it is better to just treat
-        // them all together as a single string.
-        if (false && i == 0) {
-            firstirq = irq;
-        } else {
-            int len = snprintf(mergedreasonpos, remainreasonlen,
-                    i == 0 ? "%d" : ":%d", irq);
-            if (len >= 0 && len < remainreasonlen) {
-                mergedreasonpos += len;
-                remainreasonlen -= len;
-            }
-        }
-        int len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
+
+        len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
         if (len >= 0 && len < remainreasonlen) {
             mergedreasonpos += len;
             remainreasonlen -= len;
         }
-        // For now it is better to combine all of these in to one entry in the
-        // battery history.  In the future, it might be nice to figure out a way
-        // to efficiently store multiple lines as a single entry in the history.
-        //irqs[i] = irq;
-        //ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(pos));
-        //env->SetObjectArrayElement(outReasons, i, reasonString.get());
-        //ALOGV("Wakeup reason #%d: irw %d reason %s", i, irq, pos);
         i++;
     }
 
     ALOGV("Got %d reasons", i);
     if (i > 0) {
-        irqs[0] = firstirq;
         *mergedreasonpos = 0;
         ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(mergedreason));
         env->SetObjectArrayElement(outReasons, 0, reasonString.get());
@@ -182,7 +175,7 @@ static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jintArray outIrqs,
 }
 
 static JNINativeMethod method_table[] = {
-    { "nativeWaitWakeup", "([I[Ljava/lang/String;)I", (void*)nativeWaitWakeup },
+    { "nativeWaitWakeup", "([Ljava/lang/String;)I", (void*)nativeWaitWakeup },
 };
 
 int register_android_server_BatteryStatsService(JNIEnv *env)
index 491b412..5cfbb40 100644 (file)
@@ -85,6 +85,7 @@ import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
 import android.provider.ContactsContract.QuickContact;
 import android.provider.ContactsInternal;
 import android.provider.Settings;
@@ -108,7 +109,6 @@ import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.R;
-import com.android.internal.os.storage.ExternalStorageFormatter;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
@@ -3307,25 +3307,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
     }
 
     private void wipeDataLocked(boolean wipeExtRequested, String reason) {
-        // TODO: wipe all public volumes on device
-
-        // If the SD card is encrypted and non-removable, we have to force a wipe.
-        boolean forceExtWipe = !Environment.isExternalStorageRemovable() && isExtStorageEncrypted();
-
-        // Note: we can only do the wipe via ExternalStorageFormatter if the volume is not emulated.
-        if ((forceExtWipe || wipeExtRequested) && !Environment.isExternalStorageEmulated()) {
-            Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
-            intent.putExtra(ExternalStorageFormatter.EXTRA_ALWAYS_RESET, true);
-            intent.putExtra(Intent.EXTRA_REASON, reason);
-            intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
-            mWakeLock.acquire(10000);
-            mContext.startService(intent);
-        } else {
-            try {
-                RecoverySystem.rebootWipeUserData(mContext, reason);
-            } catch (IOException | SecurityException e) {
-                Slog.w(LOG_TAG, "Failed requesting data wipe", e);
-            }
+        if (wipeExtRequested) {
+            StorageManager sm = (StorageManager) mContext.getSystemService(
+                    Context.STORAGE_SERVICE);
+            sm.wipeAdoptableDisks();
+        }
+        try {
+            RecoverySystem.rebootWipeUserData(mContext, reason);
+        } catch (IOException | SecurityException e) {
+            Slog.w(LOG_TAG, "Failed requesting data wipe", e);
         }
     }
 
index b68abab..cde87bd 100644 (file)
@@ -433,6 +433,11 @@ public class VoiceInteractionManagerService extends SystemService {
                     + " user=" + userHandle);
         }
 
+        void resetCurAssistant(int userHandle) {
+            Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                    Settings.Secure.ASSISTANT, null, userHandle);
+        }
+
         @Override
         public void showSession(IVoiceInteractionService service, Bundle args, int flags) {
             synchronized (this) {
@@ -897,6 +902,7 @@ public class VoiceInteractionManagerService extends SystemService {
                         }
                         setCurInteractor(null, userHandle);
                         setCurRecognizer(null, userHandle);
+                        resetCurAssistant(userHandle);
                         initForUser(userHandle);
                         switchImplementationIfNeededLocked(true);
                     }
index 06e6a62..e861668 100644 (file)
@@ -211,6 +211,19 @@ public class CarrierConfigManager {
             KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
 
     /**
+     * Override the platform's notion of a network operator being considered non roaming.
+     * If true all networks are considered as home network a.k.a non-roaming.  When false,
+     * the 2 pairs of CMDA and GSM roaming/non-roaming arrays are consulted.
+     *
+     * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+     * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
+     * @see KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
+     */
+    public static final String
+            KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
+
+    /**
      * Flag specifying whether VoLTE should be available for carrier, independent of carrier
      * provisioning. If false: hard disabled. If true: then depends on carrier provisioning,
      * availability, etc.
@@ -407,6 +420,7 @@ public class CarrierConfigManager {
         sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
index a2bd74b..ab2a98d 100644 (file)
@@ -891,16 +891,17 @@ public class TelephonyManager {
     }
 
     /**
-     * Returns the neighboring cell information of the device. The getAllCellInfo is preferred
-     * and use this only if getAllCellInfo return nulls or an empty list.
-     *<p>
-     * In the future this call will be deprecated.
-     *<p>
+     * Returns the neighboring cell information of the device.
+     *
      * @return List of NeighboringCellInfo or null if info unavailable.
      *
      * <p>Requires Permission:
      * (@link android.Manifest.permission#ACCESS_COARSE_UPDATES}
+     *
+     * @deprecated Use (@link getAllCellInfo} which returns a superset of the information
+     *             from NeighboringCellInfo.
      */
+    @Deprecated
     public List<NeighboringCellInfo> getNeighboringCellInfo() {
         try {
             ITelephony telephony = getITelephony();
index c708c14..5a6bd1d 100644 (file)
@@ -88,6 +88,7 @@ public class CellNetworkScanResult implements Parcelable {
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mStatus);
         if (mOperators != null && mOperators.size() > 0) {
+            out.writeInt(mOperators.size());
             for (OperatorInfo network : mOperators) {
                 network.writeToParcel(out, flags);
             }
index d44afb0..610f30b 100644 (file)
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
+    <ImageView android:id="@+id/full_screenshot"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="invisible"/>
+
     <com.android.test.voiceinteraction.AssistVisualizer android:id="@+id/assist_visualizer"
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="top"
-            android:orientation="vertical"
+            android:orientation="horizontal"
             android:background="#ffffffff"
             android:elevation="8dp"
             >
-            <Button android:id="@+id/start"
+            <ImageView android:id="@+id/screenshot"
+                android:layout_width="wrap_content"
+                android:layout_height="46dp"
+                android:adjustViewBounds="true" />
+            <View android:layout_width="0dp"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+            <Button android:id="@+id/do_tree"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_gravity="top|right"
-                android:text="@string/start"
-                />
-            <ImageView android:id="@+id/screenshot"
+                android:text="@string/tree" />
+            <Button android:id="@+id/do_text"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"/>
+                android:layout_height="wrap_content"
+                android:text="@string/text" />
+            <Button android:id="@+id/start"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/start" />
         </LinearLayout>
 
         <LinearLayout android:id="@+id/bottom_content"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginBottom="16dp"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                />
+                android:textAppearance="?android:attr/textAppearanceMedium" />
 
             <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
                     android:orientation="horizontal">
                 <Button android:id="@+id/confirm"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/confirm"
-                    />
+                    android:text="@string/confirm" />
                 <Button android:id="@+id/complete"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/complete"
-                    />
+                    android:text="@string/complete" />
                 <Button android:id="@+id/abort"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/abort"
-                    />
+                    android:text="@string/abort" />
             </LinearLayout>
 
         </LinearLayout>
index 6289929..4cf4104 100644 (file)
@@ -17,6 +17,8 @@
 <resources>
 
     <string name="start">Start</string>
+    <string name="tree">Tree</string>
+    <string name="text">Text</string>
     <string name="asyncStructure">(Async structure goes here)</string>
     <string name="confirm">Confirm</string>
     <string name="abort">Abort</string>
index 439ace8..339755f 100644 (file)
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
 import android.app.assist.AssistStructure;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
@@ -31,10 +32,32 @@ import java.util.ArrayList;
 public class AssistVisualizer extends View {
     static final String TAG = "AssistVisualizer";
 
+    static class TextEntry {
+        final Rect bounds;
+        final int parentLeft, parentTop;
+        final Matrix matrix;
+        final String className;
+        final CharSequence text;
+
+        TextEntry(AssistStructure.ViewNode node, int parentLeft, int parentTop, Matrix matrix) {
+            int left = parentLeft+node.getLeft();
+            int top = parentTop+node.getTop();
+            bounds = new Rect(left, top, left+node.getWidth(), top+node.getHeight());
+            this.parentLeft = parentLeft;
+            this.parentTop = parentTop;
+            this.matrix = new Matrix(matrix);
+            this.className = node.getClassName();
+            this.text = node.getText() != null ? node.getText() : node.getContentDescription();
+        }
+    }
+
     AssistStructure mAssistStructure;
     final Paint mFramePaint = new Paint();
-    final ArrayList<Rect> mTextRects = new ArrayList<>();
+    final Paint mFrameNoTransformPaint = new Paint();
+    final ArrayList<Matrix> mMatrixStack = new ArrayList<>();
+    final ArrayList<TextEntry> mTextRects = new ArrayList<>();
     final int[] mTmpLocation = new int[2];
+    final float[] mTmpMatrixPoint = new float[2];
 
     public AssistVisualizer(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -42,17 +65,26 @@ public class AssistVisualizer extends View {
         mFramePaint.setColor(0xffff0000);
         mFramePaint.setStyle(Paint.Style.STROKE);
         mFramePaint.setStrokeWidth(0);
+        float density = getResources().getDisplayMetrics().density;
+        mFramePaint.setShadowLayer(density, density, density, 0xff000000);
+        mFrameNoTransformPaint.setColor(0xff0000ff);
+        mFrameNoTransformPaint.setStyle(Paint.Style.STROKE);
+        mFrameNoTransformPaint.setStrokeWidth(0);
+        mFrameNoTransformPaint.setShadowLayer(density, density, density, 0xff000000);
     }
 
     public void setAssistStructure(AssistStructure as) {
         mAssistStructure = as;
-        mAssistStructure.dump();
         mTextRects.clear();
         final int N = as.getWindowNodeCount();
         if (N > 0) {
             for (int i=0; i<N; i++) {
                 AssistStructure.WindowNode windowNode = as.getWindowNodeAt(i);
-                buildTextRects(windowNode.getRootViewNode(), windowNode.getLeft(),
+                mMatrixStack.clear();
+                Matrix matrix = new Matrix();
+                matrix.setTranslate(windowNode.getLeft(), windowNode.getTop());
+                mMatrixStack.add(matrix);
+                buildTextRects(windowNode.getRootViewNode(), 0, windowNode.getLeft(),
                         windowNode.getTop());
             }
         }
@@ -60,31 +92,62 @@ public class AssistVisualizer extends View {
         invalidate();
     }
 
+    public void logTree() {
+        if (mAssistStructure != null) {
+            mAssistStructure.dump();
+        }
+    }
+
+    public void logText() {
+        final int N = mTextRects.size();
+        for (int i=0; i<N; i++) {
+            TextEntry te = mTextRects.get(i);
+            Log.d(TAG, "View " + te.className + " " + te.bounds.toShortString()
+                    + " in " + te.parentLeft + "," + te.parentTop
+                    + " matrix=" + te.matrix.toShortString() + ": "
+                    + te.text);
+        }
+    }
+
     public void clearAssistData() {
         mAssistStructure = null;
         mTextRects.clear();
     }
 
-    void buildTextRects(AssistStructure.ViewNode root, int parentLeft, int parentTop) {
+    void buildTextRects(AssistStructure.ViewNode root, int matrixStackIndex,
+            int parentLeft, int parentTop) {
         if (root.getVisibility() != View.VISIBLE) {
             return;
         }
-        int left = parentLeft+root.getLeft();
-        int top = parentTop+root.getTop();
+        Matrix parentMatrix = mMatrixStack.get(matrixStackIndex);
+        matrixStackIndex++;
+        Matrix matrix;
+        if (mMatrixStack.size() > matrixStackIndex) {
+            matrix = mMatrixStack.get(matrixStackIndex);
+            matrix.set(parentMatrix);
+        } else {
+            matrix = new Matrix(parentMatrix);
+            mMatrixStack.add(matrix);
+        }
+        matrix.preTranslate(root.getLeft(), root.getTop());
+        int left = parentLeft + root.getLeft();
+        int top = parentTop + root.getTop();
+        Matrix transform = root.getTransformation();
+        if (transform != null) {
+            matrix.preConcat(transform);
+        }
         if (root.getText() != null || root.getContentDescription() != null) {
-            Rect r = new Rect(left, top, left+root.getWidth(), top+root.getHeight());
-            Log.d(TAG, "View " + root.getClassName() + " " + left + "," + top + " tr "
-                    + r.toShortString() + ": "
-                    + (root.getText() != null ? root.getText() : root.getContentDescription()));
-            mTextRects.add(r);
+            TextEntry te = new TextEntry(root, parentLeft, parentTop, matrix);
+            mTextRects.add(te);
         }
         final int N = root.getChildCount();
         if (N > 0) {
             left -= root.getScrollX();
             top -= root.getScrollY();
+            matrix.preTranslate(-root.getScrollX(), -root.getScrollY());
             for (int i=0; i<N; i++) {
                 AssistStructure.ViewNode child = root.getChildAt(i);
-                buildTextRects(child, left, top);
+                buildTextRects(child, matrixStackIndex, left, top);
             }
         }
     }
@@ -96,9 +159,19 @@ public class AssistVisualizer extends View {
         final int N = mTextRects.size();
         Log.d(TAG, "Drawing text rects in " + this + ": found " + mTextRects.size());
         for (int i=0; i<N; i++) {
-            Rect r = mTextRects.get(i);
-            canvas.drawRect(r.left-mTmpLocation[0], r.top-mTmpLocation[1],
-                    r.right-mTmpLocation[0], r.bottom-mTmpLocation[1], mFramePaint);
+            TextEntry te = mTextRects.get(i);
+            canvas.drawRect(te.bounds.left - mTmpLocation[0], te.bounds.top - mTmpLocation[1],
+                    te.bounds.right - mTmpLocation[0], te.bounds.bottom - mTmpLocation[1],
+                    mFrameNoTransformPaint);
+        }
+        for (int i=0; i<N; i++) {
+            TextEntry te = mTextRects.get(i);
+            canvas.save();
+            canvas.translate(-mTmpLocation[0], -mTmpLocation[1]);
+            canvas.concat(te.matrix);
+            canvas.drawRect(0, 0, te.bounds.right - te.bounds.left, te.bounds.bottom - te.bounds.top,
+                    mFramePaint);
+            canvas.restore();
         }
     }
 }
index 90a781c..97c1e85 100644 (file)
@@ -42,8 +42,11 @@ public class MainInteractionSession extends VoiceInteractionSession
     View mTopContent;
     View mBottomContent;
     TextView mText;
+    Button mTreeButton;
+    Button mTextButton;
     Button mStartButton;
     ImageView mScreenshot;
+    ImageView mFullScreenshot;
     Button mConfirmButton;
     Button mCompleteButton;
     Button mAbortButton;
@@ -110,9 +113,15 @@ public class MainInteractionSession extends VoiceInteractionSession
         mTopContent = mContentView.findViewById(R.id.top_content);
         mBottomContent = mContentView.findViewById(R.id.bottom_content);
         mText = (TextView)mContentView.findViewById(R.id.text);
+        mTreeButton = (Button)mContentView.findViewById(R.id.do_tree);
+        mTreeButton.setOnClickListener(this);
+        mTextButton = (Button)mContentView.findViewById(R.id.do_text);
+        mTextButton.setOnClickListener(this);
         mStartButton = (Button)mContentView.findViewById(R.id.start);
         mStartButton.setOnClickListener(this);
         mScreenshot = (ImageView)mContentView.findViewById(R.id.screenshot);
+        mScreenshot.setOnClickListener(this);
+        mFullScreenshot = (ImageView)mContentView.findViewById(R.id.full_screenshot);
         mConfirmButton = (Button)mContentView.findViewById(R.id.confirm);
         mConfirmButton.setOnClickListener(this);
         mCompleteButton = (Button)mContentView.findViewById(R.id.complete);
@@ -156,8 +165,10 @@ public class MainInteractionSession extends VoiceInteractionSession
             mScreenshot.setAdjustViewBounds(true);
             mScreenshot.setMaxWidth(screenshot.getWidth()/3);
             mScreenshot.setMaxHeight(screenshot.getHeight()/3);
+            mFullScreenshot.setImageBitmap(screenshot);
         } else {
             mScreenshot.setImageDrawable(null);
+            mFullScreenshot.setImageDrawable(null);
         }
     }
 
@@ -183,7 +194,15 @@ public class MainInteractionSession extends VoiceInteractionSession
     }
 
     public void onClick(View v) {
-        if (v == mStartButton) {
+        if (v == mTreeButton) {
+            if (mAssistVisualizer != null) {
+                mAssistVisualizer.logTree();
+            }
+        } else if (v == mTextButton) {
+            if (mAssistVisualizer != null) {
+                mAssistVisualizer.logText();
+            }
+        } else if (v == mStartButton) {
             mState = STATE_LAUNCHING;
             updateState();
             startVoiceActivity(mStartIntent);
@@ -219,9 +238,15 @@ public class MainInteractionSession extends VoiceInteractionSession
         } else if (v == mAbortButton) {
             mPendingRequest.sendAbortVoiceResult(null);
             mPendingRequest = null;
-        } else if (v== mCompleteButton) {
+        } else if (v == mCompleteButton) {
             mPendingRequest.sendCompleteVoiceResult(null);
             mPendingRequest = null;
+        } else if (v == mScreenshot) {
+            if (mFullScreenshot.getVisibility() != View.VISIBLE) {
+                mFullScreenshot.setVisibility(View.VISIBLE);
+            } else {
+                mFullScreenshot.setVisibility(View.INVISIBLE);
+            }
         }
         updateState();
     }
diff --git a/tests/WebViewTests/Android.mk b/tests/WebViewTests/Android.mk
deleted file mode 100644 (file)
index b118845..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
-LOCAL_PACKAGE_NAME := WebViewTests
-
-include $(BUILD_PACKAGE)
diff --git a/tests/WebViewTests/AndroidManifest.xml b/tests/WebViewTests/AndroidManifest.xml
deleted file mode 100644 (file)
index 8b080c1..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2011 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.webviewtests">
-    <application>
-        <uses-library android:name="android.test.runner" />
-        <activity android:name="WebViewStubActivity" android:label="WebViewStubActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.TEST" />
-            </intent-filter>
-        </activity>
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="com.android.webviewtests"
-                     android:label="Tests for android.webkit.WebView" />
-</manifest>
diff --git a/tests/WebViewTests/res/layout/webview_layout.xml b/tests/WebViewTests/res/layout/webview_layout.xml
deleted file mode 100644 (file)
index d266d21..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <WebView android:id="@+id/web_page"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-</LinearLayout>
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java
deleted file mode 100644 (file)
index c2bbdf5..0000000
+++ /dev/null
@@ -1,625 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Part of the test suite for the WebView's Java Bridge. This class tests that
- * we correctly convert JavaScript arrays to Java arrays when passing them to
- * the methods of injected Java objects.
- *
- * The conversions should follow
- * http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. Places in
- * which the implementation differs from the spec are marked with
- * LIVECONNECT_COMPLIANCE.
- * FIXME: Consider making our implementation more compliant, if it will not
- * break backwards-compatibility. See b/4408210.
- *
- * To run this test ...
- *  adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeArrayCoercionTest \
- *     com.android.webviewtests/android.test.InstrumentationTestRunner
- */
-
-package com.android.webviewtests;
-
-public class JavaBridgeArrayCoercionTest extends JavaBridgeTestBase {
-    private class TestObject extends Controller {
-        private Object mObjectInstance;
-        private CustomType mCustomTypeInstance;
-
-        private boolean[] mBooleanArray;
-        private byte[] mByteArray;
-        private char[] mCharArray;
-        private short[] mShortArray;
-        private int[] mIntArray;
-        private long[] mLongArray;
-        private float[] mFloatArray;
-        private double[] mDoubleArray;
-        private String[] mStringArray;
-        private Object[] mObjectArray;
-        private CustomType[] mCustomTypeArray;
-
-        public TestObject() {
-            mObjectInstance = new Object();
-            mCustomTypeInstance = new CustomType();
-        }
-
-        public Object getObjectInstance() {
-            return mObjectInstance;
-        }
-        public CustomType getCustomTypeInstance() {
-            return mCustomTypeInstance;
-        }
-
-        public synchronized void setBooleanArray(boolean[] x) {
-            mBooleanArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setByteArray(byte[] x) {
-            mByteArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setCharArray(char[] x) {
-            mCharArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setShortArray(short[] x) {
-            mShortArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setIntArray(int[] x) {
-            mIntArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setLongArray(long[] x) {
-            mLongArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setFloatArray(float[] x) {
-            mFloatArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setDoubleArray(double[] x) {
-            mDoubleArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setStringArray(String[] x) {
-            mStringArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setObjectArray(Object[] x) {
-            mObjectArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setCustomTypeArray(CustomType[] x) {
-            mCustomTypeArray = x;
-            notifyResultIsReady();
-        }
-
-        public synchronized boolean[] waitForBooleanArray() {
-            waitForResult();
-            return mBooleanArray;
-        }
-        public synchronized byte[] waitForByteArray() {
-            waitForResult();
-            return mByteArray;
-        }
-        public synchronized char[] waitForCharArray() {
-            waitForResult();
-            return mCharArray;
-        }
-        public synchronized short[] waitForShortArray() {
-            waitForResult();
-            return mShortArray;
-        }
-        public synchronized int[] waitForIntArray() {
-            waitForResult();
-            return mIntArray;
-        }
-        public synchronized long[] waitForLongArray() {
-            waitForResult();
-            return mLongArray;
-        }
-        public synchronized float[] waitForFloatArray() {
-            waitForResult();
-            return mFloatArray;
-        }
-        public synchronized double[] waitForDoubleArray() {
-            waitForResult();
-            return mDoubleArray;
-        }
-        public synchronized String[] waitForStringArray() {
-            waitForResult();
-            return mStringArray;
-        }
-        public synchronized Object[] waitForObjectArray() {
-            waitForResult();
-            return mObjectArray;
-        }
-        public synchronized CustomType[] waitForCustomTypeArray() {
-            waitForResult();
-            return mCustomTypeArray;
-        }
-    }
-
-    // Two custom types used when testing passing objects.
-    private class CustomType {
-    }
-
-    private TestObject mTestObject;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTestObject = new TestObject();
-        setUpWebView(mTestObject, "testObject");
-    }
-
-    // Note that all tests use a single element array for simplicity. We test
-    // multiple elements elsewhere.
-
-    // Test passing an array of JavaScript numbers in the int32 range to a
-    // method which takes a Java array.
-    public void testPassNumberInt32() throws Throwable {
-        executeJavaScript("testObject.setBooleanArray([0]);");
-        assertFalse(mTestObject.waitForBooleanArray()[0]);
-        // LIVECONNECT_COMPLIANCE: Should convert to boolean.
-        executeJavaScript("testObject.setBooleanArray([42]);");
-        assertFalse(mTestObject.waitForBooleanArray()[0]);
-
-        executeJavaScript("testObject.setByteArray([42]);");
-        assertEquals(42, mTestObject.waitForByteArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should convert to numeric char value.
-        executeJavaScript("testObject.setCharArray([42]);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
-
-        executeJavaScript("testObject.setShortArray([42]);");
-        assertEquals(42, mTestObject.waitForShortArray()[0]);
-
-        executeJavaScript("testObject.setIntArray([42]);");
-        assertEquals(42, mTestObject.waitForIntArray()[0]);
-
-        executeJavaScript("testObject.setLongArray([42]);");
-        assertEquals(42L, mTestObject.waitForLongArray()[0]);
-
-        executeJavaScript("testObject.setFloatArray([42]);");
-        assertEquals(42.0f, mTestObject.waitForFloatArray()[0]);
-
-        executeJavaScript("testObject.setDoubleArray([42]);");
-        assertEquals(42.0, mTestObject.waitForDoubleArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
-        executeJavaScript("testObject.setObjectArray([42]);");
-        assertNull(mTestObject.waitForObjectArray());
-
-        // LIVECONNECT_COMPLIANCE: Should create instances of java.lang.String.
-        executeJavaScript("testObject.setStringArray([42]);");
-        assertNull(mTestObject.waitForStringArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeArray([42]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-    }
-
-    // Test passing an array of JavaScript numbers in the double range to a
-    // method which takes a Java array.
-    public void testPassNumberDouble() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: Should convert to boolean.
-        executeJavaScript("testObject.setBooleanArray([42.1]);");
-        assertFalse(mTestObject.waitForBooleanArray()[0]);
-
-        executeJavaScript("testObject.setByteArray([42.1]);");
-        assertEquals(42, mTestObject.waitForByteArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should convert to numeric char value.
-        executeJavaScript("testObject.setCharArray([42.1]);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
-
-        executeJavaScript("testObject.setShortArray([42.1]);");
-        assertEquals(42, mTestObject.waitForShortArray()[0]);
-
-        executeJavaScript("testObject.setIntArray([42.1]);");
-        assertEquals(42, mTestObject.waitForIntArray()[0]);
-
-        executeJavaScript("testObject.setLongArray([42.1]);");
-        assertEquals(42L, mTestObject.waitForLongArray()[0]);
-
-        executeJavaScript("testObject.setFloatArray([42.1]);");
-        assertEquals(42.1f, mTestObject.waitForFloatArray()[0]);
-
-        executeJavaScript("testObject.setDoubleArray([42.1]);");
-        assertEquals(42.1, mTestObject.waitForDoubleArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
-        executeJavaScript("testObject.setObjectArray([42.1]);");
-        assertNull(mTestObject.waitForObjectArray());
-
-        // LIVECONNECT_COMPLIANCE: Should create instances of java.lang.String.
-        executeJavaScript("testObject.setStringArray([42.1]);");
-        assertNull(mTestObject.waitForStringArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeArray([42.1]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-    }
-
-    // Test passing an array of JavaScript NaN values to a method which takes a
-    // Java array.
-    public void testPassNumberNaN() throws Throwable {
-        executeJavaScript("testObject.setBooleanArray([Number.NaN]);");
-        assertFalse(mTestObject.waitForBooleanArray()[0]);
-
-        executeJavaScript("testObject.setByteArray([Number.NaN]);");
-        assertEquals(0, mTestObject.waitForByteArray()[0]);
-
-        executeJavaScript("testObject.setCharArray([Number.NaN]);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
-
-        executeJavaScript("testObject.setShortArray([Number.NaN]);");
-        assertEquals(0, mTestObject.waitForShortArray()[0]);
-
-        executeJavaScript("testObject.setIntArray([Number.NaN]);");
-        assertEquals(0, mTestObject.waitForIntArray()[0]);
-
-        executeJavaScript("testObject.setLongArray([Number.NaN]);");
-        assertEquals(0L, mTestObject.waitForLongArray()[0]);
-
-        executeJavaScript("testObject.setFloatArray([Number.NaN]);");
-        assertEquals(Float.NaN, mTestObject.waitForFloatArray()[0]);
-
-        executeJavaScript("testObject.setDoubleArray([Number.NaN]);");
-        assertEquals(Double.NaN, mTestObject.waitForDoubleArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
-        executeJavaScript("testObject.setObjectArray([Number.NaN]);");
-        assertNull(mTestObject.waitForObjectArray());
-
-        // LIVECONNECT_COMPLIANCE: Should create instances of java.lang.String.
-        executeJavaScript("testObject.setStringArray([Number.NaN]);");
-        assertNull(mTestObject.waitForStringArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeArray([Number.NaN]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-    }
-
-    // Test passing an array of JavaScript infinity values to a method which
-    // takes a Java array.
-    public void testPassNumberInfinity() throws Throwable {
-        executeJavaScript("testObject.setBooleanArray([Infinity]);");
-        assertFalse(mTestObject.waitForBooleanArray()[0]);
-
-        executeJavaScript("testObject.setByteArray([Infinity]);");
-        assertEquals(-1, mTestObject.waitForByteArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should convert to maximum numeric char value.
-        executeJavaScript("testObject.setCharArray([Infinity]);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
-
-        executeJavaScript("testObject.setShortArray([Infinity]);");
-        assertEquals(-1, mTestObject.waitForShortArray()[0]);
-
-        executeJavaScript("testObject.setIntArray([Infinity]);");
-        assertEquals(Integer.MAX_VALUE, mTestObject.waitForIntArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should be Long.MAX_VALUE.
-        executeJavaScript("testObject.setLongArray([Infinity]);");
-        assertEquals(-1L, mTestObject.waitForLongArray()[0]);
-
-        executeJavaScript("testObject.setFloatArray([Infinity]);");
-        assertEquals(Float.POSITIVE_INFINITY, mTestObject.waitForFloatArray()[0]);
-
-        executeJavaScript("testObject.setDoubleArray([Infinity]);");
-        assertEquals(Double.POSITIVE_INFINITY, mTestObject.waitForDoubleArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
-        executeJavaScript("testObject.setObjectArray([Infinity]);");
-        assertNull(mTestObject.waitForObjectArray());
-
-        // LIVECONNECT_COMPLIANCE: Should create instances of java.lang.String.
-        executeJavaScript("testObject.setStringArray([Infinity]);");
-        assertNull(mTestObject.waitForStringArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeArray([Infinity]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-    }
-
-    // Test passing an array of JavaScript boolean values to a method which
-    // takes a Java array.
-    public void testPassBoolean() throws Throwable {
-        executeJavaScript("testObject.setBooleanArray([true]);");
-        assertTrue(mTestObject.waitForBooleanArray()[0]);
-        executeJavaScript("testObject.setBooleanArray([false]);");
-        assertFalse(mTestObject.waitForBooleanArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.
-        executeJavaScript("testObject.setByteArray([true]);");
-        assertEquals(0, mTestObject.waitForByteArray()[0]);
-        executeJavaScript("testObject.setByteArray([false]);");
-        assertEquals(0, mTestObject.waitForByteArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should convert to numeric char value 1.
-        executeJavaScript("testObject.setCharArray([true]);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
-        executeJavaScript("testObject.setCharArray([false]);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.
-        executeJavaScript("testObject.setShortArray([true]);");
-        assertEquals(0, mTestObject.waitForShortArray()[0]);
-        executeJavaScript("testObject.setShortArray([false]);");
-        assertEquals(0, mTestObject.waitForShortArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.
-        executeJavaScript("testObject.setIntArray([true]);");
-        assertEquals(0, mTestObject.waitForIntArray()[0]);
-        executeJavaScript("testObject.setIntArray([false]);");
-        assertEquals(0, mTestObject.waitForIntArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.
-        executeJavaScript("testObject.setLongArray([true]);");
-        assertEquals(0L, mTestObject.waitForLongArray()[0]);
-        executeJavaScript("testObject.setLongArray([false]);");
-        assertEquals(0L, mTestObject.waitForLongArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.0.
-        executeJavaScript("testObject.setFloatArray([true]);");
-        assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
-        executeJavaScript("testObject.setFloatArray([false]);");
-        assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.0.
-        executeJavaScript("testObject.setDoubleArray([true]);");
-        assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
-        executeJavaScript("testObject.setDoubleArray([false]);");
-        assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
-        executeJavaScript("testObject.setObjectArray([true]);");
-        assertNull(mTestObject.waitForObjectArray());
-
-        // LIVECONNECT_COMPLIANCE: Should create instances of java.lang.String.
-        executeJavaScript("testObject.setStringArray([true]);");
-        assertNull(mTestObject.waitForStringArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeArray([true]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-    }
-
-    // Test passing an array of JavaScript strings to a method which takes a
-    // Java array.
-    public void testPassString() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: Non-empty string should convert to true.
-        executeJavaScript("testObject.setBooleanArray([\"+042.10\"]);");
-        assertFalse(mTestObject.waitForBooleanArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setByteArray([\"+042.10\"]);");
-        assertEquals(0, mTestObject.waitForByteArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should decode and convert to numeric char value.
-        executeJavaScript("testObject.setCharArray([\"+042.10\"]);");
-        assertEquals(0, mTestObject.waitForCharArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setShortArray([\"+042.10\"]);");
-        assertEquals(0, mTestObject.waitForShortArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setIntArray([\"+042.10\"]);");
-        assertEquals(0, mTestObject.waitForIntArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setLongArray([\"+042.10\"]);");
-        assertEquals(0L, mTestObject.waitForLongArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setFloatArray([\"+042.10\"]);");
-        assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setDoubleArray([\"+042.10\"]);");
-        assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and create instances of java.lang.Number.
-        executeJavaScript("testObject.setObjectArray([\"+042.10\"]);");
-        assertNull(mTestObject.waitForObjectArray());
-
-        executeJavaScript("testObject.setStringArray([\"+042.10\"]);");
-        assertEquals("+042.10", mTestObject.waitForStringArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeArray([\"+042.10\"]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-    }
-
-    // Test passing an array of JavaScript objects to a method which takes a
-    // Java array.
-    public void testPassJavaScriptObject() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setBooleanArray([{foo: 42}]);");
-        assertFalse(mTestObject.waitForBooleanArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setByteArray([{foo: 42}]);");
-        assertEquals(0, mTestObject.waitForByteArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCharArray([{foo: 42}]);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setShortArray([{foo: 42}]);");
-        assertEquals(0, mTestObject.waitForShortArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setIntArray([{foo: 42}]);");
-        assertEquals(0, mTestObject.waitForIntArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setLongArray([{foo: 42}]);");
-        assertEquals(0L, mTestObject.waitForLongArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setFloatArray([{foo: 42}]);");
-        assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setDoubleArray([{foo: 42}]);");
-        assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setObjectArray([{foo: 42}]);");
-        assertNull(mTestObject.waitForObjectArray());
-
-        // LIVECONNECT_COMPLIANCE: Should call toString() on object.
-        executeJavaScript("testObject.setStringArray([{foo: 42}]);");
-        assertNull(mTestObject.waitForStringArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeArray([{foo: 42}]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-    }
-
-    // Test passing an array of Java objects to a method which takes a Java
-    // array.
-    public void testPassJavaObject() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setBooleanArray([testObject.getObjectInstance()]);");
-        assertFalse(mTestObject.waitForBooleanArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setByteArray([testObject.getObjectInstance()]);");
-        assertEquals(0, mTestObject.waitForByteArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCharArray([testObject.getObjectInstance()]);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setShortArray([testObject.getObjectInstance()]);");
-        assertEquals(0, mTestObject.waitForShortArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setIntArray([testObject.getObjectInstance()]);");
-        assertEquals(0, mTestObject.waitForIntArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setLongArray([testObject.getObjectInstance()]);");
-        assertEquals(0L, mTestObject.waitForLongArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setFloatArray([testObject.getObjectInstance()]);");
-        assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setDoubleArray([testObject.getObjectInstance()]);");
-        assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create an array and pass Java object.
-        executeJavaScript("testObject.setObjectArray([testObject.getObjectInstance()]);");
-        assertNull(mTestObject.waitForObjectArray());
-
-        // LIVECONNECT_COMPLIANCE: Should call toString() on object.
-        executeJavaScript("testObject.setStringArray([testObject.getObjectInstance()]);");
-        assertNull(mTestObject.waitForStringArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and pass Java object.
-        executeJavaScript("testObject.setCustomTypeArray([testObject.getObjectInstance()]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-        executeJavaScript("testObject.setCustomTypeArray([testObject.getCustomTypeInstance()]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-    }
-
-    // Test passing an array of JavaScript null values to a method which takes
-    // a Java array.
-    public void testPassNull() throws Throwable {
-        executeJavaScript("testObject.setByteArray([null]);");
-        assertEquals(0, mTestObject.waitForByteArray()[0]);
-
-        executeJavaScript("testObject.setCharArray([null]);");
-        assertEquals('\u0000', mTestObject.waitForCharArray()[0]);
-
-        executeJavaScript("testObject.setShortArray([null]);");
-        assertEquals(0, mTestObject.waitForShortArray()[0]);
-
-        executeJavaScript("testObject.setIntArray([null]);");
-        assertEquals(0, mTestObject.waitForIntArray()[0]);
-
-        executeJavaScript("testObject.setLongArray([null]);");
-        assertEquals(0L, mTestObject.waitForLongArray()[0]);
-
-        executeJavaScript("testObject.setFloatArray([null]);");
-        assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
-
-        executeJavaScript("testObject.setDoubleArray([null]);");
-        assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
-
-        executeJavaScript("testObject.setBooleanArray([null]);");
-        assertFalse(mTestObject.waitForBooleanArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and pass null.
-        executeJavaScript("testObject.setObjectArray([null]);");
-        assertNull(mTestObject.waitForObjectArray());
-
-        executeJavaScript("testObject.setStringArray([null]);");
-        assertNull(mTestObject.waitForStringArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and pass null.
-        executeJavaScript("testObject.setCustomTypeArray([null]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-    }
-
-    // Test passing an array of JavaScript undefined values to a method which
-    // takes a Java array.
-    public void testPassUndefined() throws Throwable {
-        executeJavaScript("testObject.setByteArray([undefined]);");
-        assertEquals(0, mTestObject.waitForByteArray()[0]);
-
-        executeJavaScript("testObject.setCharArray([undefined]);");
-        assertEquals(0, mTestObject.waitForCharArray()[0]);
-
-        executeJavaScript("testObject.setShortArray([undefined]);");
-        assertEquals(0, mTestObject.waitForShortArray()[0]);
-
-        executeJavaScript("testObject.setIntArray([undefined]);");
-        assertEquals(0, mTestObject.waitForIntArray()[0]);
-
-        executeJavaScript("testObject.setLongArray([undefined]);");
-        assertEquals(0L, mTestObject.waitForLongArray()[0]);
-
-        executeJavaScript("testObject.setFloatArray([undefined]);");
-        assertEquals(0.0f, mTestObject.waitForFloatArray()[0]);
-
-        executeJavaScript("testObject.setDoubleArray([undefined]);");
-        assertEquals(0.0, mTestObject.waitForDoubleArray()[0]);
-
-        executeJavaScript("testObject.setBooleanArray([undefined]);");
-        assertEquals(false, mTestObject.waitForBooleanArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and pass null.
-        executeJavaScript("testObject.setObjectArray([undefined]);");
-        assertNull(mTestObject.waitForObjectArray());
-
-        executeJavaScript("testObject.setStringArray([undefined]);");
-        assertNull(mTestObject.waitForStringArray()[0]);
-
-        // LIVECONNECT_COMPLIANCE: Should create array and pass null.
-        executeJavaScript("testObject.setCustomTypeArray([undefined]);");
-        assertNull(mTestObject.waitForCustomTypeArray());
-    }
-}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java
deleted file mode 100644 (file)
index 2fd42a7..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Part of the test suite for the WebView's Java Bridge. This class tests the
- * general use of arrays.
- *
- * The conversions should follow
- * http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. Places in
- * which the implementation differs from the spec are marked with
- * LIVECONNECT_COMPLIANCE.
- * FIXME: Consider making our implementation more compliant, if it will not
- * break backwards-compatibility. See b/4408210.
- *
- * To run this test ...
- *  adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeArrayTest \
- *     com.android.webviewtests/android.test.InstrumentationTestRunner
- */
-
-package com.android.webviewtests;
-
-public class JavaBridgeArrayTest extends JavaBridgeTestBase {
-    private class TestObject extends Controller {
-        private boolean mBooleanValue;
-        private int mIntValue;
-        private String mStringValue;
-
-        private int[] mIntArray;
-        private int[][] mIntIntArray;
-
-        private boolean mWasArrayMethodCalled;
-
-        public synchronized void setBooleanValue(boolean x) {
-            mBooleanValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setIntValue(int x) {
-            mIntValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setStringValue(String x) {
-            mStringValue = x;
-            notifyResultIsReady();
-        }
-
-        public synchronized boolean waitForBooleanValue() {
-            waitForResult();
-            return mBooleanValue;
-        }
-        public synchronized int waitForIntValue() {
-            waitForResult();
-            return mIntValue;
-        }
-        public synchronized String waitForStringValue() {
-            waitForResult();
-            return mStringValue;
-        }
-
-        public synchronized void setIntArray(int[] x) {
-            mIntArray = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setIntIntArray(int[][] x) {
-            mIntIntArray = x;
-            notifyResultIsReady();
-        }
-
-        public synchronized int[] waitForIntArray() {
-            waitForResult();
-            return mIntArray;
-        }
-        public synchronized int[][] waitForIntIntArray() {
-            waitForResult();
-            return mIntIntArray;
-        }
-
-        public synchronized int[] arrayMethod() {
-            mWasArrayMethodCalled = true;
-            return new int[] {42, 43, 44};
-        }
-
-        public synchronized boolean wasArrayMethodCalled() {
-            return mWasArrayMethodCalled;
-        }
-    }
-
-    private TestObject mTestObject;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTestObject = new TestObject();
-        setUpWebView(mTestObject, "testObject");
-    }
-
-    public void testArrayLength() throws Throwable {
-        executeJavaScript("testObject.setIntArray([42, 43, 44]);");
-        int[] result = mTestObject.waitForIntArray();
-        assertEquals(3, result.length);
-        assertEquals(42, result[0]);
-        assertEquals(43, result[1]);
-        assertEquals(44, result[2]);
-    }
-
-    public void testPassNull() throws Throwable {
-        executeJavaScript("testObject.setIntArray(null);");
-        assertNull(mTestObject.waitForIntArray());
-    }
-
-    public void testPassUndefined() throws Throwable {
-        executeJavaScript("testObject.setIntArray(undefined);");
-        assertNull(mTestObject.waitForIntArray());
-    }
-
-    public void testPassEmptyArray() throws Throwable {
-        executeJavaScript("testObject.setIntArray([]);");
-        assertEquals(0, mTestObject.waitForIntArray().length);
-    }
-
-    // Note that this requires being able to pass a string from JavaScript to
-    // Java.
-    public void testPassArrayToStringMethod() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: Should call toString() on array.
-        executeJavaScript("testObject.setStringValue([42, 42, 42]);");
-        assertEquals("undefined", mTestObject.waitForStringValue());
-    }
-
-    // Note that this requires being able to pass an integer from JavaScript to
-    // Java.
-    public void testPassArrayToNonStringNonArrayMethod() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: Should raise JavaScript exception.
-        executeJavaScript("testObject.setIntValue([42, 42, 42]);");
-        assertEquals(0, mTestObject.waitForIntValue());
-    }
-
-    public void testPassNonArrayToArrayMethod() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: Should raise JavaScript exception.
-        executeJavaScript("testObject.setIntArray(42);");
-        assertNull(mTestObject.waitForIntArray());
-    }
-
-    public void testObjectWithLengthProperty() throws Throwable {
-        executeJavaScript("testObject.setIntArray({length: 3, 1: 42});");
-        int[] result = mTestObject.waitForIntArray();
-        assertEquals(3, result.length);
-        assertEquals(0, result[0]);
-        assertEquals(42, result[1]);
-        assertEquals(0, result[2]);
-    }
-
-    public void testNonNumericLengthProperty() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: This should not count as an array, so we
-        // should raise a JavaScript exception.
-        executeJavaScript("testObject.setIntArray({length: \"foo\"});");
-        assertNull(mTestObject.waitForIntArray());
-    }
-
-    public void testLengthOutOfBounds() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: This should not count as an array, so we
-        // should raise a JavaScript exception.
-        executeJavaScript("testObject.setIntArray({length: -1});");
-        assertNull(mTestObject.waitForIntArray());
-
-        // LIVECONNECT_COMPLIANCE: This should not count as an array, so we
-        // should raise a JavaScript exception.
-        long length = (long)Integer.MAX_VALUE + 1L;
-        executeJavaScript("testObject.setIntArray({length: " + length + "});");
-        assertNull(mTestObject.waitForIntArray());
-
-        // LIVECONNECT_COMPLIANCE: This should not count as an array, so we
-        // should raise a JavaScript exception.
-        length = (long)Integer.MAX_VALUE + 1L - (long)Integer.MIN_VALUE + 1L;
-        executeJavaScript("testObject.setIntArray({length: " + length + "});");
-        assertNull(mTestObject.waitForIntArray());
-    }
-
-    public void testSparseArray() throws Throwable {
-        executeJavaScript("var x = [42, 43]; x[3] = 45; testObject.setIntArray(x);");
-        int[] result = mTestObject.waitForIntArray();
-        assertEquals(4, result.length);
-        assertEquals(42, result[0]);
-        assertEquals(43, result[1]);
-        assertEquals(0, result[2]);
-        assertEquals(45, result[3]);
-    }
-
-    // Note that this requires being able to pass a boolean from JavaScript to
-    // Java.
-    public void testMethodReturningArrayNotCalled() throws Throwable {
-        // We don't invoke methods which return arrays, but note that no
-        // exception is raised.
-        // LIVECONNECT_COMPLIANCE: Should call method and convert result to
-        // JavaScript array.
-        executeJavaScript("testObject.setBooleanValue(undefined === testObject.arrayMethod())");
-        assertTrue(mTestObject.waitForBooleanValue());
-        assertFalse(mTestObject.wasArrayMethodCalled());
-    }
-
-    public void testMultiDimensionalArrayMethod() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: Should handle multi-dimensional arrays.
-        executeJavaScript("testObject.setIntIntArray([ [42, 43], [44, 45] ]);");
-        assertNull(mTestObject.waitForIntIntArray());
-    }
-
-    public void testPassMultiDimensionalArray() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: Should handle multi-dimensional arrays.
-        executeJavaScript("testObject.setIntArray([ [42, 43], [44, 45] ]);");
-        int[] result = mTestObject.waitForIntArray();
-        assertEquals(2, result.length);
-        assertEquals(0, result[0]);
-        assertEquals(0, result[1]);
-    }
-}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java
deleted file mode 100644 (file)
index 1ecccf6..0000000
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Part of the test suite for the WebView's Java Bridge. Tests a number of features including ...
- * - The type of injected objects
- * - The type of their methods
- * - Replacing objects
- * - Removing objects
- * - Access control
- * - Calling methods on returned objects
- * - Multiply injected objects
- * - Threading
- * - Inheritance
- *
- * To run this test ...
- *  adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeBasicsTest \
- *     com.android.webviewtests/android.test.InstrumentationTestRunner
- */
-
-package com.android.webviewtests;
-
-public class JavaBridgeBasicsTest extends JavaBridgeTestBase {
-    private class TestController extends Controller {
-        private int mIntValue;
-        private long mLongValue;
-        private String mStringValue;
-        private boolean mBooleanValue;
-
-        public synchronized void setIntValue(int x) {
-            mIntValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setLongValue(long x) {
-            mLongValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setStringValue(String x) {
-            mStringValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setBooleanValue(boolean x) {
-            mBooleanValue = x;
-            notifyResultIsReady();
-        }
-
-        public synchronized int waitForIntValue() {
-            waitForResult();
-            return mIntValue;
-        }
-        public synchronized long waitForLongValue() {
-            waitForResult();
-            return mLongValue;
-        }
-        public synchronized String waitForStringValue() {
-            waitForResult();
-            return mStringValue;
-        }
-        public synchronized boolean waitForBooleanValue() {
-            waitForResult();
-            return mBooleanValue;
-        }
-    }
-
-    private static class ObjectWithStaticMethod {
-        public static String staticMethod() {
-            return "foo";
-        }
-    }
-
-    TestController mTestController;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTestController = new TestController();
-        setUpWebView(mTestController, "testController");
-    }
-
-    // Note that this requires that we can pass a JavaScript string to Java.
-    protected String executeJavaScriptAndGetStringResult(String script) throws Throwable {
-        executeJavaScript("testController.setStringValue(" + script + ");");
-        return mTestController.waitForStringValue();
-    }
-
-    protected void injectObjectAndReload(final Object object, final String name) throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getWebView().addJavascriptInterface(object, name);
-                getWebView().reload();
-            }
-        });
-        mWebViewClient.waitForOnPageFinished();
-    }
-
-    // Note that this requires that we can pass a JavaScript boolean to Java.
-    private void assertRaisesException(String script) throws Throwable {
-        executeJavaScript("try {" +
-                          script + ";" +
-                          "  testController.setBooleanValue(false);" +
-                          "} catch (exception) {" +
-                          "  testController.setBooleanValue(true);" +
-                          "}");
-        assertTrue(mTestController.waitForBooleanValue());
-    }
-
-    public void testTypeOfInjectedObject() throws Throwable {
-        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
-    }
-
-    public void testAdditionNotReflectedUntilReload() throws Throwable {
-        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getWebView().addJavascriptInterface(new Object(), "testObject");
-            }
-        });
-        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getWebView().reload();
-            }
-        });
-        mWebViewClient.waitForOnPageFinished();
-        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
-    }
-
-    public void testRemovalNotReflectedUntilReload() throws Throwable {
-        injectObjectAndReload(new Object(), "testObject");
-        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getWebView().removeJavascriptInterface("testObject");
-            }
-        });
-        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getWebView().reload();
-            }
-        });
-        mWebViewClient.waitForOnPageFinished();
-        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
-    }
-
-    public void testRemoveObjectNotAdded() throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getWebView().removeJavascriptInterface("foo");
-                getWebView().reload();
-            }
-        });
-        mWebViewClient.waitForOnPageFinished();
-        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof foo"));
-    }
-
-    public void testTypeOfMethod() throws Throwable {
-        assertEquals("function",
-                executeJavaScriptAndGetStringResult("typeof testController.setStringValue"));
-    }
-
-    public void testTypeOfInvalidMethod() throws Throwable {
-        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testController.foo"));
-    }
-
-    public void testCallingInvalidMethodRaisesException() throws Throwable {
-        assertRaisesException("testController.foo()");
-    }
-
-    public void testUncaughtJavaExceptionRaisesJavaException() throws Throwable {
-        injectObjectAndReload(new Object() {
-            public void method() { throw new RuntimeException("foo"); }
-        }, "testObject");
-        assertRaisesException("testObject.method()");
-    }
-
-    // Note that this requires that we can pass a JavaScript string to Java.
-    public void testTypeOfStaticMethod() throws Throwable {
-        injectObjectAndReload(new ObjectWithStaticMethod(), "testObject");
-        executeJavaScript("testController.setStringValue(typeof testObject.staticMethod)");
-        assertEquals("function", mTestController.waitForStringValue());
-    }
-
-    // Note that this requires that we can pass a JavaScript string to Java.
-    public void testCallStaticMethod() throws Throwable {
-        injectObjectAndReload(new ObjectWithStaticMethod(), "testObject");
-        executeJavaScript("testController.setStringValue(testObject.staticMethod())");
-        assertEquals("foo", mTestController.waitForStringValue());
-    }
-
-    public void testPrivateMethodNotExposed() throws Throwable {
-        injectObjectAndReload(new Object() {
-            private void method() {}
-        }, "testObject");
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.method"));
-    }
-
-    public void testReplaceInjectedObject() throws Throwable {
-        injectObjectAndReload(new Object() {
-            public void method() { mTestController.setStringValue("object 1"); }
-        }, "testObject");
-        executeJavaScript("testObject.method()");
-        assertEquals("object 1", mTestController.waitForStringValue());
-
-        injectObjectAndReload(new Object() {
-            public void method() { mTestController.setStringValue("object 2"); }
-        }, "testObject");
-        executeJavaScript("testObject.method()");
-        assertEquals("object 2", mTestController.waitForStringValue());
-    }
-
-    public void testInjectNullObjectIsIgnored() throws Throwable {
-        injectObjectAndReload(null, "testObject");
-        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject"));
-    }
-
-    public void testReplaceInjectedObjectWithNullObjectIsIgnored() throws Throwable {
-        injectObjectAndReload(new Object(), "testObject");
-        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
-        injectObjectAndReload(null, "testObject");
-        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testObject"));
-    }
-
-    public void testCallOverloadedMethodWithDifferentNumberOfArguments() throws Throwable {
-        injectObjectAndReload(new Object() {
-            public void method() { mTestController.setStringValue("0 args"); }
-            public void method(int x) { mTestController.setStringValue("1 arg"); }
-            public void method(int x, int y) { mTestController.setStringValue("2 args"); }
-        }, "testObject");
-        executeJavaScript("testObject.method()");
-        assertEquals("0 args", mTestController.waitForStringValue());
-        executeJavaScript("testObject.method(42)");
-        assertEquals("1 arg", mTestController.waitForStringValue());
-        executeJavaScript("testObject.method(null)");
-        assertEquals("1 arg", mTestController.waitForStringValue());
-        executeJavaScript("testObject.method(undefined)");
-        assertEquals("1 arg", mTestController.waitForStringValue());
-        executeJavaScript("testObject.method(42, 42)");
-        assertEquals("2 args", mTestController.waitForStringValue());
-    }
-
-    public void testCallMethodWithWrongNumberOfArgumentsRaisesException() throws Throwable {
-        assertRaisesException("testController.setIntValue()");
-        assertRaisesException("testController.setIntValue(42, 42)");
-    }
-
-    public void testObjectPersistsAcrossPageLoads() throws Throwable {
-        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getWebView().reload();
-            }
-        });
-        mWebViewClient.waitForOnPageFinished();
-        assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController"));
-    }
-
-    public void testSameObjectInjectedMultipleTimes() throws Throwable {
-        class TestObject {
-            private int mNumMethodInvocations;
-            public void method() { mTestController.setIntValue(++mNumMethodInvocations); }
-        }
-        final TestObject testObject = new TestObject();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getWebView().addJavascriptInterface(testObject, "testObject1");
-                getWebView().addJavascriptInterface(testObject, "testObject2");
-                getWebView().reload();
-            }
-        });
-        mWebViewClient.waitForOnPageFinished();
-        executeJavaScript("testObject1.method()");
-        assertEquals(1, mTestController.waitForIntValue());
-        executeJavaScript("testObject2.method()");
-        assertEquals(2, mTestController.waitForIntValue());
-    }
-
-    public void testCallMethodOnReturnedObject() throws Throwable {
-        injectObjectAndReload(new Object() {
-            public Object getInnerObject() {
-                return new Object() {
-                    public void method(int x) { mTestController.setIntValue(x); }
-                };
-            }
-        }, "testObject");
-        executeJavaScript("testObject.getInnerObject().method(42)");
-        assertEquals(42, mTestController.waitForIntValue());
-    }
-
-    public void testReturnedObjectInjectedElsewhere() throws Throwable {
-        class InnerObject {
-            private int mNumMethodInvocations;
-            public void method() { mTestController.setIntValue(++mNumMethodInvocations); }
-        }
-        final InnerObject innerObject = new InnerObject();
-        final Object object = new Object() {
-            public InnerObject getInnerObject() {
-                return innerObject;
-            }
-        };
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                getWebView().addJavascriptInterface(object, "testObject");
-                getWebView().addJavascriptInterface(innerObject, "innerObject");
-                getWebView().reload();
-            }
-        });
-        mWebViewClient.waitForOnPageFinished();
-        executeJavaScript("testObject.getInnerObject().method()");
-        assertEquals(1, mTestController.waitForIntValue());
-        executeJavaScript("innerObject.method()");
-        assertEquals(2, mTestController.waitForIntValue());
-    }
-
-    public void testMethodInvokedOnBackgroundThread() throws Throwable {
-        injectObjectAndReload(new Object() {
-            public void captureThreadId() {
-                mTestController.setLongValue(Thread.currentThread().getId());
-            }
-        }, "testObject");
-        executeJavaScript("testObject.captureThreadId()");
-        final long threadId = mTestController.waitForLongValue();
-        assertFalse(threadId == Thread.currentThread().getId());
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                assertFalse(threadId == Thread.currentThread().getId());
-            }
-        });
-    }
-
-    public void testPublicInheritedMethod() throws Throwable {
-        class Base {
-            public void method(int x) { mTestController.setIntValue(x); }
-        }
-        class Derived extends Base {
-        }
-        injectObjectAndReload(new Derived(), "testObject");
-        assertEquals("function", executeJavaScriptAndGetStringResult("typeof testObject.method"));
-        executeJavaScript("testObject.method(42)");
-        assertEquals(42, mTestController.waitForIntValue());
-    }
-
-    public void testPrivateInheritedMethod() throws Throwable {
-        class Base {
-            private void method() {}
-        }
-        class Derived extends Base {
-        }
-        injectObjectAndReload(new Derived(), "testObject");
-        assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testObject.method"));
-    }
-
-    public void testOverriddenMethod() throws Throwable {
-        class Base {
-            public void method() { mTestController.setStringValue("base"); }
-        }
-        class Derived extends Base {
-            public void method() { mTestController.setStringValue("derived"); }
-        }
-        injectObjectAndReload(new Derived(), "testObject");
-        executeJavaScript("testObject.method()");
-        assertEquals("derived", mTestController.waitForStringValue());
-    }
-
-    public void testEnumerateMembers() throws Throwable {
-        injectObjectAndReload(new Object() {
-            public void method() {}
-            private void privateMethod() {}
-            public int field;
-            private int privateField;
-        }, "testObject");
-        executeJavaScript(
-                "var result = \"\"; " +
-                "for (x in testObject) { result += \" \" + x } " +
-                "testController.setStringValue(result);");
-        // LIVECONNECT_COMPLIANCE: Should be able to enumerate members.
-        assertEquals("", mTestController.waitForStringValue());
-    }
-
-    public void testReflectPublicMethod() throws Throwable {
-        injectObjectAndReload(new Object() {
-            public String method() { return "foo"; }
-        }, "testObject");
-        assertEquals("foo", executeJavaScriptAndGetStringResult(
-                "testObject.getClass().getMethod('method', null).invoke(testObject, null)" +
-                ".toString()"));
-    }
-
-    public void testReflectPublicField() throws Throwable {
-        injectObjectAndReload(new Object() {
-            public String field = "foo";
-        }, "testObject");
-        assertEquals("foo", executeJavaScriptAndGetStringResult(
-                "testObject.getClass().getField('field').get(testObject).toString()"));
-    }
-
-    public void testReflectPrivateMethodRaisesException() throws Throwable {
-        injectObjectAndReload(new Object() {
-            private void method() {};
-        }, "testObject");
-        assertRaisesException("testObject.getClass().getMethod('method', null)");
-        // getDeclaredMethod() is able to access a private method, but invoke()
-        // throws a Java exception.
-        assertRaisesException(
-                "testObject.getClass().getDeclaredMethod('method', null).invoke(testObject, null)");
-    }
-
-    public void testReflectPrivateFieldRaisesException() throws Throwable {
-        injectObjectAndReload(new Object() {
-            private int field;
-        }, "testObject");
-        assertRaisesException("testObject.getClass().getField('field')");
-        // getDeclaredField() is able to access a private field, but getInt()
-        // throws a Java exception.
-        assertRaisesException(
-                "testObject.getClass().getDeclaredField('field').getInt(testObject)");
-    }
-}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeChildFrameTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeChildFrameTest.java
deleted file mode 100644 (file)
index 3f0e2b3..0000000
+++ /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.
- */
-
-/**
- * Part of the test suite for the WebView's Java Bridge.
- *
- * Ensures that injected objects are exposed to child frames as well as the
- * main frame.
- *
- * To run this test ...
- *  adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeChildFrameTest \
- *          com.android.webviewtests/android.test.InstrumentationTestRunner
- */
-
-package com.android.webviewtests;
-
-public class JavaBridgeChildFrameTest extends JavaBridgeTestBase {
-    private class TestController extends Controller {
-        private String mStringValue;
-
-       public synchronized void setStringValue(String x) {
-            mStringValue = x;
-            notifyResultIsReady();
-        }
-       public synchronized String waitForStringValue() {
-            waitForResult();
-            return mStringValue;
-        }
-    }
-
-    TestController mTestController;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTestController = new TestController();
-        setUpWebView(mTestController, "testController");
-    }
-
-    public void testInjectedObjectPresentInChildFrame() throws Throwable {
-        // In the case that the test fails (i.e. the child frame doesn't get the injected object,
-        // the call to testController.setStringValue in the child frame's onload handler will
-        // not be made.
-        getActivity().getWebView().loadData(
-                "<html><head></head><body>" +
-                "<iframe id=\"childFrame\" onload=\"testController.setStringValue('PASS');\" />" +
-                "</body></html>", "text/html", null);
-        assertEquals("PASS", mTestController.waitForStringValue());
-    }
-}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java
deleted file mode 100644 (file)
index a0f78a4..0000000
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Part of the test suite for the WebView's Java Bridge. This class tests that
- * we correctly convert JavaScript values to Java values when passing them to
- * the methods of injected Java objects.
- *
- * The conversions should follow
- * http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. Places in
- * which the implementation differs from the spec are marked with
- * LIVECONNECT_COMPLIANCE.
- * FIXME: Consider making our implementation more compliant, if it will not
- * break backwards-compatibility. See b/4408210.
- *
- * To run this test ...
- *  adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeCoercionTest \
- *     com.android.webviewtests/android.test.InstrumentationTestRunner
- */
-
-package com.android.webviewtests;
-
-public class JavaBridgeCoercionTest extends JavaBridgeTestBase {
-    private class TestObject extends Controller {
-        private Object objectInstance;
-        private CustomType customTypeInstance;
-        private CustomType2 customType2Instance;
-
-        private boolean mBooleanValue;
-        private byte mByteValue;
-        private char mCharValue;
-        private short mShortValue;
-        private int mIntValue;
-        private long mLongValue;
-        private float mFloatValue;
-        private double mDoubleValue;
-        private String mStringValue;
-        private Object mObjectValue;
-        private CustomType mCustomTypeValue;
-
-        public TestObject() {
-            objectInstance = new Object();
-            customTypeInstance = new CustomType();
-            customType2Instance = new CustomType2();
-        }
-
-        public Object getObjectInstance() {
-            return objectInstance;
-        }
-        public CustomType getCustomTypeInstance() {
-            return customTypeInstance;
-        }
-        public CustomType2 getCustomType2Instance() {
-            return customType2Instance;
-        }
-
-        public synchronized void setBooleanValue(boolean x) {
-            mBooleanValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setByteValue(byte x) {
-            mByteValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setCharValue(char x) {
-            mCharValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setShortValue(short x) {
-            mShortValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setIntValue(int x) {
-            mIntValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setLongValue(long x) {
-            mLongValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setFloatValue(float x) {
-            mFloatValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setDoubleValue(double x) {
-            mDoubleValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setStringValue(String x) {
-            mStringValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setObjectValue(Object x) {
-            mObjectValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized void setCustomTypeValue(CustomType x) {
-            mCustomTypeValue = x;
-            notifyResultIsReady();
-        }
-
-        public synchronized boolean waitForBooleanValue() {
-            waitForResult();
-            return mBooleanValue;
-        }
-        public synchronized byte waitForByteValue() {
-            waitForResult();
-            return mByteValue;
-        }
-        public synchronized char waitForCharValue() {
-            waitForResult();
-            return mCharValue;
-        }
-        public synchronized short waitForShortValue() {
-            waitForResult();
-            return mShortValue;
-        }
-        public synchronized int waitForIntValue() {
-            waitForResult();
-            return mIntValue;
-        }
-        public synchronized long waitForLongValue() {
-            waitForResult();
-            return mLongValue;
-        }
-        public synchronized float waitForFloatValue() {
-            waitForResult();
-            return mFloatValue;
-        }
-        public synchronized double waitForDoubleValue() {
-            waitForResult();
-            return mDoubleValue;
-        }
-        public synchronized String waitForStringValue() {
-            waitForResult();
-            return mStringValue;
-        }
-        public synchronized Object waitForObjectValue() {
-            waitForResult();
-            return mObjectValue;
-        }
-        public synchronized CustomType waitForCustomTypeValue() {
-            waitForResult();
-            return mCustomTypeValue;
-        }
-    }
-
-    // Two custom types used when testing passing objects.
-    private static class CustomType {
-    }
-    private static class CustomType2 {
-    }
-
-    private TestObject mTestObject;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTestObject = new TestObject();
-        setUpWebView(mTestObject, "testObject");
-    }
-
-    // Test passing a JavaScript number in the int32 range to a method of an
-    // injected object.
-    public void testPassNumberInt32() throws Throwable {
-        executeJavaScript("testObject.setByteValue(42);");
-        assertEquals(42, mTestObject.waitForByteValue());
-        executeJavaScript("testObject.setByteValue(" + Byte.MAX_VALUE + " + 42);");
-        assertEquals(Byte.MIN_VALUE + 42 - 1, mTestObject.waitForByteValue());
-
-        // LIVECONNECT_COMPLIANCE: Should convert to numeric char value.
-        executeJavaScript("testObject.setCharValue(42);");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-
-        executeJavaScript("testObject.setShortValue(42);");
-        assertEquals(42, mTestObject.waitForShortValue());
-        executeJavaScript("testObject.setShortValue(" + Short.MAX_VALUE + " + 42);");
-        assertEquals(Short.MIN_VALUE + 42 - 1, mTestObject.waitForShortValue());
-
-        executeJavaScript("testObject.setIntValue(42);");
-        assertEquals(42, mTestObject.waitForIntValue());
-
-        executeJavaScript("testObject.setLongValue(42);");
-        assertEquals(42L, mTestObject.waitForLongValue());
-
-        executeJavaScript("testObject.setFloatValue(42);");
-        assertEquals(42.0f, mTestObject.waitForFloatValue());
-
-        executeJavaScript("testObject.setDoubleValue(42);");
-        assertEquals(42.0, mTestObject.waitForDoubleValue());
-
-        // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.Number.
-        executeJavaScript("testObject.setObjectValue(42);");
-        assertNull(mTestObject.waitForObjectValue());
-
-        // The spec allows the JS engine flexibility in how to format the number.
-        executeJavaScript("testObject.setStringValue(42);");
-        String str = mTestObject.waitForStringValue();
-        assertTrue("42".equals(str) || "42.0".equals(str));
-
-        executeJavaScript("testObject.setBooleanValue(0);");
-        assertFalse(mTestObject.waitForBooleanValue());
-        // LIVECONNECT_COMPLIANCE: Should be true;
-        executeJavaScript("testObject.setBooleanValue(42);");
-        assertFalse(mTestObject.waitForBooleanValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeValue(42);");
-        assertNull(mTestObject.waitForCustomTypeValue());
-    }
-
-    // Test passing a JavaScript number in the double range to a method of an
-    // injected object.
-    public void testPassNumberDouble() throws Throwable {
-        executeJavaScript("testObject.setByteValue(42.1);");
-        assertEquals(42, mTestObject.waitForByteValue());
-        executeJavaScript("testObject.setByteValue(" + Byte.MAX_VALUE + " + 42.1);");
-        assertEquals(Byte.MIN_VALUE + 42 - 1, mTestObject.waitForByteValue());
-        executeJavaScript("testObject.setByteValue(" + Integer.MAX_VALUE + " + 42.1);");
-        assertEquals(-1, mTestObject.waitForByteValue());
-
-        // LIVECONNECT_COMPLIANCE: Should convert to numeric char value.
-        executeJavaScript("testObject.setCharValue(42.1);");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-
-        executeJavaScript("testObject.setShortValue(42.1);");
-        assertEquals(42, mTestObject.waitForShortValue());
-        executeJavaScript("testObject.setShortValue(" + Short.MAX_VALUE + " + 42.1);");
-        assertEquals(Short.MIN_VALUE + 42 - 1, mTestObject.waitForShortValue());
-        executeJavaScript("testObject.setShortValue(" + Integer.MAX_VALUE + " + 42.1);");
-        assertEquals(-1, mTestObject.waitForShortValue());
-
-        executeJavaScript("testObject.setIntValue(42.1);");
-        assertEquals(42, mTestObject.waitForIntValue());
-        executeJavaScript("testObject.setIntValue(" + Integer.MAX_VALUE + " + 42.1);");
-        assertEquals(Integer.MAX_VALUE, mTestObject.waitForIntValue());
-
-        executeJavaScript("testObject.setLongValue(42.1);");
-        assertEquals(42L, mTestObject.waitForLongValue());
-        // LIVECONNECT_COMPLIANCE: Should be Long.MAX_VALUE.
-        executeJavaScript("testObject.setLongValue(" + Long.MAX_VALUE + " + 42.1);");
-        assertEquals(Long.MIN_VALUE, mTestObject.waitForLongValue());
-
-        executeJavaScript("testObject.setFloatValue(42.1);");
-        assertEquals(42.1f, mTestObject.waitForFloatValue());
-
-        executeJavaScript("testObject.setDoubleValue(42.1);");
-        assertEquals(42.1, mTestObject.waitForDoubleValue());
-
-        // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.Number.
-        executeJavaScript("testObject.setObjectValue(42.1);");
-        assertNull(mTestObject.waitForObjectValue());
-
-        executeJavaScript("testObject.setStringValue(42.1);");
-        assertEquals("42.1", mTestObject.waitForStringValue());
-
-        executeJavaScript("testObject.setBooleanValue(0.0);");
-        assertFalse(mTestObject.waitForBooleanValue());
-        // LIVECONNECT_COMPLIANCE: Should be true.
-        executeJavaScript("testObject.setBooleanValue(42.1);");
-        assertFalse(mTestObject.waitForBooleanValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeValue(42.1);");
-        assertNull(mTestObject.waitForCustomTypeValue());
-    }
-
-    // Test passing JavaScript NaN to a method of an injected object.
-    public void testPassNumberNaN() throws Throwable {
-        executeJavaScript("testObject.setByteValue(Number.NaN);");
-        assertEquals(0, mTestObject.waitForByteValue());
-
-        executeJavaScript("testObject.setCharValue(Number.NaN);");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-
-        executeJavaScript("testObject.setShortValue(Number.NaN);");
-        assertEquals(0, mTestObject.waitForShortValue());
-
-        executeJavaScript("testObject.setIntValue(Number.NaN);");
-        assertEquals(0, mTestObject.waitForIntValue());
-
-        executeJavaScript("testObject.setLongValue(Number.NaN);");
-        assertEquals(0L, mTestObject.waitForLongValue());
-
-        executeJavaScript("testObject.setFloatValue(Number.NaN);");
-        assertEquals(Float.NaN, mTestObject.waitForFloatValue());
-
-        executeJavaScript("testObject.setDoubleValue(Number.NaN);");
-        assertEquals(Double.NaN, mTestObject.waitForDoubleValue());
-
-        // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.Number.
-        executeJavaScript("testObject.setObjectValue(Number.NaN);");
-        assertNull(mTestObject.waitForObjectValue());
-
-        executeJavaScript("testObject.setStringValue(Number.NaN);");
-        assertEquals("NaN", mTestObject.waitForStringValue());
-
-        executeJavaScript("testObject.setBooleanValue(Number.NaN);");
-        assertFalse(mTestObject.waitForBooleanValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeValue(Number.NaN);");
-        assertNull(mTestObject.waitForCustomTypeValue());
-    }
-
-    // Test passing JavaScript infinity to a method of an injected object.
-    public void testPassNumberInfinity() throws Throwable {
-        executeJavaScript("testObject.setByteValue(Infinity);");
-        assertEquals(-1, mTestObject.waitForByteValue());
-
-        // LIVECONNECT_COMPLIANCE: Should convert to maximum numeric char value.
-        executeJavaScript("testObject.setCharValue(Infinity);");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-
-        executeJavaScript("testObject.setShortValue(Infinity);");
-        assertEquals(-1, mTestObject.waitForShortValue());
-
-        executeJavaScript("testObject.setIntValue(Infinity);");
-        assertEquals(Integer.MAX_VALUE, mTestObject.waitForIntValue());
-
-        // LIVECONNECT_COMPLIANCE: Should be Long.MAX_VALUE.
-        executeJavaScript("testObject.setLongValue(Infinity);");
-        assertEquals(-1L, mTestObject.waitForLongValue());
-
-        executeJavaScript("testObject.setFloatValue(Infinity);");
-        assertEquals(Float.POSITIVE_INFINITY, mTestObject.waitForFloatValue());
-
-        executeJavaScript("testObject.setDoubleValue(Infinity);");
-        assertEquals(Double.POSITIVE_INFINITY, mTestObject.waitForDoubleValue());
-
-        // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.Number.
-        executeJavaScript("testObject.setObjectValue(Infinity);");
-        assertNull(mTestObject.waitForObjectValue());
-
-        executeJavaScript("testObject.setStringValue(Infinity);");
-        assertEquals("Inf", mTestObject.waitForStringValue());
-
-        executeJavaScript("testObject.setBooleanValue(Infinity);");
-        assertFalse(mTestObject.waitForBooleanValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeValue(Infinity);");
-        assertNull(mTestObject.waitForCustomTypeValue());
-    }
-
-    // Test passing a JavaScript boolean to a method of an injected object.
-    public void testPassBoolean() throws Throwable {
-        executeJavaScript("testObject.setBooleanValue(true);");
-        assertTrue(mTestObject.waitForBooleanValue());
-        executeJavaScript("testObject.setBooleanValue(false);");
-        assertFalse(mTestObject.waitForBooleanValue());
-
-        // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.Boolean.
-        executeJavaScript("testObject.setObjectValue(true);");
-        assertNull(mTestObject.waitForObjectValue());
-
-        executeJavaScript("testObject.setStringValue(false);");
-        assertEquals("false", mTestObject.waitForStringValue());
-        executeJavaScript("testObject.setStringValue(true);");
-        assertEquals("true", mTestObject.waitForStringValue());
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.
-        executeJavaScript("testObject.setByteValue(true);");
-        assertEquals(0, mTestObject.waitForByteValue());
-        executeJavaScript("testObject.setByteValue(false);");
-        assertEquals(0, mTestObject.waitForByteValue());
-
-        // LIVECONNECT_COMPLIANCE: Should convert to numeric char value 1.
-        executeJavaScript("testObject.setCharValue(true);");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-        executeJavaScript("testObject.setCharValue(false);");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.
-        executeJavaScript("testObject.setShortValue(true);");
-        assertEquals(0, mTestObject.waitForShortValue());
-        executeJavaScript("testObject.setShortValue(false);");
-        assertEquals(0, mTestObject.waitForShortValue());
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.
-        executeJavaScript("testObject.setIntValue(true);");
-        assertEquals(0, mTestObject.waitForIntValue());
-        executeJavaScript("testObject.setIntValue(false);");
-        assertEquals(0, mTestObject.waitForIntValue());
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.
-        executeJavaScript("testObject.setLongValue(true);");
-        assertEquals(0L, mTestObject.waitForLongValue());
-        executeJavaScript("testObject.setLongValue(false);");
-        assertEquals(0L, mTestObject.waitForLongValue());
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.0.
-        executeJavaScript("testObject.setFloatValue(true);");
-        assertEquals(0.0f, mTestObject.waitForFloatValue());
-        executeJavaScript("testObject.setFloatValue(false);");
-        assertEquals(0.0f, mTestObject.waitForFloatValue());
-
-        // LIVECONNECT_COMPLIANCE: Should be 1.0.
-        executeJavaScript("testObject.setDoubleValue(true);");
-        assertEquals(0.0, mTestObject.waitForDoubleValue());
-        executeJavaScript("testObject.setDoubleValue(false);");
-        assertEquals(0.0, mTestObject.waitForDoubleValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeValue(true);");
-        assertNull(mTestObject.waitForCustomTypeValue());
-    }
-
-    // Test passing a JavaScript string to a method of an injected object.
-    public void testPassString() throws Throwable {
-        executeJavaScript("testObject.setStringValue(\"+042.10\");");
-        assertEquals("+042.10", mTestObject.waitForStringValue());
-
-        // Make sure that we distinguish between the empty string and NULL.
-        executeJavaScript("testObject.setStringValue(\"\");");
-        assertEquals("", mTestObject.waitForStringValue());
-
-        // LIVECONNECT_COMPLIANCE: Should create an instance of java.lang.String.
-        executeJavaScript("testObject.setObjectValue(\"+042.10\");");
-        assertNull(mTestObject.waitForObjectValue());
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setByteValue(\"+042.10\");");
-        assertEquals(0, mTestObject.waitForByteValue());
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setShortValue(\"+042.10\");");
-        assertEquals(0, mTestObject.waitForShortValue());
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setIntValue(\"+042.10\");");
-        assertEquals(0, mTestObject.waitForIntValue());
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setLongValue(\"+042.10\");");
-        assertEquals(0L, mTestObject.waitForLongValue());
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setFloatValue(\"+042.10\");");
-        assertEquals(0.0f, mTestObject.waitForFloatValue());
-
-        // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type.
-        executeJavaScript("testObject.setDoubleValue(\"+042.10\");");
-        assertEquals(0.0, mTestObject.waitForDoubleValue());
-
-        // LIVECONNECT_COMPLIANCE: Should decode and convert to numeric char value.
-        executeJavaScript("testObject.setCharValue(\"+042.10\");");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-
-        // LIVECONNECT_COMPLIANCE: Non-empty string should convert to true.
-        executeJavaScript("testObject.setBooleanValue(\"+042.10\");");
-        assertFalse(mTestObject.waitForBooleanValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeValue(\"+042.10\");");
-        assertNull(mTestObject.waitForCustomTypeValue());
-    }
-
-    // Test passing a JavaScript object to a method of an injected object.
-    public void testPassJavaScriptObject() throws Throwable {
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setObjectValue({foo: 42});");
-        assertNull(mTestObject.waitForObjectValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCustomTypeValue({foo: 42});");
-        assertNull(mTestObject.waitForCustomTypeValue());
-
-        // LIVECONNECT_COMPLIANCE: Should call toString() on object.
-        executeJavaScript("testObject.setStringValue({foo: 42});");
-        assertEquals("undefined", mTestObject.waitForStringValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setByteValue({foo: 42});");
-        assertEquals(0, mTestObject.waitForByteValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCharValue({foo: 42});");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setShortValue({foo: 42});");
-        assertEquals(0, mTestObject.waitForShortValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setIntValue({foo: 42});");
-        assertEquals(0, mTestObject.waitForIntValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setLongValue({foo: 42});");
-        assertEquals(0L, mTestObject.waitForLongValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setFloatValue({foo: 42});");
-        assertEquals(0.0f, mTestObject.waitForFloatValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setDoubleValue({foo: 42});");
-        assertEquals(0.0, mTestObject.waitForDoubleValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setBooleanValue({foo: 42});");
-        assertFalse(mTestObject.waitForBooleanValue());
-    }
-
-    // Test passing a Java object to a method of an injected object. Note that
-    // this test requires being able to return objects from the methods of
-    // injected objects. This is tested elsewhere.
-    public void testPassJavaObject() throws Throwable {
-        executeJavaScript("testObject.setObjectValue(testObject.getObjectInstance());");
-        assertTrue(mTestObject.getObjectInstance() == mTestObject.waitForObjectValue());
-        executeJavaScript("testObject.setObjectValue(testObject.getCustomTypeInstance());");
-        assertTrue(mTestObject.getCustomTypeInstance() == mTestObject.waitForObjectValue());
-
-        executeJavaScript("testObject.setCustomTypeValue(testObject.getObjectInstance());");
-        assertTrue(mTestObject.getObjectInstance() == mTestObject.waitForCustomTypeValue());
-        executeJavaScript("testObject.setCustomTypeValue(testObject.getCustomTypeInstance());");
-        assertTrue(mTestObject.getCustomTypeInstance() == mTestObject.waitForCustomTypeValue());
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception, as the types are unrelated.
-        executeJavaScript("testObject.setCustomTypeValue(testObject.getCustomType2Instance());");
-        assertTrue(mTestObject.getCustomType2Instance() ==
-                (Object)mTestObject.waitForCustomTypeValue());
-
-        // LIVECONNECT_COMPLIANCE: Should call toString() on object.
-        executeJavaScript("testObject.setStringValue(testObject.getObjectInstance());");
-        assertEquals("undefined", mTestObject.waitForStringValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setByteValue(testObject.getObjectInstance());");
-        assertEquals(0, mTestObject.waitForByteValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setCharValue(testObject.getObjectInstance());");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setShortValue(testObject.getObjectInstance());");
-        assertEquals(0, mTestObject.waitForShortValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setIntValue(testObject.getObjectInstance());");
-        assertEquals(0, mTestObject.waitForIntValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setLongValue(testObject.getObjectInstance());");
-        assertEquals(0L, mTestObject.waitForLongValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setFloatValue(testObject.getObjectInstance());");
-        assertEquals(0.0f, mTestObject.waitForFloatValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setDoubleValue(testObject.getObjectInstance());");
-        assertEquals(0.0, mTestObject.waitForDoubleValue());
-
-        // LIVECONNECT_COMPLIANCE: Should raise a JavaScript exception.
-        executeJavaScript("testObject.setBooleanValue(testObject.getObjectInstance());");
-        assertFalse(mTestObject.waitForBooleanValue());
-    }
-
-    // Test passing JavaScript null to a method of an injected object.
-    public void testPassNull() throws Throwable {
-        executeJavaScript("testObject.setObjectValue(null);");
-        assertNull(mTestObject.waitForObjectValue());
-
-        executeJavaScript("testObject.setCustomTypeValue(null);");
-        assertNull(mTestObject.waitForCustomTypeValue());
-
-        executeJavaScript("testObject.setStringValue(null);");
-        assertNull(mTestObject.waitForStringValue());
-
-        executeJavaScript("testObject.setByteValue(null);");
-        assertEquals(0, mTestObject.waitForByteValue());
-
-        executeJavaScript("testObject.setCharValue(null);");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-
-        executeJavaScript("testObject.setShortValue(null);");
-        assertEquals(0, mTestObject.waitForShortValue());
-
-        executeJavaScript("testObject.setIntValue(null);");
-        assertEquals(0, mTestObject.waitForIntValue());
-
-        executeJavaScript("testObject.setLongValue(null);");
-        assertEquals(0L, mTestObject.waitForLongValue());
-
-        executeJavaScript("testObject.setFloatValue(null);");
-        assertEquals(0.0f, mTestObject.waitForFloatValue());
-
-        executeJavaScript("testObject.setDoubleValue(null);");
-        assertEquals(0.0, mTestObject.waitForDoubleValue());
-
-        executeJavaScript("testObject.setBooleanValue(null);");
-        assertFalse(mTestObject.waitForBooleanValue());
-    }
-
-    // Test passing JavaScript undefined to a method of an injected object.
-    public void testPassUndefined() throws Throwable {
-        executeJavaScript("testObject.setObjectValue(undefined);");
-        assertNull(mTestObject.waitForObjectValue());
-
-        executeJavaScript("testObject.setCustomTypeValue(undefined);");
-        assertNull(mTestObject.waitForCustomTypeValue());
-
-        // LIVECONNECT_COMPLIANCE: Should be NULL.
-        executeJavaScript("testObject.setStringValue(undefined);");
-        assertEquals("undefined", mTestObject.waitForStringValue());
-
-        executeJavaScript("testObject.setByteValue(undefined);");
-        assertEquals(0, mTestObject.waitForByteValue());
-
-        executeJavaScript("testObject.setCharValue(undefined);");
-        assertEquals('\u0000', mTestObject.waitForCharValue());
-
-        executeJavaScript("testObject.setShortValue(undefined);");
-        assertEquals(0, mTestObject.waitForShortValue());
-
-        executeJavaScript("testObject.setIntValue(undefined);");
-        assertEquals(0, mTestObject.waitForIntValue());
-
-        executeJavaScript("testObject.setLongValue(undefined);");
-        assertEquals(0L, mTestObject.waitForLongValue());
-
-        executeJavaScript("testObject.setFloatValue(undefined);");
-        assertEquals(0.0f, mTestObject.waitForFloatValue());
-
-        executeJavaScript("testObject.setDoubleValue(undefined);");
-        assertEquals(0.0, mTestObject.waitForDoubleValue());
-
-        executeJavaScript("testObject.setBooleanValue(undefined);");
-        assertFalse(mTestObject.waitForBooleanValue());
-    }
-}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeFieldsTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeFieldsTest.java
deleted file mode 100644 (file)
index 0ccd175..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Part of the test suite for the WebView's Java Bridge. This test tests the
- * use of fields.
- *
- * To run this test ...
- *  adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeFieldsTest \
- *     com.android.webviewtests/android.test.InstrumentationTestRunner
- */
-
-package com.android.webviewtests;
-
-public class JavaBridgeFieldsTest extends JavaBridgeTestBase {
-    private class TestObject extends Controller {
-        private String mStringValue;
-
-        // These methods are used to control the test.
-        public synchronized void setStringValue(String x) {
-            mStringValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized String waitForStringValue() {
-            waitForResult();
-            return mStringValue;
-        }
-
-        public boolean booleanField = true;
-        public byte byteField = 42;
-        public char charField = '\u002A';
-        public short shortField = 42;
-        public int intField = 42;
-        public long longField = 42L;
-        public float floatField = 42.0f;
-        public double doubleField = 42.0;
-        public String stringField = "foo";
-        public Object objectField = new Object();
-        public CustomType customTypeField = new CustomType();
-    }
-
-    // A custom type used when testing passing objects.
-    private class CustomType {
-    }
-
-    TestObject mTestObject;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTestObject = new TestObject();
-        setUpWebView(mTestObject, "testObject");
-    }
-
-    // Note that this requires that we can pass a JavaScript string to Java.
-    protected String executeJavaScriptAndGetStringResult(String script) throws Throwable {
-        executeJavaScript("testObject.setStringValue(" + script + ");");
-        return mTestObject.waitForStringValue();
-    }
-
-    // The Java bridge does not provide access to fields.
-    // FIXME: Consider providing support for this. See See b/4408210.
-    public void testFieldTypes() throws Throwable {
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.booleanField"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.byteField"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.charField"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.shortField"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.intField"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.longField"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.floatField"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.doubleField"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.objectField"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.stringField"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.customTypeField"));
-    }
-}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeReturnValuesTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeReturnValuesTest.java
deleted file mode 100644 (file)
index 44d5cc6..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Part of the test suite for the WebView's Java Bridge. This test checks that
- * we correctly convert Java values to JavaScript values when returning them
- * from the methods of injected Java objects.
- *
- * The conversions should follow
- * http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. Places in
- * which the implementation differs from the spec are marked with
- * LIVECONNECT_COMPLIANCE.
- * FIXME: Consider making our implementation more compliant, if it will not
- * break backwards-compatibility. See b/4408210.
- *
- * To run this test ...
- *  adb shell am instrument -w -e class com.android.webviewtests.JavaBridgeReturnValuesTest \
- *     com.android.webviewtests/android.test.InstrumentationTestRunner
- */
-
-package com.android.webviewtests;
-
-public class JavaBridgeReturnValuesTest extends JavaBridgeTestBase {
-    // An instance of this class is injected into the page to test returning
-    // Java values to JavaScript.
-    private class TestObject extends Controller {
-        private String mStringValue;
-        private boolean mBooleanValue;
-
-        // These four methods are used to control the test.
-        public synchronized void setStringValue(String x) {
-            mStringValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized String waitForStringValue() {
-            waitForResult();
-            return mStringValue;
-        }
-        public synchronized void setBooleanValue(boolean x) {
-            mBooleanValue = x;
-            notifyResultIsReady();
-        }
-        public synchronized boolean waitForBooleanValue() {
-            waitForResult();
-            return mBooleanValue;
-        }
-
-        public boolean getBooleanValue() {
-            return true;
-        }
-        public byte getByteValue() {
-            return 42;
-        }
-        public char getCharValue() {
-            return '\u002A';
-        }
-        public short getShortValue() {
-            return 42;
-        }
-        public int getIntValue() {
-            return 42;
-        }
-        public long getLongValue() {
-            return 42L;
-        }
-        public float getFloatValue() {
-            return 42.1f;
-        }
-        public float getFloatValueNoDecimal() {
-            return 42.0f;
-        }
-        public double getDoubleValue() {
-            return 42.1;
-        }
-        public double getDoubleValueNoDecimal() {
-            return 42.0;
-        }
-        public String getStringValue() {
-            return "foo";
-        }
-        public String getEmptyStringValue() {
-            return "";
-        }
-        public String getNullStringValue() {
-            return null;
-        }
-        public Object getObjectValue() {
-            return new Object();
-        }
-        public Object getNullObjectValue() {
-            return null;
-        }
-        public CustomType getCustomTypeValue() {
-            return new CustomType();
-        }
-        public void getVoidValue() {
-        }
-    }
-
-    // A custom type used when testing passing objects.
-    private class CustomType {
-    }
-
-    TestObject mTestObject;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTestObject = new TestObject();
-        setUpWebView(mTestObject, "testObject");
-    }
-
-    // Note that this requires that we can pass a JavaScript string to Java.
-    protected String executeJavaScriptAndGetStringResult(String script) throws Throwable {
-        executeJavaScript("testObject.setStringValue(" + script + ");");
-        return mTestObject.waitForStringValue();
-    }
-
-    // Note that this requires that we can pass a JavaScript boolean to Java.
-    private boolean executeJavaScriptAndGetBooleanResult(String script) throws Throwable {
-        executeJavaScript("testObject.setBooleanValue(" + script + ");");
-        return mTestObject.waitForBooleanValue();
-    }
-
-    public void testMethodReturnTypes() throws Throwable {
-        assertEquals("boolean",
-                executeJavaScriptAndGetStringResult("typeof testObject.getBooleanValue()"));
-        assertEquals("number",
-                executeJavaScriptAndGetStringResult("typeof testObject.getByteValue()"));
-        // char values are returned to JavaScript as numbers.
-        assertEquals("number",
-                executeJavaScriptAndGetStringResult("typeof testObject.getCharValue()"));
-        assertEquals("number",
-                executeJavaScriptAndGetStringResult("typeof testObject.getShortValue()"));
-        assertEquals("number",
-                executeJavaScriptAndGetStringResult("typeof testObject.getIntValue()"));
-        assertEquals("number",
-                executeJavaScriptAndGetStringResult("typeof testObject.getLongValue()"));
-        assertEquals("number",
-                executeJavaScriptAndGetStringResult("typeof testObject.getFloatValue()"));
-        assertEquals("number",
-                executeJavaScriptAndGetStringResult("typeof testObject.getFloatValueNoDecimal()"));
-        assertEquals("number",
-                executeJavaScriptAndGetStringResult("typeof testObject.getDoubleValue()"));
-        assertEquals("number",
-                executeJavaScriptAndGetStringResult("typeof testObject.getDoubleValueNoDecimal()"));
-        assertEquals("string",
-                executeJavaScriptAndGetStringResult("typeof testObject.getStringValue()"));
-        assertEquals("string",
-                executeJavaScriptAndGetStringResult("typeof testObject.getEmptyStringValue()"));
-        // LIVECONNECT_COMPLIANCE: This should have type object.
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.getNullStringValue()"));
-        assertEquals("object",
-                executeJavaScriptAndGetStringResult("typeof testObject.getObjectValue()"));
-        assertEquals("object",
-                executeJavaScriptAndGetStringResult("typeof testObject.getNullObjectValue()"));
-        assertEquals("object",
-                executeJavaScriptAndGetStringResult("typeof testObject.getCustomTypeValue()"));
-        assertEquals("undefined",
-                executeJavaScriptAndGetStringResult("typeof testObject.getVoidValue()"));
-    }
-
-    public void testMethodReturnValues() throws Throwable {
-        // We do the string comparison in JavaScript, to avoid relying on the
-        // coercion algorithm from JavaScript to Java.
-        assertTrue(executeJavaScriptAndGetBooleanResult("testObject.getBooleanValue()"));
-        assertTrue(executeJavaScriptAndGetBooleanResult("42 === testObject.getByteValue()"));
-        // char values are returned to JavaScript as numbers.
-        assertTrue(executeJavaScriptAndGetBooleanResult("42 === testObject.getCharValue()"));
-        assertTrue(executeJavaScriptAndGetBooleanResult("42 === testObject.getShortValue()"));
-        assertTrue(executeJavaScriptAndGetBooleanResult("42 === testObject.getIntValue()"));
-        assertTrue(executeJavaScriptAndGetBooleanResult("42 === testObject.getLongValue()"));
-        assertTrue(executeJavaScriptAndGetBooleanResult(
-                "Math.abs(42.1 - testObject.getFloatValue()) < 0.001"));
-        assertTrue(executeJavaScriptAndGetBooleanResult(
-                "42.0 === testObject.getFloatValueNoDecimal()"));
-        assertTrue(executeJavaScriptAndGetBooleanResult(
-                "Math.abs(42.1 - testObject.getDoubleValue()) < 0.001"));
-        assertTrue(executeJavaScriptAndGetBooleanResult(
-                "42.0 === testObject.getDoubleValueNoDecimal()"));
-        assertEquals("foo", executeJavaScriptAndGetStringResult("testObject.getStringValue()"));
-        assertEquals("", executeJavaScriptAndGetStringResult("testObject.getEmptyStringValue()"));
-        assertTrue(executeJavaScriptAndGetBooleanResult("undefined === testObject.getVoidValue()"));
-    }
-}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java
deleted file mode 100644 (file)
index a451015..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Common functionality for testing the WebView's Java Bridge.
- */
-
-package com.android.webviewtests;
-
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-
-import junit.framework.Assert;
-
-public class JavaBridgeTestBase extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
-    protected class TestWebViewClient extends WebViewClient {
-        private boolean mIsPageFinished;
-        @Override
-        public synchronized void onPageFinished(WebView webView, String url) {
-            mIsPageFinished = true;
-            notify();
-        }
-        public synchronized void waitForOnPageFinished() throws RuntimeException {
-            while (!mIsPageFinished) {
-                try {
-                    wait(5000);
-                } catch (Exception e) {
-                    continue;
-                }
-                if (!mIsPageFinished) {
-                    throw new RuntimeException("Timed out waiting for onPageFinished()");
-                }
-            }
-            mIsPageFinished = false;
-        }
-    }
-
-    protected class Controller {
-        private boolean mIsResultReady;
-
-        protected synchronized void notifyResultIsReady() {
-            mIsResultReady = true;
-            notify();
-        }
-        protected synchronized void waitForResult() {
-            while (!mIsResultReady) {
-                try {
-                    wait(5000);
-                } catch (Exception e) {
-                    continue;
-                }
-                if (!mIsResultReady) {
-                    Assert.fail("Wait timed out");
-                }
-            }
-            mIsResultReady = false;
-        }
-    }
-
-    protected TestWebViewClient mWebViewClient;
-
-    public JavaBridgeTestBase() {
-        super(WebViewStubActivity.class);
-    }
-
-    // Sets up the WebView and injects the supplied object. Intended to be called from setUp().
-    protected void setUpWebView(final Object object, final String name) throws Exception {
-        mWebViewClient = new TestWebViewClient();
-        // This starts the activity, so must be called on the test thread.
-        final WebViewStubActivity activity = getActivity();
-        // On the UI thread, load an empty page and wait for it to finish
-        // loading so that the Java object is injected.
-        try {
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    WebView webView = activity.getWebView();
-                    webView.addJavascriptInterface(object, name);
-                    webView.getSettings().setJavaScriptEnabled(true);
-                    webView.setWebViewClient(mWebViewClient);
-                    webView.loadData("<!DOCTYPE html><title></title>", "text/html", null);
-                }
-            });
-            mWebViewClient.waitForOnPageFinished();
-        } catch (Throwable e) {
-            throw new RuntimeException("Failed to set up WebView: " + Log.getStackTraceString(e));
-        }
-    }
-
-    protected void executeJavaScript(final String script) throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                // When a JavaScript URL is executed, if the value of the last
-                // expression evaluated is not 'undefined', this value is
-                // converted to a string and used as the new document for the
-                // frame. We don't want this behaviour, so wrap the script in
-                // an anonymous function.
-                getWebView().loadUrl("javascript:(function() { " + script + " })()");
-            }
-        });
-    }
-
-    protected WebView getWebView() {
-        return getActivity().getWebView();
-    }
-}
diff --git a/tests/WebViewTests/src/com/android/webviewtests/WebViewStubActivity.java b/tests/WebViewTests/src/com/android/webviewtests/WebViewStubActivity.java
deleted file mode 100644 (file)
index ccfd3d5..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.webviewtests;
-
-import com.android.webviewtests.R;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.webkit.WebView;
-
-public class WebViewStubActivity extends Activity {
-    private WebView mWebView;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.webview_layout);
-        mWebView = (WebView) findViewById(R.id.web_page);
-    }
-
-    public WebView getWebView() {
-        return mWebView;
-    }
-}
index 2ba38e1..e25b38c 100644 (file)
@@ -101,6 +101,8 @@ public class WifiInfo implements Parcelable {
     private InetAddress mIpAddress;
     private String mMacAddress = DEFAULT_MAC_ADDRESS;
 
+    private boolean mEphemeral;
+
     /**
      * @hide
      */
@@ -253,6 +255,7 @@ public class WifiInfo implements Parcelable {
         setLinkSpeed(-1);
         setFrequency(-1);
         setMeteredHint(false);
+        setEphemeral(false);
         txBad = 0;
         txSuccess = 0;
         rxSuccess = 0;
@@ -283,6 +286,7 @@ public class WifiInfo implements Parcelable {
             mIpAddress = source.mIpAddress;
             mMacAddress = source.mMacAddress;
             mMeteredHint = source.mMeteredHint;
+            mEphemeral = source.mEphemeral;
             txBad = source.txBad;
             txRetries = source.txRetries;
             txSuccess = source.txSuccess;
@@ -430,6 +434,16 @@ public class WifiInfo implements Parcelable {
         return mMeteredHint;
     }
 
+    /** {@hide} */
+    public void setEphemeral(boolean ephemeral) {
+        mEphemeral = ephemeral;
+    }
+
+    /** {@hide} */
+    public boolean isEphemeral() {
+        return mEphemeral;
+    }
+
     /** @hide */
     public void setNetworkId(int id) {
         mNetworkId = id;
@@ -567,6 +581,7 @@ public class WifiInfo implements Parcelable {
         dest.writeString(mBSSID);
         dest.writeString(mMacAddress);
         dest.writeInt(mMeteredHint ? 1 : 0);
+        dest.writeInt(mEphemeral ? 1 : 0);
         dest.writeInt(score);
         dest.writeDouble(txSuccessRate);
         dest.writeDouble(txRetriesRate);
@@ -597,6 +612,7 @@ public class WifiInfo implements Parcelable {
                 info.mBSSID = in.readString();
                 info.mMacAddress = in.readString();
                 info.mMeteredHint = in.readInt() != 0;
+                info.mEphemeral = in.readInt() != 0;
                 info.score = in.readInt();
                 info.txSuccessRate = in.readDouble();
                 info.txRetriesRate = in.readDouble();