summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/IUiAutomationConnection.aidl4
-rw-r--r--core/java/android/app/Notification.java3
-rw-r--r--core/java/android/app/assist/AssistStructure.java71
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeScanner.java8
-rw-r--r--core/java/android/content/ContentProvider.java106
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java7
-rw-r--r--core/java/android/net/ConnectivityManager.java6
-rw-r--r--core/java/android/net/LinkProperties.java13
-rw-r--r--core/java/android/os/FileUtils.java12
-rw-r--r--core/java/android/os/IDeviceIdleController.aidl1
-rw-r--r--core/java/android/provider/DocumentsProvider.java6
-rw-r--r--core/java/android/provider/Settings.java8
-rw-r--r--core/java/android/text/Hyphenator.java24
-rw-r--r--core/java/android/util/LocalLog.java33
-rw-r--r--core/java/android/view/ViewRootImpl.java5
-rw-r--r--core/java/android/view/inputmethod/InputMethodManagerInternal.java30
-rw-r--r--core/java/android/webkit/WebChromeClient.java8
-rw-r--r--core/java/android/widget/AppSecurityPermissions.java8
-rw-r--r--core/java/android/widget/Editor.java123
-rw-r--r--core/java/android/widget/RelativeLayout.java7
21 files changed, 351 insertions, 134 deletions
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 8ab9ac3..474154b 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -38,10 +38,12 @@ interface IUiAutomationConnection {
boolean injectInputEvent(in InputEvent event, boolean sync);
boolean setRotation(int rotation);
Bitmap takeScreenshot(int width, int height);
- void shutdown();
boolean clearWindowContentFrameStats(int windowId);
WindowContentFrameStats getWindowContentFrameStats(int windowId);
void clearWindowAnimationFrameStats();
WindowAnimationFrameStats getWindowAnimationFrameStats();
void executeShellCommand(String command, in ParcelFileDescriptor fd);
+
+ // Called from the system process.
+ oneway void shutdown();
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b0d8541..fc71783 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1862,6 +1862,9 @@ public class Notification implements Parcelable
} else {
sb.append("null");
}
+ if (this.tickerText != null) {
+ sb.append(" tick");
+ }
sb.append(" defaults=0x");
sb.append(Integer.toHexString(this.defaults));
sb.append(" flags=0x");
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 40126d6..ee0fc91 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -4,6 +4,7 @@ import android.app.Activity;
import android.content.ComponentName;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.os.BadParcelableException;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -31,8 +32,12 @@ public class AssistStructure implements Parcelable {
static final String TAG = "AssistStructure";
static final boolean DEBUG_PARCEL = false;
+ static final boolean DEBUG_PARCEL_CHILDREN = false;
static final boolean DEBUG_PARCEL_TREE = false;
+ static final int VALIDATE_WINDOW_TOKEN = 0x11111111;
+ static final int VALIDATE_VIEW_TOKEN = 0x22222222;
+
boolean mHaveData;
ComponentName mActivityComponent;
@@ -173,6 +178,26 @@ public class AssistStructure implements Parcelable {
mCurViewStackEntry = entry;
}
+ void writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj) {
+ if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition()
+ + ", windows=" + mNumWrittenWindows
+ + ", views=" + mNumWrittenViews
+ + ", level=" + (mCurViewStackPos+levelAdj));
+ out.writeInt(VALIDATE_VIEW_TOKEN);
+ int flags = child.writeSelfToParcel(out, pwriter, mTmpMatrix);
+ mNumWrittenViews++;
+ // If the child has children, push it on the stack to write them next.
+ if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) {
+ if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
+ "Preparing to write " + child.mChildren.length
+ + " children: @ #" + mNumWrittenViews
+ + ", level " + (mCurViewStackPos+levelAdj));
+ out.writeInt(child.mChildren.length);
+ int pos = ++mCurViewStackPos;
+ pushViewStackEntry(child, pos);
+ }
+ }
+
boolean writeNextEntryToParcel(AssistStructure as, Parcel out, PooledStringWriter pwriter) {
// Write next view node if appropriate.
if (mCurViewStackEntry != null) {
@@ -182,20 +207,7 @@ public class AssistStructure implements Parcelable {
+ mCurViewStackEntry.curChild + " in " + mCurViewStackEntry.node);
ViewNode child = mCurViewStackEntry.node.mChildren[mCurViewStackEntry.curChild];
mCurViewStackEntry.curChild++;
- if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition()
- + ", windows=" + mNumWrittenWindows
- + ", views=" + mNumWrittenViews);
- out.writeInt(1);
- int flags = child.writeSelfToParcel(out, pwriter, mTmpMatrix);
- mNumWrittenViews++;
- // If the child has children, push it on the stack to write them next.
- if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) {
- if (DEBUG_PARCEL_TREE) Log.d(TAG, "Preparing to write "
- + child.mChildren.length + " children under " + child);
- out.writeInt(child.mChildren.length);
- int pos = ++mCurViewStackPos;
- pushViewStackEntry(child, pos);
- }
+ writeView(child, out, pwriter, 1);
return true;
}
@@ -223,13 +235,13 @@ public class AssistStructure implements Parcelable {
if (DEBUG_PARCEL) Log.d(TAG, "write window #" + pos + ": at " + out.dataPosition()
+ ", windows=" + mNumWrittenWindows
+ ", views=" + mNumWrittenViews);
- out.writeInt(1);
+ out.writeInt(VALIDATE_WINDOW_TOKEN);
win.writeSelfToParcel(out, pwriter, mTmpMatrix);
mNumWrittenWindows++;
ViewNode root = win.mRoot;
mCurViewStackPos = 0;
- if (DEBUG_PARCEL_TREE) Log.d(TAG, "Pushing initial root view " + root);
- pushViewStackEntry(root, 0);
+ if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing initial root view " + root);
+ writeView(root, out, pwriter, 0);
return true;
}
@@ -271,11 +283,16 @@ public class AssistStructure implements Parcelable {
+ ", views=" + mNumReadViews);
}
- Parcel readParcel() {
+ Parcel readParcel(int validateToken, int level) {
if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
+ ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
- + ", views=" + mNumReadViews);
- if (mCurParcel.readInt() != 0) {
+ + ", views=" + mNumReadViews + ", level=" + level);
+ int token = mCurParcel.readInt();
+ if (token != 0) {
+ if (token != validateToken) {
+ throw new BadParcelableException("Got token " + Integer.toHexString(token)
+ + ", expected token " + Integer.toHexString(validateToken));
+ }
return mCurParcel;
}
// We have run out of partial data, need to read another batch.
@@ -406,7 +423,7 @@ public class AssistStructure implements Parcelable {
}
WindowNode(ParcelTransferReader reader) {
- Parcel in = reader.readParcel();
+ Parcel in = reader.readParcel(VALIDATE_WINDOW_TOKEN, 0);
reader.mNumReadWindows++;
mX = in.readInt();
mY = in.readInt();
@@ -414,7 +431,7 @@ public class AssistStructure implements Parcelable {
mHeight = in.readInt();
mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mDisplayId = in.readInt();
- mRoot = new ViewNode(reader);
+ mRoot = new ViewNode(reader, 0);
}
void writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
@@ -548,8 +565,8 @@ public class AssistStructure implements Parcelable {
ViewNode() {
}
- ViewNode(ParcelTransferReader reader) {
- final Parcel in = reader.readParcel();
+ ViewNode(ParcelTransferReader reader, int nestingLevel) {
+ final Parcel in = reader.readParcel(VALIDATE_VIEW_TOKEN, nestingLevel);
reader.mNumReadViews++;
final PooledStringReader preader = reader.mStringReader;
mClassName = preader.readString();
@@ -604,9 +621,13 @@ public class AssistStructure implements Parcelable {
}
if ((flags&FLAGS_HAS_CHILDREN) != 0) {
final int NCHILDREN = in.readInt();
+ if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
+ "Preparing to read " + NCHILDREN
+ + " children: @ #" + reader.mNumReadViews
+ + ", level " + nestingLevel);
mChildren = new ViewNode[NCHILDREN];
for (int i=0; i<NCHILDREN; i++) {
- mChildren[i] = new ViewNode(reader);
+ mChildren[i] = new ViewNode(reader, nestingLevel + 1);
}
}
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index e09ab56..2ba8774 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -79,6 +79,10 @@ public final class BluetoothLeScanner {
* delivered through {@code callback}.
* <p>
* Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ * An app must hold
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
+ * in order to get results.
*
* @param callback Callback used to deliver scan results.
* @throws IllegalArgumentException If {@code callback} is null.
@@ -95,6 +99,10 @@ public final class BluetoothLeScanner {
* Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
* <p>
* Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ * An app must hold
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
+ * in order to get results.
*
* @param filters {@link ScanFilter}s for finding exact BLE devices.
* @param settings Settings for the scan.
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 3cc7684..ba9cf7c 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -16,8 +16,11 @@
package android.content;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -40,8 +43,8 @@ import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.UserHandle;
-import android.util.Log;
import android.text.TextUtils;
+import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
@@ -474,14 +477,9 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
private int enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken)
throws SecurityException {
- enforceReadPermissionInner(uri, callerToken);
-
- final int permOp = AppOpsManager.permissionToOpCode(mReadPermission);
- if (permOp != AppOpsManager.OP_NONE) {
- final int mode = mAppOpsManager.noteProxyOp(permOp, callingPkg);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- return mode;
- }
+ final int mode = enforceReadPermissionInner(uri, callingPkg, callerToken);
+ if (mode != MODE_ALLOWED) {
+ return mode;
}
if (mReadOp != AppOpsManager.OP_NONE) {
@@ -493,14 +491,9 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
private int enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken)
throws SecurityException {
- enforceWritePermissionInner(uri, callerToken);
-
- final int permOp = AppOpsManager.permissionToOpCode(mWritePermission);
- if (permOp != AppOpsManager.OP_NONE) {
- final int mode = mAppOpsManager.noteProxyOp(permOp, callingPkg);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- return mode;
- }
+ final int mode = enforceWritePermissionInner(uri, callingPkg, callerToken);
+ if (mode != MODE_ALLOWED) {
+ return mode;
}
if (mWriteOp != AppOpsManager.OP_NONE) {
@@ -518,26 +511,47 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
== PERMISSION_GRANTED;
}
+ /**
+ * Verify that calling app holds both the given permission and any app-op
+ * associated with that permission.
+ */
+ private int checkPermissionAndAppOp(String permission, String callingPkg,
+ IBinder callerToken) {
+ if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(),
+ callerToken) != PERMISSION_GRANTED) {
+ return MODE_ERRORED;
+ }
+
+ final int permOp = AppOpsManager.permissionToOpCode(permission);
+ if (permOp != AppOpsManager.OP_NONE) {
+ return mTransport.mAppOpsManager.noteProxyOp(permOp, callingPkg);
+ }
+
+ return MODE_ALLOWED;
+ }
+
/** {@hide} */
- protected void enforceReadPermissionInner(Uri uri, IBinder callerToken)
+ protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken)
throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
String missingPerm = null;
+ int strongestMode = MODE_ALLOWED;
if (UserHandle.isSameApp(uid, mMyUid)) {
- return;
+ return MODE_ALLOWED;
}
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getReadPermission();
if (componentPerm != null) {
- if (context.checkPermission(componentPerm, pid, uid, callerToken)
- == PERMISSION_GRANTED) {
- return;
+ final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken);
+ if (mode == MODE_ALLOWED) {
+ return MODE_ALLOWED;
} else {
missingPerm = componentPerm;
+ strongestMode = Math.max(strongestMode, mode);
}
}
@@ -551,14 +565,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
for (PathPermission pp : pps) {
final String pathPerm = pp.getReadPermission();
if (pathPerm != null && pp.match(path)) {
- if (context.checkPermission(pathPerm, pid, uid, callerToken)
- == PERMISSION_GRANTED) {
- return;
+ final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken);
+ if (mode == MODE_ALLOWED) {
+ return MODE_ALLOWED;
} else {
// any denied <path-permission> means we lose
// default <provider> access.
allowDefaultRead = false;
missingPerm = pathPerm;
+ strongestMode = Math.max(strongestMode, mode);
}
}
}
@@ -566,7 +581,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
// if we passed <path-permission> checks above, and no default
// <provider> permission, then allow access.
- if (allowDefaultRead) return;
+ if (allowDefaultRead) return MODE_ALLOWED;
}
// last chance, check against any uri grants
@@ -575,7 +590,13 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
? maybeAddUserId(uri, callingUserId) : uri;
if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION,
callerToken) == PERMISSION_GRANTED) {
- return;
+ return MODE_ALLOWED;
+ }
+
+ // If the worst denial we found above was ignored, then pass that
+ // ignored through; otherwise we assume it should be a real error below.
+ if (strongestMode == MODE_IGNORED) {
+ return MODE_IGNORED;
}
final String failReason = mExported
@@ -587,25 +608,27 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
/** {@hide} */
- protected void enforceWritePermissionInner(Uri uri, IBinder callerToken)
+ protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken)
throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
String missingPerm = null;
+ int strongestMode = MODE_ALLOWED;
if (UserHandle.isSameApp(uid, mMyUid)) {
- return;
+ return MODE_ALLOWED;
}
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getWritePermission();
if (componentPerm != null) {
- if (context.checkPermission(componentPerm, pid, uid, callerToken)
- == PERMISSION_GRANTED) {
- return;
+ final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken);
+ if (mode == MODE_ALLOWED) {
+ return MODE_ALLOWED;
} else {
missingPerm = componentPerm;
+ strongestMode = Math.max(strongestMode, mode);
}
}
@@ -619,14 +642,15 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
for (PathPermission pp : pps) {
final String pathPerm = pp.getWritePermission();
if (pathPerm != null && pp.match(path)) {
- if (context.checkPermission(pathPerm, pid, uid, callerToken)
- == PERMISSION_GRANTED) {
- return;
+ final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken);
+ if (mode == MODE_ALLOWED) {
+ return MODE_ALLOWED;
} else {
// any denied <path-permission> means we lose
// default <provider> access.
allowDefaultWrite = false;
missingPerm = pathPerm;
+ strongestMode = Math.max(strongestMode, mode);
}
}
}
@@ -634,13 +658,19 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
// if we passed <path-permission> checks above, and no default
// <provider> permission, then allow access.
- if (allowDefaultWrite) return;
+ if (allowDefaultWrite) return MODE_ALLOWED;
}
// last chance, check against any uri grants
if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
callerToken) == PERMISSION_GRANTED) {
- return;
+ return MODE_ALLOWED;
+ }
+
+ // If the worst denial we found above was ignored, then pass that
+ // ignored through; otherwise we assume it should be a real error below.
+ if (strongestMode == MODE_IGNORED) {
+ return MODE_IGNORED;
}
final String failReason = mExported
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ceb610a..bc24d67 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -253,7 +253,7 @@ interface IPackageManager {
List<PackageInfo> getPreferredPackages(int flags);
- void resetPreferredActivities(int userId);
+ void resetApplicationPreferences(int userId);
ResolveInfo getLastChosenActivity(in Intent intent,
String resolvedType, int flags);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6533bbc..cda5816 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1514,6 +1514,13 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device has biometric hardware to detect a fingerprint.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports portrait orientation
* screens. For backwards compatibility, you can assume that if neither
* this nor {@link #FEATURE_SCREEN_LANDSCAPE} is set then the device supports
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index dc8ff8f..ba63969 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -892,7 +892,6 @@ public class ConnectivityManager {
*
* @deprecated Deprecated in favor of the cleaner
* {@link #requestNetwork(NetworkRequest, NetworkCallback)} API.
- * @removed
*/
public int startUsingNetworkFeature(int networkType, String feature) {
NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -939,8 +938,7 @@ public class ConnectivityManager {
* implementation+feature combination, except that the value {@code -1}
* always indicates failure.
*
- * @deprecated Deprecated in favor of the cleaner {@link unregisterNetworkCallback} API.
- * @removed
+ * @deprecated Deprecated in favor of the cleaner {@link #unregisterNetworkCallback} API.
*/
public int stopUsingNetworkFeature(int networkType, String feature) {
NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -1220,7 +1218,6 @@ public class ConnectivityManager {
* @deprecated Deprecated in favor of the
* {@link #requestNetwork(NetworkRequest, NetworkCallback)},
* {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} API.
- * @removed
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
@@ -1239,7 +1236,6 @@ public class ConnectivityManager {
* @hide
* @deprecated Deprecated in favor of the {@link #requestNetwork} and
* {@link #bindProcessToNetwork} API.
- * @removed
*/
public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
try {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index cfd5bf1..c4de4a2 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -662,6 +662,17 @@ public final class LinkProperties implements Parcelable {
}
/**
+ * Returns true if this link or any of its stacked interfaces has an IPv4 address.
+ *
+ * @return {@code true} if there is an IPv4 address, {@code false} otherwise.
+ */
+ private boolean hasIPv4AddressOnInterface(String iface) {
+ return (mIfaceName.equals(iface) && hasIPv4Address()) ||
+ (iface != null && mStackedLinks.containsKey(iface) &&
+ mStackedLinks.get(iface).hasIPv4Address());
+ }
+
+ /**
* Returns true if this link has a global preferred IPv6 address.
*
* @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
@@ -792,7 +803,7 @@ public final class LinkProperties implements Parcelable {
if (ip instanceof Inet4Address) {
// For IPv4, it suffices for now to simply have any address.
- return hasIPv4Address();
+ return hasIPv4AddressOnInterface(bestRoute.getInterface());
} else if (ip instanceof Inet6Address) {
if (ip.isLinkLocalAddress()) {
// For now, just make sure link-local destinations have
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 864225a..af4c2bc 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.NonNull;
import android.provider.DocumentsContract.Document;
import android.system.ErrnoException;
import android.system.Os;
@@ -69,6 +70,8 @@ public class FileUtils {
/** Regular expression for safe filenames: no spaces or metacharacters */
private static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
+ private static final File[] EMPTY = new File[0];
+
/**
* Set owner and mode of of given {@link File}.
*
@@ -634,4 +637,13 @@ public class FileUtils {
return new File(parent, name + "." + ext);
}
}
+
+ public static @NonNull File[] listFilesOrEmpty(File dir) {
+ File[] res = dir.listFiles();
+ if (res != null) {
+ return res;
+ } else {
+ return EMPTY;
+ }
+ }
}
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index b768852..d3eec1e 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -29,5 +29,6 @@ interface IDeviceIdleController {
boolean isPowerSaveWhitelistApp(String name);
void addPowerSaveTempWhitelistApp(String name, long duration, int userId, String reason);
long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason);
+ long addPowerSaveTempWhitelistAppForSms(String name, int userId, String reason);
void exitIdle(String reason);
}
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 6979bee..5a341fc 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -640,7 +640,7 @@ public abstract class DocumentsProvider extends ContentProvider {
final Bundle out = new Bundle();
try {
if (METHOD_CREATE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), null);
final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -654,7 +654,7 @@ public abstract class DocumentsProvider extends ContentProvider {
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
} else if (METHOD_RENAME_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), null);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
final String newDocumentId = renameDocument(documentId, displayName);
@@ -678,7 +678,7 @@ public abstract class DocumentsProvider extends ContentProvider {
}
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), null);
deleteDocument(documentId);
// Document no longer exists, clean up any grants
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fff355b..a79970c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7578,14 +7578,6 @@ public final class Settings {
public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
/**
- * Global override to disable VoLTE (independent of user setting)
- * <p>
- * Type: int (1 for disable VoLTE, 0 to use user configuration)
- * @hide
- */
- public static final String VOLTE_FEATURE_DISABLED = "volte_feature_disabled";
-
- /**
* Whether user can enable/disable LTE as a preferred network. A carrier might control
* this via gservices, OMA-DM, carrier app, etc.
* <p>
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 1ee3827..10a994a 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -45,6 +45,8 @@ public class Hyphenator {
@GuardedBy("sLock")
final static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>();
+ final static Hyphenator sEmptyHyphenator = new Hyphenator(StaticLayout.nLoadHyphenator(""));
+
final private long mNativePtr;
private Hyphenator(long nativePtr) {
@@ -53,19 +55,19 @@ public class Hyphenator {
public static long get(@Nullable Locale locale) {
synchronized (sLock) {
- if (sMap.containsKey(locale)) {
- Hyphenator result = sMap.get(locale);
- return (result == null) ? 0 : result.mNativePtr;
+ Hyphenator result = sMap.get(locale);
+ if (result != null) {
+ return result.mNativePtr;
}
// TODO: Convert this a proper locale-fallback system
// Fall back to language-only, if available
Locale languageOnlyLocale = new Locale(locale.getLanguage());
- if (sMap.containsKey(languageOnlyLocale)) {
- Hyphenator result = sMap.get(languageOnlyLocale);
+ result = sMap.get(languageOnlyLocale);
+ if (result != null) {
sMap.put(locale, result);
- return (result == null) ? 0 : result.mNativePtr;
+ return result.mNativePtr;
}
// Fall back to script-only, if available
@@ -75,16 +77,16 @@ public class Hyphenator {
.setLanguage("und")
.setScript(script)
.build();
- if (sMap.containsKey(scriptOnlyLocale)) {
- Hyphenator result = sMap.get(scriptOnlyLocale);
+ result = sMap.get(scriptOnlyLocale);
+ if (result != null) {
sMap.put(locale, result);
- return (result == null) ? 0 : result.mNativePtr;
+ return result.mNativePtr;
}
}
- sMap.put(locale, null); // To remember we found nothing.
+ sMap.put(locale, sEmptyHyphenator); // To remember we found nothing.
}
- return 0;
+ return sEmptyHyphenator.mNativePtr;
}
private static Hyphenator loadHyphenator(String languageTag) {
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index 39f66a5..a405dab 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -30,20 +30,32 @@ public final class LocalLog {
private LinkedList<String> mLog;
private int mMaxLines;
private long mNow;
+ private final boolean mKeepFirst;
public LocalLog(int maxLines) {
mLog = new LinkedList<String>();
mMaxLines = maxLines;
+ mKeepFirst = false;
+ }
+
+ public LocalLog(int maxLines, boolean keepFirst) {
+ mLog = new LinkedList<String>();
+ mMaxLines = maxLines;
+ mKeepFirst = keepFirst;
}
public synchronized void log(String msg) {
+ mNow = System.currentTimeMillis();
+ StringBuilder sb = new StringBuilder();
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(mNow);
+ sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
+ logStraight(sb.toString() + " - " + msg);
+ }
+
+ private synchronized void logStraight(String msg) {
+ if (mKeepFirst == false || mLog.size() < mMaxLines) mLog.add(msg);
if (mMaxLines > 0) {
- mNow = System.currentTimeMillis();
- StringBuilder sb = new StringBuilder();
- Calendar c = Calendar.getInstance();
- c.setTimeInMillis(mNow);
- sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
- mLog.add(sb.toString() + " - " + msg);
while (mLog.size() > mMaxLines) mLog.remove();
}
}
@@ -74,4 +86,13 @@ public final class LocalLog {
public ReadOnlyLocalLog readOnlyLocalLog() {
return new ReadOnlyLocalLog(this);
}
+
+ public synchronized void copyTo(LocalLog other, int lines) {
+ int end = mLog.size()-1;
+ int begin = end - lines;
+ if (begin < 0) begin = 0;
+ for (; begin < end; begin++) {
+ other.logStraight(mLog.get(begin));
+ }
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9b1db57..c22c0ef 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -555,11 +555,6 @@ public final class ViewRootImpl implements ViewParent,
mPendingContentInsets.set(mAttachInfo.mContentInsets);
mPendingStableInsets.set(mAttachInfo.mStableInsets);
mPendingVisibleInsets.set(0, 0, 0, 0);
- try {
- relayoutWindow(attrs, getHostVisibility(), false);
- } catch (RemoteException e) {
- if (DEBUG_LAYOUT) Log.e(TAG, "failed to relayoutWindow", e);
- }
if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
diff --git a/core/java/android/view/inputmethod/InputMethodManagerInternal.java b/core/java/android/view/inputmethod/InputMethodManagerInternal.java
new file mode 100644
index 0000000..c22127b
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodManagerInternal.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+/**
+ * Input method manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public interface InputMethodManagerInternal {
+ /**
+ * Called by the power manager to tell the input method manager whether it
+ * should start watching for wake events.
+ */
+ public void setInteractive(boolean interactive);
+}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 0b18bb8..4737e9b 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -284,19 +284,13 @@ public class WebChromeClient {
* currently set for that origin. The host application should invoke the
* specified callback with the desired permission state. See
* {@link GeolocationPermissions} for details.
- *
- * If this method isn't overridden, the callback is invoked with permission
- * denied state.
- *
* @param origin The origin of the web content attempting to use the
* Geolocation API.
* @param callback The callback to use to set the permission state for the
* origin.
*/
public void onGeolocationPermissionsShowPrompt(String origin,
- GeolocationPermissions.Callback callback) {
- callback.invoke(origin, false, false);
- }
+ GeolocationPermissions.Callback callback) {}
/**
* Notify the host application that a request for Geolocation permissions,
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index e3ce6f2..a12b15e 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -533,6 +533,12 @@ public class AppSecurityPermissions {
int existingReqFlags) {
final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
final boolean isNormal = (base == PermissionInfo.PROTECTION_NORMAL);
+
+ // We do not show normal permissions in the UI.
+ if (isNormal) {
+ return false;
+ }
+
final boolean isDangerous = (base == PermissionInfo.PROTECTION_DANGEROUS)
|| ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_PRE23) != 0);
final boolean isRequired =
@@ -546,7 +552,7 @@ public class AppSecurityPermissions {
// Dangerous and normal permissions are always shown to the user if the permission
// is required, or it was previously granted
- if ((isNormal || isDangerous) && (isRequired || wasGranted || isGranted)) {
+ if (isDangerous && (isRequired || wasGranted || isGranted)) {
return true;
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 15d13ae..010cb27 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1004,7 +1004,8 @@ public class Editor {
}
if (!handled && mTextActionMode != null) {
- if (touchPositionIsInSelection()) {
+ // TODO: Fix dragging in extracted mode.
+ if (touchPositionIsInSelection() && !mTextView.isInExtractedMode()) {
// Start a drag
final int start = mTextView.getSelectionStart();
final int end = mTextView.getSelectionEnd();
@@ -4059,9 +4060,17 @@ public class Editor {
private float mPrevX;
// Indicates if the handle has moved a boundary between LTR and RTL text.
private boolean mLanguageDirectionChanged = false;
+ // Distance from edge of horizontally scrolling text view
+ // to use to switch to character mode.
+ private final float mTextViewEdgeSlop;
+ // Used to save text view location.
+ private final int[] mTextViewLocation = new int[2];
public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) {
super(drawableLtr, drawableRtl);
+ ViewConfiguration viewConfiguration = ViewConfiguration.get(
+ mTextView.getContext());
+ mTextViewEdgeSlop = viewConfiguration.getScaledTouchSlop() * 4;
}
@Override
@@ -4099,7 +4108,7 @@ public class Editor {
if (layout == null) {
// HandleView will deal appropriately in positionAtCursorOffset when
// layout is null.
- positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false);
+ positionAndAdjustForCrossingHandles(mTextView.getOffsetForPosition(x, y));
return;
}
@@ -4141,12 +4150,12 @@ public class Editor {
// to the current position.
mLanguageDirectionChanged = true;
mTouchWordDelta = 0.0f;
- positionAtCursorOffset(offset, false);
+ positionAndAdjustForCrossingHandles(offset);
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);
+ positionAndAdjustForCrossingHandles(offset);
mTouchWordDelta = 0.0f;
mLanguageDirectionChanged = false;
return;
@@ -4159,6 +4168,21 @@ public class Editor {
}
}
+ if (mTextView.getHorizontallyScrolling()) {
+ if (positionNearEdgeOfScrollingView(x, atRtl)
+ && (mTextView.getScrollX() != 0)
+ && ((isExpanding && offset < selectionStart) || !isExpanding)) {
+ // If we're expanding ensure that the offset is smaller than the
+ // selection start, if the handle snapped to the word, the finger position
+ // may be out of sync and we don't want the selection to jump back.
+ mTouchWordDelta = 0.0f;
+ final int nextOffset = atRtl ? layout.getOffsetToRightOf(mPreviousOffset)
+ : layout.getOffsetToLeftOf(mPreviousOffset);
+ positionAndAdjustForCrossingHandles(nextOffset);
+ return;
+ }
+ }
+
if (isExpanding) {
// User is increasing the selection.
if (!mInWord || currLine < mPrevLine) {
@@ -4214,17 +4238,22 @@ public class Editor {
}
if (positionCursor) {
- // Handles can not cross and selection is at least one character.
- if (offset >= selectionEnd) {
- offset = getNextCursorOffset(selectionEnd, false);
- mTouchWordDelta = 0.0f;
- }
mPreviousLineTouched = currLine;
- positionAtCursorOffset(offset, false);
+ positionAndAdjustForCrossingHandles(offset);
}
mPrevX = x;
}
+ private void positionAndAdjustForCrossingHandles(int offset) {
+ final int selectionEnd = mTextView.getSelectionEnd();
+ if (offset >= selectionEnd) {
+ // Handles can not cross and selection is at least one character.
+ offset = getNextCursorOffset(selectionEnd, false);
+ mTouchWordDelta = 0.0f;
+ }
+ positionAtCursorOffset(offset, false);
+ }
+
@Override
protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
super.positionAtCursorOffset(offset, parentScrolled);
@@ -4242,6 +4271,20 @@ public class Editor {
}
return superResult;
}
+
+ private boolean positionNearEdgeOfScrollingView(float x, boolean atRtl) {
+ mTextView.getLocationOnScreen(mTextViewLocation);
+ boolean nearEdge;
+ if (atRtl) {
+ int rightEdge = mTextViewLocation[0] + mTextView.getWidth()
+ - mTextView.getPaddingRight();
+ nearEdge = x > rightEdge - mTextViewEdgeSlop;
+ } else {
+ int leftEdge = mTextViewLocation[0] + mTextView.getPaddingLeft();
+ nearEdge = x < leftEdge + mTextViewEdgeSlop;
+ }
+ return nearEdge;
+ }
}
private class SelectionEndHandleView extends HandleView {
@@ -4253,9 +4296,17 @@ public class Editor {
private float mPrevX;
// Indicates if the handle has moved a boundary between LTR and RTL text.
private boolean mLanguageDirectionChanged = false;
+ // Distance from edge of horizontally scrolling text view
+ // to use to switch to character mode.
+ private final float mTextViewEdgeSlop;
+ // Used to save the text view location.
+ private final int[] mTextViewLocation = new int[2];
public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) {
super(drawableLtr, drawableRtl);
+ ViewConfiguration viewConfiguration = ViewConfiguration.get(
+ mTextView.getContext());
+ mTextViewEdgeSlop = viewConfiguration.getScaledTouchSlop() * 4;
}
@Override
@@ -4293,7 +4344,7 @@ public class Editor {
if (layout == null) {
// HandleView will deal appropriately in positionAtCursorOffset when
// layout is null.
- positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false);
+ positionAndAdjustForCrossingHandles(mTextView.getOffsetForPosition(x, y));
return;
}
@@ -4335,12 +4386,12 @@ public class Editor {
// to the current position.
mLanguageDirectionChanged = true;
mTouchWordDelta = 0.0f;
- positionAtCursorOffset(offset, false);
+ positionAndAdjustForCrossingHandles(offset);
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);
+ positionAndAdjustForCrossingHandles(offset);
mTouchWordDelta = 0.0f;
mLanguageDirectionChanged = false;
return;
@@ -4353,6 +4404,21 @@ public class Editor {
}
}
+ if (mTextView.getHorizontallyScrolling()) {
+ if (positionNearEdgeOfScrollingView(x, atRtl)
+ && mTextView.canScrollHorizontally(atRtl ? -1 : 1)
+ && ((isExpanding && offset > selectionEnd) || !isExpanding)) {
+ // If we're expanding ensure that the offset is actually greater than the
+ // selection end, if the handle snapped to the word, the finger position
+ // may be out of sync and we don't want the selection to jump back.
+ mTouchWordDelta = 0.0f;
+ final int nextOffset = atRtl ? layout.getOffsetToLeftOf(mPreviousOffset)
+ : layout.getOffsetToRightOf(mPreviousOffset);
+ positionAndAdjustForCrossingHandles(nextOffset);
+ return;
+ }
+ }
+
if (isExpanding) {
// User is increasing the selection.
if (!mInWord || currLine > mPrevLine) {
@@ -4408,17 +4474,22 @@ public class Editor {
}
if (positionCursor) {
- // Handles can not cross and selection is at least one character.
- if (offset <= selectionStart) {
- offset = getNextCursorOffset(selectionStart, true);
- mTouchWordDelta = 0.0f;
- }
mPreviousLineTouched = currLine;
- positionAtCursorOffset(offset, false);
+ positionAndAdjustForCrossingHandles(offset);
}
mPrevX = x;
}
+ private void positionAndAdjustForCrossingHandles(int offset) {
+ final int selectionStart = mTextView.getSelectionStart();
+ if (offset <= selectionStart) {
+ // Handles can not cross and selection is at least one character.
+ offset = getNextCursorOffset(selectionStart, true);
+ mTouchWordDelta = 0.0f;
+ }
+ positionAtCursorOffset(offset, false);
+ }
+
@Override
protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
super.positionAtCursorOffset(offset, parentScrolled);
@@ -4436,6 +4507,20 @@ public class Editor {
}
return superResult;
}
+
+ private boolean positionNearEdgeOfScrollingView(float x, boolean atRtl) {
+ mTextView.getLocationOnScreen(mTextViewLocation);
+ boolean nearEdge;
+ if (atRtl) {
+ int leftEdge = mTextViewLocation[0] + mTextView.getPaddingLeft();
+ nearEdge = x < leftEdge + mTextViewEdgeSlop;
+ } else {
+ int rightEdge = mTextViewLocation[0] + mTextView.getWidth()
+ - mTextView.getPaddingRight();
+ nearEdge = x > rightEdge - mTextViewEdgeSlop;
+ }
+ return nearEdge;
+ }
}
private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index dac02fa..6a561e6 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -731,7 +731,8 @@ public class RelativeLayout extends ViewGroup {
// Negative values in a mySize value in RelativeLayout
// measurement is code for, "we got an unspecified mode in the
// RelativeLayout's measure spec."
- if (mySize < 0 && !mAllowBrokenMeasureSpecs) {
+ final boolean isUnspecified = mySize < 0;
+ if (isUnspecified && !mAllowBrokenMeasureSpecs) {
if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
// Constraints fixed both edges, so child has an exact size.
childSpecSize = Math.max(0, childEnd - childStart);
@@ -767,7 +768,7 @@ public class RelativeLayout extends ViewGroup {
if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
// Constraints fixed both edges, so child must be an exact size.
- childSpecMode = MeasureSpec.EXACTLY;
+ childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
childSpecSize = Math.max(0, maxAvailable);
} else {
if (childSize >= 0) {
@@ -784,7 +785,7 @@ public class RelativeLayout extends ViewGroup {
} else if (childSize == LayoutParams.MATCH_PARENT) {
// Child wanted to be as big as possible. Give all available
// space.
- childSpecMode = MeasureSpec.EXACTLY;
+ childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
childSpecSize = Math.max(0, maxAvailable);
} else if (childSize == LayoutParams.WRAP_CONTENT) {
// Child wants to wrap content. Use AT_MOST to communicate