Merge "Don't offer crypto ops for public keys of trusted cert entries." into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index d951591..2d51b97 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10,10 +10,8 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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();
@@ -30543,8 +30532,8 @@
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 @@
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";
@@ -36961,6 +36951,7 @@
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 @@
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 @@
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);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 78e0a16..33c7a1b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5964,10 +5964,12 @@
}
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 @@
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();
@@ -32766,8 +32769,8 @@
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 @@
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";
@@ -39242,6 +39246,7 @@
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 @@
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 @@
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);
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 2be44bc..ce83caa 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -204,6 +204,10 @@
return runGrantRevokePermission(false);
}
+ if ("reset-permissions".equals(op)) {
+ return runResetPermissions();
+ }
+
if ("set-permission-enforced".equals(op)) {
return runSetPermissionEnforced();
}
@@ -1636,6 +1640,24 @@
}
}
+ 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 @@
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 @@
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");
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 284dfd6..3429b6e 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -2,6 +2,7 @@
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 @@
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 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 @@
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 @@
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;
- }
- } else {
- mIdPackage = mIdType = mIdEntry = null;
- }
- mX = in.readInt();
- mY = in.readInt();
- mScrollX = in.readInt();
- mScrollY = in.readInt();
- mWidth = in.readInt();
- mHeight = in.readInt();
- mFlags = in.readInt();
+ ViewNode(Parcel in, PooledStringReader preader, float[] tmpMatrix) {
mClassName = preader.readString();
- mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- if (in.readInt() != 0) {
- mText = new ViewNodeText(in);
- } else {
- mText = null;
+ 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();
+ }
+ }
}
- mExtras = in.readBundle();
- final int NCHILDREN = in.readInt();
- if (NCHILDREN > 0) {
+ if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
+ mX = in.readInt();
+ mY = in.readInt();
+ mWidth = in.readInt();
+ mHeight = in.readInt();
+ } else {
+ int val = in.readInt();
+ mX = val&0x7fff;
+ mY = (val>>16)&0x7fff;
+ val = in.readInt();
+ mWidth = val&0x7fff;
+ mHeight = (val>>16)&0x7fff;
+ }
+ 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);
+ }
+ 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;
+ }
+ if (mText != null) {
+ flags |= FLAGS_HAS_TEXT;
+ }
+ if (mExtras != null) {
+ flags |= FLAGS_HAS_EXTRAS;
+ }
+ 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);
+ }
}
}
- 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);
+ if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
+ out.writeInt(mX);
+ out.writeInt(mY);
+ out.writeInt(mWidth);
+ out.writeInt(mHeight);
} else {
- out.writeInt(0);
+ out.writeInt((mY<<16) | mX);
+ out.writeInt((mHeight<<16) | mWidth);
}
- out.writeBundle(mExtras);
- if (mChildren != null) {
+ 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 @@
}
/**
+ * 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 @@
}
@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 @@
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 @@
}
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();
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 67d9de5..eaf20d8 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -152,7 +152,6 @@
*/
public void stopAdvertising(final AdvertiseCallback callback) {
synchronized (mLeAdvertisers) {
- BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5190037..83d6cb0 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1020,6 +1020,11 @@
* <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";
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index cb68d74..cea6e99 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -102,6 +102,8 @@
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 @@
void grantDefaultPermissions(int userId);
void setCarrierAppPackagesProvider(in IPackagesProvider provider);
+ int getMountExternalMode(int uid);
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 75289f7..33cc962 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1062,6 +1062,15 @@
* 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 @@
*
* @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 @@
* 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 @@
* </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
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1d31109..9dee045 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -779,6 +779,15 @@
* 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 @@
*
* @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 @@
* 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 @@
* </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
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 0484806..f596c93 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -26,7 +26,7 @@
*/
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
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index ae74b9a..ff7a300 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1819,7 +1819,18 @@
}
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 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 @@
* 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 @@
}
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 @@
return true;
}
}
-
+
return false;
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index f9c50f3..7234e98 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -643,6 +643,10 @@
}
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 @@
* @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;
}
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index e55ae99..84a879c 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1177,6 +1177,21 @@
_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 @@
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 @@
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 String getPrimaryStorageUuid() throws RemoteException;
public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
throws RemoteException;
+
+ public void remountUid(int uid) throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 9b26f24..aab68e9 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -871,6 +871,15 @@
}
/** {@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;
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 51dbdee..e63fb04 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -283,7 +283,13 @@
* 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 @@
* 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
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 0309d24..d424546 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -369,7 +369,9 @@
/**
* 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) {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 464710b..b6fa4e4c 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -714,6 +714,27 @@
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;
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 47d5c79..82689b9 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -105,32 +105,45 @@
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);
}
/**
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index 5dda8a7..3688cfa 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -147,24 +147,13 @@
* @throws IllegalArgumentException is offset is not valid.
*/
public int getBeginning(int offset) {
- final int shiftedOffset = offset - mOffsetShift;
- checkOffsetIsValid(shiftedOffset);
-
- if (isOnLetterOrDigit(shiftedOffset)) {
- if (mIterator.isBoundary(shiftedOffset)) {
- return shiftedOffset + mOffsetShift;
- } else {
- return mIterator.preceding(shiftedOffset) + mOffsetShift;
- }
- } else {
- if (isAfterLetterOrDigit(shiftedOffset)) {
- return mIterator.preceding(shiftedOffset) + mOffsetShift;
- }
- }
- return BreakIterator.DONE;
+ // 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
+ /**
+ * 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,
@@ -177,11 +166,106 @@
* @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)
+ && (!isAfterLetterOrDigit(shiftedOffset)
+ || !getPrevWordBeginningOnTwoWordsBoundary)) {
+ return shiftedOffset + mOffsetShift;
+ } else {
+ return mIterator.preceding(shiftedOffset) + mOffsetShift;
+ }
+ } else {
+ if (isAfterLetterOrDigit(shiftedOffset)) {
+ return mIterator.preceding(shiftedOffset) + mOffsetShift;
+ }
+ }
+ return 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.
+ *
+ * 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.
+ *
+ * Returns BreakIterator.DONE if there is no next boundary.
+ *
+ * @throws IllegalArgumentException is offset is not valid.
+ */
+ 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;
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index c61ca4e..e958058 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -528,11 +528,13 @@
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 @@
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 @@
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 Transition removeTarget(int targetId) {
if (targetId > 0) {
- mTargetIds.remove(targetId);
+ mTargetIds.remove((Integer)targetId);
}
return this;
}
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 5209f90..71c8099 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -435,10 +435,11 @@
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();
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 92dae2e..23da6d2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6179,6 +6179,10 @@
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 @@
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 @@
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 @@
}
}
- /** @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 @@
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 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 @@
}
/**
- * 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) {
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 2e2ba88..b53d93c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1919,7 +1919,7 @@
}
@Override
- public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
+ public void addChildrenForAccessibility(ArrayList<View> outChildren) {
if (getAccessibilityNodeProvider() != null) {
return;
}
@@ -1930,9 +1930,9 @@
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);
}
}
}
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 9ab0ace..794622a 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -16,6 +16,7 @@
package android.view;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
@@ -50,6 +51,28 @@
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()}.
*/
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index a96bf71..8bf6992 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6605,6 +6605,8 @@
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 @@
// 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 @@
// 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 @@
}
}
+ private ArrayList<View> getSkippedScrap() {
+ if (mSkippedScrap == null) {
+ mSkippedScrap = new ArrayList<>();
+ }
+ return mSkippedScrap;
+ }
+
/**
* Finish the removal of any views that skipped the scrap heap.
*/
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 48e69a1..c96d39b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -123,6 +123,7 @@
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 @@
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 @@
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 @@
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,12 +4132,48 @@
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);
+ int offsetToWord = (end - start) / 2;
if (offset <= end - offsetToWord || currLine < mPrevLine) {
offset = start;
} else {
@@ -4182,6 +4223,7 @@
}
positionAtCursorOffset(offset, false);
}
+ mPrevX = x;
}
@Override
@@ -4196,6 +4238,7 @@
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 +4249,10 @@
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,12 +4313,48 @@
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);
+ int midPoint = (end - start) / 2;
if (offset >= start + midPoint || currLine > mPrevLine) {
offset = end;
} else {
@@ -4321,6 +4404,7 @@
}
positionAtCursorOffset(offset, false);
}
+ mPrevX = x;
}
@Override
@@ -4335,6 +4419,7 @@
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;
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 7ca450a..280ff15 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -230,28 +230,29 @@
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() +
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 056323db..4dcc242 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -359,7 +359,7 @@
}
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 @@
}
}
+ /**
+ * 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 @@
}
if (hasDividerBeforeChildAt(count)) {
- final View child = getVirtualChildAt(count - 1);
+ final View child = getLastNonGoneChild();
int position;
if (child == null) {
if (isLayoutRtl) {
@@ -627,21 +641,29 @@
* @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;
- }
- }
- return hasVisibleViewBefore;
}
- return false;
+ 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 true;
}
/**
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 534bfad..c6de5dd 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1127,10 +1127,19 @@
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();
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 49226cd0..f45e750 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -242,6 +242,38 @@
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) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 207605e..310b474 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5231,7 +5231,7 @@
// 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 +5849,43 @@
@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);
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index c97fdf4..197004c 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -46,8 +46,12 @@
/** 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();
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index fa870b9..3e86fac 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -519,6 +519,10 @@
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=")) {
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 523663c..32a145c 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -23,6 +23,7 @@
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.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;
@@ -108,8 +110,10 @@
* 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());
}
/**
@@ -276,6 +280,7 @@
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;
@@ -375,9 +380,10 @@
* @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 +421,7 @@
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 +429,7 @@
if (!overflowMenuItems.isEmpty()) {
if (mOverflowPanel == null) {
mOverflowPanel =
- new FloatingToolbarOverflowPanel(mParent.getContext(), mCloseOverflow);
+ new FloatingToolbarOverflowPanel(mContext, mCloseOverflow);
}
mOverflowPanel.setMenuItems(overflowMenuItems);
mOverflowPanel.setOnMenuItemClickListener(menuItemClickListener);
@@ -540,7 +546,7 @@
* Returns the context this popup is running in.
*/
public Context getContext() {
- return mContentContainer.getContext();
+ return mContext;
}
private void refreshCoordinatesAndOverflowDirection(Rect contentRect) {
@@ -562,7 +568,7 @@
} 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 +627,7 @@
}
private int getToolbarHeightWithVerticalMargin() {
- return getEstimatedToolbarHeight(mParent.getContext()) + mMarginVertical * 2;
+ return getEstimatedToolbarHeight(mContext) + mMarginVertical * 2;
}
/**
@@ -1477,6 +1483,17 @@
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 +1503,6 @@
.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.
*/
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 70a7805..e2cfa44 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -682,18 +682,28 @@
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",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 76db5d3..f7cfe0e 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -66,6 +66,8 @@
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 @@
// 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) {
+static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
+ bool force_mount_namespace) {
+ // See storage config details at http://source.android.com/tech/storage/
+
+ // Create a second private mount namespace for our process
+ if (unshare(CLONE_NEWNS) == -1) {
+ ALOGW("Failed to unshare(): %s", strerror(errno));
+ 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;
+ }
+
+ // 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;
- }
-
- // Create a second private mount namespace for our process
- if (unshare(CLONE_NEWNS) == -1) {
- ALOGW("Failed to unshare(): %s", strerror(errno));
- return false;
- }
-
- 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"));
-
- if (fs_prepare_dir(source.string(), 0755, 0, 0) == -1) {
- 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;
- }
-
- return true;
}
static bool NeedsNoRandomizeWorkaround() {
@@ -543,7 +556,7 @@
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.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9637cf5..bada791 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -335,14 +335,18 @@
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"
@@ -361,14 +365,18 @@
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"
@@ -387,7 +395,9 @@
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"
@@ -395,28 +405,36 @@
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"
@@ -433,6 +451,7 @@
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"
@@ -471,7 +490,9 @@
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"
@@ -490,7 +511,9 @@
<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"
@@ -509,14 +532,18 @@
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"
@@ -543,7 +570,9 @@
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"
@@ -551,7 +580,9 @@
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"
@@ -568,7 +599,9 @@
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"
@@ -585,21 +618,27 @@
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"
@@ -608,7 +647,9 @@
<!-- 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"
@@ -629,7 +670,9 @@
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"
@@ -655,7 +698,9 @@
<uses-feature>}</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"
@@ -676,14 +721,17 @@
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"
@@ -768,7 +816,9 @@
<!-- =============================================================== -->
<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"
@@ -779,11 +829,15 @@
<!-- =============================================================== -->
<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" />
@@ -792,7 +846,9 @@
<!-- ======================================= -->
<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"
@@ -816,7 +872,10 @@
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" />
@@ -825,25 +884,33 @@
<!-- ======================================= -->
<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"
@@ -890,13 +957,17 @@
<!-- ======================================= -->
<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"
@@ -920,7 +991,9 @@
<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"
@@ -960,7 +1033,9 @@
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"
@@ -978,32 +1053,42 @@
<!-- ================================== -->
<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"
@@ -1014,7 +1099,9 @@
<!-- ==================================================== -->
<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"
@@ -1036,8 +1123,10 @@
<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" />
@@ -1146,7 +1235,9 @@
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" />
@@ -1160,7 +1251,9 @@
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" />
@@ -1185,7 +1278,9 @@
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" />
@@ -1194,7 +1289,9 @@
<!-- ================================== -->
<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"
@@ -1261,7 +1358,9 @@
<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"
@@ -1289,7 +1388,9 @@
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"
@@ -1320,13 +1421,17 @@
<!-- ================================== -->
<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"
@@ -1342,7 +1447,9 @@
<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"
@@ -1353,7 +1460,9 @@
<!-- ==================================================== -->
<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"
@@ -1364,13 +1473,17 @@
<!-- ============================================================== -->
<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"
@@ -1381,19 +1494,25 @@
<!-- ==================================================== -->
<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"
@@ -1451,7 +1570,9 @@
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"
@@ -1473,7 +1594,9 @@
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"
@@ -1482,7 +1605,9 @@
<!-- 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"
@@ -1528,14 +1653,18 @@
<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" />
@@ -1634,9 +1763,11 @@
<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" />
@@ -1654,15 +1785,19 @@
<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" />
@@ -1671,10 +1806,12 @@
<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" />
@@ -1695,10 +1832,12 @@
<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" />
@@ -1716,9 +1855,11 @@
<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" />
@@ -1730,28 +1871,38 @@
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" />
@@ -1761,22 +1912,30 @@
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" />
@@ -1793,7 +1952,9 @@
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" />
@@ -1810,7 +1971,9 @@
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" />
@@ -1820,14 +1983,18 @@
<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" />
@@ -1845,7 +2012,9 @@
<!-- 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"
@@ -1856,8 +2025,10 @@
<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" />
@@ -1890,8 +2061,10 @@
<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" />
@@ -1957,8 +2130,10 @@
<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" />
@@ -1967,9 +2142,11 @@
<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.
@@ -1983,23 +2160,27 @@
<!-- 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" />
@@ -2242,7 +2423,9 @@
<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"
@@ -2286,13 +2469,17 @@
<!-- 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" />
@@ -2304,7 +2491,9 @@
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" />
@@ -2359,7 +2548,9 @@
<!-- 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"
diff --git a/core/res/res/layout/floating_popup_menu_button.xml b/core/res/res/layout/floating_popup_menu_button.xml
index 1b58ce5..482f91f 100644
--- a/core/res/res/layout/floating_popup_menu_button.xml
+++ b/core/res/res/layout/floating_popup_menu_button.xml
@@ -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"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 33c9c60..fd47d49 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -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. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f6cefba..9d79952 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1268,6 +1268,14 @@
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>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3a1a156..8bf1aac 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -240,6 +240,7 @@
<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" />
@@ -902,6 +903,7 @@
<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" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index b7acdd4..c230645 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -42,6 +42,7 @@
-->
<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 @@
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>
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
index 6ee6ffa..6b3b55e 100644
--- a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
@@ -5,6 +5,4 @@
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
index 0000000..d2e2131
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -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);
+ }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index c517201..377e6a16 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -58,21 +58,6 @@
<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>
diff --git a/docs/html/guide/topics/security/permissions.jd b/docs/html/guide/topics/security/permissions.jd
index 6f919da..cfab3c9 100644
--- a/docs/html/guide/topics/security/permissions.jd
+++ b/docs/html/guide/topics/security/permissions.jd
@@ -52,9 +52,7 @@
<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/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 5e9b213..c98932c 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -19,6 +19,7 @@
#include <SkPath.h>
#include "Rect.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
@@ -85,6 +86,11 @@
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);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 81cf2df..11abd70 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -169,7 +169,7 @@
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
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 024ff10..09d1258 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -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 @@
// 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);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a806440..b3b2b97 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -619,10 +619,6 @@
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 @@
* @hide
*/
public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
+ sAudioPortEventHandler.init();
sAudioPortEventHandler.registerListener(l);
}
@@ -3586,6 +3583,7 @@
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 @@
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 @@
synchronized (mDeviceCallbacks) {
if (mDeviceCallbacks.containsKey(callback)) {
mDeviceCallbacks.remove(callback);
+ if (mDeviceCallbacks.size() == 0) {
+ unregisterAudioPortUpdateListener(mPortListener);
+ }
}
}
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 6c26220..28d0713 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -705,6 +705,9 @@
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);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
index ac9dc85..d265e0d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
@@ -50,7 +50,7 @@
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);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 59a8ad5..3568429 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -109,7 +109,7 @@
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);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 44b9d8b..41043eb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -905,7 +905,9 @@
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()) {
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 005077f..155f5ea 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -583,7 +583,7 @@
<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>
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 7838119..6ba5626 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -125,14 +125,11 @@
}
}
- 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 @@
v.setImageDrawable(null);
}
- private void vibrate() {
- mView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
- }
-
private boolean isAssistantService() {
return mAssistComponent == null ?
false : mAssistComponent.equals(getVoiceInteractorComponentName());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 8d20772..3e66907 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -303,6 +303,7 @@
try {
ActivityManagerNative.getDefault()
.keyguardWaitingForActivityDrawn();
+ ActivityManagerNative.getDefault().resumeAppSwitches();
} catch (RemoteException e) {
}
}
@@ -315,7 +316,9 @@
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
true /* force */);
visibilityChanged(false);
+ mAssistManager.hideAssist();
}
+
// Wait for activity start.
return handled;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index f5fdf48..6bcb766 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -213,7 +213,8 @@
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 @@
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;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 7f87485..3678cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -106,6 +106,7 @@
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 @@
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 @@
mNotificationPanelDebugText.setVisibility(View.VISIBLE);
}
- updateShowSearchHoldoff();
-
try {
boolean showNav = mWindowManagerService.hasNavigationBar();
if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
@@ -1013,11 +1012,6 @@
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 @@
}
};
- 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 @@
mNavigationBarView.getBackButton().setLongClickable(true);
mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
+ mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener);
mAssistManager.onConfigurationChanged();
}
@@ -1236,10 +1228,6 @@
}
}
- private void updateShowSearchHoldoff() {
- mShowSearchHoldoff = ViewConfiguration.getLongPressTimeout();
- }
-
private void updateNotificationShade() {
if (mStackScroller == null) return;
@@ -2922,7 +2910,6 @@
updateResources();
repositionNavigationBar();
- updateShowSearchHoldoff();
updateRowStates();
mIconController.updateResources();
mScreenPinningRequest.onConfigurationChanged();
@@ -2944,6 +2931,9 @@
if (mZenModeController != null) {
mZenModeController.setUserId(mCurrentUserId);
}
+ if (mSecurityController != null) {
+ mSecurityController.onUserSwitched(mCurrentUserId);
+ }
}
private void resetUserSetupObserver() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 634270c..0e22aa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -53,7 +53,7 @@
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 @@
}
@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
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 6bc51fa..4c99792 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -57,7 +57,7 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
performClick();
}
}
- if (mSupportsLongpress) {
- removeCallbacks(mCheckLongPress);
- }
+ removeCallbacks(mCheckLongPress);
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 962000a..b505d9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -64,6 +64,7 @@
private SparseArray<VpnConfig> mCurrentVpns = new SparseArray<>();
private int mCurrentUserId;
+ private int mVpnUserId;
public SecurityControllerImpl(Context context) {
mContext = context;
@@ -78,7 +79,7 @@
// 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 @@
@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 @@
@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 @@
@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 @@
@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();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 5700732..f98840b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1898,15 +1898,23 @@
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;
}
- onBottom = !pinnedAndClosed;
+ 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;
+ }
}
AnimationEvent event = new AnimationEvent(row, type);
event.headsUpFromBottom = onBottom;
@@ -1916,8 +1924,7 @@
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;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 17e6e3d..5b8fe89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -893,13 +893,12 @@
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);
}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 4fe8fb9..45a7767 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -50,7 +50,9 @@
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.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 @@
}
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 @@
}
private void handleDaemonConnected() {
- resetIfReadyAndConnected();
+ synchronized (mLock) {
+ resetIfReadyAndConnectedLocked();
+ }
/*
* Now that we've done our initialization, release
@@ -1600,7 +1607,7 @@
// reset vold so we bind into new volume into place.
if (Objects.equals(mPrimaryStorageUuid, fsUuid)) {
mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
- resetIfReadyAndConnected();
+ resetIfReadyAndConnectedLocked();
}
writeSettingsLocked();
@@ -1628,7 +1635,7 @@
}
writeSettingsLocked();
- resetIfReadyAndConnected();
+ resetIfReadyAndConnectedLocked();
}
}
@@ -1641,6 +1648,30 @@
}
@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 @@
}
writeSettingsLocked();
- resetIfReadyAndConnected();
+ resetIfReadyAndConnectedLocked();
}
}
@@ -1688,7 +1719,7 @@
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 @@
@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) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f031694..7204a6e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18,7 +18,9 @@
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 @@
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();
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f967aef..4ce5c7e 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -25,7 +25,6 @@
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_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.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.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.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 @@
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 @@
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 @@
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 @@
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;
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index c52a1c1..9ee3bc2 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -30,9 +30,11 @@
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 @@
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 @@
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 @@
private long mHalDeviceId;
private int mFailedAttempts;
private IFingerprintDaemon mDaemon;
+ private PowerManager mPowerManager;
private final Runnable mLockoutReset = new Runnable() {
@Override
@@ -109,6 +114,7 @@
super(context);
mContext = context;
mAppOps = context.getSystemService(AppOpsManager.class);
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
@Override
@@ -191,7 +197,11 @@
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 @@
*/
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 @@
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 @@
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 @@
};
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 @@
@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 @@
mHandler.post(new Runnable() {
@Override
public void run() {
+ mIsKeyguard = KEYGUARD_PACKAGE.equals(opPackageName);
startAuthentication(token, opId, groupId, receiver, flags, restricted);
}
});
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4524ff8..d6a7bf93 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -50,6 +50,7 @@
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.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 @@
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 @@
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
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 3ea384c..b9993b1 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -17,11 +17,13 @@
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 @@
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 @@
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 @@
&& 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
@@ -452,6 +480,15 @@
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()) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index be1afa8..52bf560 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18,6 +18,7 @@
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_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.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.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 @@
// 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 @@
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 @@
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 @@
enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
"grantRuntimePermission");
+ final int uid;
final SettingBase sb;
synchronized (mPackages) {
@@ -3216,6 +3295,7 @@
enforceDeclaredAsUsedAndRuntimePermission(pkg, bp);
+ uid = pkg.applicationInfo.uid;
sb = (SettingBase) pkg.mExtras;
if (sb == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
@@ -3245,11 +3325,22 @@
} 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 @@
}
@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 @@
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 @@
@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 @@
@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,
@@ -13348,15 +13466,15 @@
@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);
}
}
@@ -14159,6 +14277,7 @@
boolean checkin = false;
String packageName = null;
+ ArraySet<String> permissionNames = null;
int opti = 0;
while (opti < args.length) {
@@ -14182,6 +14301,7 @@
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 +14343,18 @@
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 +14637,8 @@
}
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 +14698,11 @@
}
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 +15550,7 @@
if (mInstaller != null) {
mInstaller.createUserConfig(userHandle);
mSettings.createNewUserLILPw(this, mInstaller, userHandle, path);
+ applyFactoryDefaultBrowserLPw(userHandle);
}
}
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
index 04beafd..57ef284 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -219,6 +219,22 @@
}
/**
+ * 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 @@
}
}
- return permissionStates;
+ return permissionStates;
}
private int grantPermission(BasePermission permission, int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index dcd7799..98b41a4 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1130,7 +1130,7 @@
return result;
}
- boolean setDefaultBrowserPackageNameLPr(String packageName, int userId) {
+ boolean setDefaultBrowserPackageNameLPw(String packageName, int userId) {
if (userId == UserHandle.USER_ALL) {
return false;
}
@@ -2710,7 +2710,7 @@
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 +3618,7 @@
UserHandle.getUid(userHandle, ps.appId), userHandle,
ps.pkg.applicationInfo.seinfo);
}
- readDefaultPreferredAppsLPw(service, userHandle);
+ applyDefaultPreferredAppsLPw(service, userHandle);
writePackageRestrictionsLPr(userHandle);
writePackageListLPr(userHandle);
}
@@ -3881,8 +3881,9 @@
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 +3954,13 @@
}
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 +3973,10 @@
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 +4069,9 @@
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 +4095,31 @@
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 +4129,10 @@
&& !packageName.equals(ps.name)) {
continue;
}
+ if (permissionNames != null
+ && !ps.getPermissionsState().hasRequestedPermission(permissionNames)) {
+ continue;
+ }
if (!checkin && packageName != null) {
dumpState.setSharedUser(ps.sharedUser);
@@ -4131,11 +4144,11 @@
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 +4172,7 @@
}
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 +4184,22 @@
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 +4229,17 @@
}
}
- 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 +4257,7 @@
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 +4266,7 @@
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 +4311,15 @@
}
}
- 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 +4328,16 @@
}
}
- 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(
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 51bb36f..5a0bee9 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3117,7 +3117,7 @@
return;
}
- if (eventTime > SystemClock.uptimeMillis()) {
+ if (eventTime > now) {
throw new IllegalArgumentException("event time must not be in the future");
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 06e6a62..e861668 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -211,6 +211,19 @@
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 @@
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);
diff --git a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
index d44afb0..610f30b 100644
--- a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
+++ b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
@@ -18,6 +18,11 @@
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" />
@@ -30,19 +35,29 @@
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"
>
+ <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:text="@string/tree" />
+ <Button android:id="@+id/do_text"
+ android:layout_width="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:layout_gravity="top|right"
- android:text="@string/start"
- />
- <ImageView android:id="@+id/screenshot"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:text="@string/start" />
</LinearLayout>
<LinearLayout android:id="@+id/bottom_content"
@@ -58,26 +73,22 @@
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>
diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml
index 6289929..4cf4104 100644
--- a/tests/VoiceInteraction/res/values/strings.xml
+++ b/tests/VoiceInteraction/res/values/strings.xml
@@ -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>
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
index 439ace8..339755f 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
@@ -20,6 +20,7 @@
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 @@
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 @@
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 @@
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 @@
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();
}
}
}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index 90a781c..97c1e85 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -42,8 +42,11 @@
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 @@
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 @@
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 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 @@
} 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
index b118845..0000000
--- a/tests/WebViewTests/Android.mk
+++ /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
index 8b080c1..0000000
--- a/tests/WebViewTests/AndroidManifest.xml
+++ /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
index d266d21..0000000
--- a/tests/WebViewTests/res/layout/webview_layout.xml
+++ /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
index c2bbdf5..0000000
--- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java
+++ /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
index 2fd42a74d..0000000
--- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java
+++ /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
index 1ecccf6..0000000
--- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java
+++ /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
index 3f0e2b3..0000000
--- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeChildFrameTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * 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
index a0f78a4..0000000
--- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeCoercionTest.java
+++ /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
index 0ccd175..0000000
--- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeFieldsTest.java
+++ /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
index 44d5cc6..0000000
--- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeReturnValuesTest.java
+++ /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
index a451015..0000000
--- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeTestBase.java
+++ /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
index ccfd3d5..0000000
--- a/tests/WebViewTests/src/com/android/webviewtests/WebViewStubActivity.java
+++ /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;
- }
-}