summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/animation/Animator.java118
-rw-r--r--core/java/android/animation/AnimatorInflater.java41
-rw-r--r--core/java/android/animation/AnimatorSet.java4
-rw-r--r--core/java/android/animation/ValueAnimator.java10
-rw-r--r--core/java/android/annotation/BinderThread.java42
-rw-r--r--core/java/android/annotation/MainThread.java42
-rw-r--r--core/java/android/annotation/RequiresPermission.java104
-rw-r--r--core/java/android/annotation/UiThread.java42
-rw-r--r--core/java/android/annotation/WorkerThread.java42
-rw-r--r--core/java/android/app/Activity.java12
-rw-r--r--core/java/android/app/ActivityManagerInternal.java21
-rw-r--r--core/java/android/app/ActivityManagerNative.java41
-rw-r--r--core/java/android/app/ActivityThread.java131
-rw-r--r--core/java/android/app/AlarmManager.java9
-rw-r--r--core/java/android/app/ApplicationPackageManager.java201
-rw-r--r--core/java/android/app/AssistStructure.java11
-rw-r--r--core/java/android/app/IActivityManager.java5
-rw-r--r--core/java/android/app/IAlarmManager.aidl1
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/app/KeyguardManager.java5
-rw-r--r--core/java/android/app/Notification.java7
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java33
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java52
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl2
-rw-r--r--core/java/android/app/backup/BlobBackupHelper.java2
-rw-r--r--core/java/android/app/usage/IUsageStatsManager.aidl2
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java17
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java1
-rw-r--r--core/java/android/content/ContentProvider.java35
-rw-r--r--core/java/android/content/Context.java1
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java10
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl10
-rw-r--r--core/java/android/content/pm/IPackageMoveObserver.aidl4
-rw-r--r--core/java/android/content/pm/PackageCleanItem.java2
-rw-r--r--core/java/android/content/pm/PackageManager.java59
-rw-r--r--core/java/android/content/pm/ParceledListSlice.java3
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java17
-rw-r--r--core/java/android/hardware/camera2/CameraCaptureSession.java19
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java70
-rw-r--r--core/java/android/hardware/camera2/TotalCaptureResult.java16
-rw-r--r--core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java8
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java24
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java22
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl2
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java10
-rw-r--r--core/java/android/net/ConnectivityManager.java56
-rw-r--r--core/java/android/net/IConnectivityManager.aidl2
-rw-r--r--core/java/android/nfc/cardemulation/ApduServiceInfo.java48
-rw-r--r--core/java/android/nfc/cardemulation/CardEmulation.java31
-rw-r--r--core/java/android/os/FileUtils.java75
-rw-r--r--core/java/android/os/Parcel.java137
-rw-r--r--core/java/android/os/Parcelable.java5
-rw-r--r--core/java/android/os/storage/DiskInfo.java20
-rw-r--r--core/java/android/os/storage/IMountService.java52
-rw-r--r--core/java/android/os/storage/IMountServiceListener.java16
-rw-r--r--core/java/android/os/storage/StorageEventListener.java2
-rw-r--r--core/java/android/os/storage/StorageManager.java49
-rw-r--r--core/java/android/os/storage/VolumeInfo.java4
-rw-r--r--core/java/android/provider/Browser.java21
-rw-r--r--core/java/android/provider/ContactsContract.java144
-rw-r--r--core/java/android/provider/Settings.java36
-rw-r--r--core/java/android/security/NetworkSecurityPolicy.java8
-rw-r--r--core/java/android/service/chooser/ChooserTarget.java36
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java14
-rw-r--r--core/java/android/view/ScaleGestureDetector.java46
-rw-r--r--core/java/android/view/View.java119
-rw-r--r--core/java/android/view/ViewAssistStructure.java2
-rw-r--r--core/java/android/view/ViewDebug.java8
-rw-r--r--core/java/android/view/ViewGroup.java32
-rw-r--r--core/java/android/view/animation/AnimationUtils.java85
-rw-r--r--core/java/android/webkit/WebSettings.java8
-rw-r--r--core/java/android/webkit/WebViewFactory.java38
-rw-r--r--core/java/android/widget/DayPickerView.java18
-rw-r--r--core/java/android/widget/Editor.java95
-rw-r--r--core/java/android/widget/GridView.java27
-rw-r--r--core/java/android/widget/RemoteViewsAdapter.java4
-rw-r--r--core/java/android/widget/SimpleMonthView.java56
-rw-r--r--core/java/android/widget/TextView.java31
-rw-r--r--core/java/android/widget/ZoomButtonsController.java4
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java5
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java6
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java10
-rw-r--r--core/java/com/android/internal/util/Preconditions.java6
-rw-r--r--core/java/com/android/internal/widget/FloatingToolbar.java57
-rw-r--r--core/java/com/android/internal/widget/SwipeDismissLayout.java12
-rw-r--r--core/java/com/android/internal/widget/ViewPager.java575
-rw-r--r--core/java/com/android/server/backup/PreferredActivityBackupHelper.java161
-rw-r--r--core/java/com/android/server/backup/SystemBackupAgent.java5
88 files changed, 2334 insertions, 1144 deletions
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 02a329d..da48709 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -16,12 +16,7 @@
package android.animation;
-import android.content.res.Configuration;
import android.content.res.ConstantState;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.animation.AnimationUtils;
import java.util.ArrayList;
@@ -30,29 +25,6 @@ import java.util.ArrayList;
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
public abstract class Animator implements Cloneable {
- /**
- * Set this hint when duration for the animation does not need to be scaled. By default, no
- * scaling is applied to the duration.
- */
- public static final int HINT_NO_SCALE = 0;
-
- /**
- * Set this scale hint (using {@link #setDurationScaleHint(int, Resources)} when the animation's
- * moving distance is proportional to the screen size. (e.g. a view coming in from the bottom of
- * the screen to top/center). With this scale hint set, the animation duration will be
- * automatically scaled based on screen size.
- */
- public static final int HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE = 1;
-
- /**
- * Set this scale hint (using {@link #setDurationScaleHint(int, Resources)}) if the animation
- * has pre-defined moving distance in dp that does not vary from device to device. This is
- * extremely useful when the animation needs to run on both phones/tablets and TV, because TV
- * has inflated dp and therefore will have a longer visual arc for the same animation than on
- * the phone. This hint is used to calculate a scaling factor to compensate for different
- * visual arcs while maintaining the same angular velocity for the animation.
- */
- public static final int HINT_DISTANCE_DEFINED_IN_DP = 2;
/**
* The set of listeners to be sent events through the life of an animation.
@@ -83,24 +55,6 @@ public abstract class Animator implements Cloneable {
private AnimatorConstantState mConstantState;
/**
- * Scaling factor for an animation that moves across the whole screen.
- */
- float mScreenSizeBasedDurationScale = 1.0f;
-
- /**
- * Scaling factor for an animation that is defined to move the same amount of dp across all
- * devices.
- */
- float mDpBasedDurationScale = 1.0f;
-
- /**
- * By default, the scaling assumes the animation moves across the entire screen.
- */
- int mDurationScaleHint = HINT_NO_SCALE;
-
- private final static boolean ANIM_DEBUG = false;
-
- /**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. A non-delayed animation will have its initial
* value(s) set immediately, followed by calls to
@@ -230,78 +184,6 @@ public abstract class Animator implements Cloneable {
public abstract long getDuration();
/**
- * Hints how duration scaling factor should be calculated. The duration will not be scaled when
- * hint is set to {@link #HINT_NO_SCALE}. Otherwise, the duration will be automatically scaled
- * per device to achieve the same look and feel across different devices. In order to do
- * that, the same angular velocity of the animation will be needed on different devices in
- * users' field of view. Therefore, the duration scale factor is determined by the ratio of the
- * angular movement on current devices to that on the baseline device (i.e. Nexus 5).
- *
- * @param hint an indicator on how the animation is defined. The hint could be
- * {@link #HINT_NO_SCALE}, {@link #HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE} or
- * {@link #HINT_DISTANCE_DEFINED_IN_DP}.
- * @param res The resources {@see android.content.res.Resources} for getting display metrics
- */
- public void setDurationScaleHint(int hint, Resources res) {
- if (ANIM_DEBUG) {
- Log.d("ANIM_DEBUG", "distance based duration hint: " + hint);
- }
- if (hint == mDurationScaleHint) {
- return;
- }
- mDurationScaleHint = hint;
- if (hint != HINT_NO_SCALE) {
- int uiMode = res.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK;
- DisplayMetrics metrics = res.getDisplayMetrics();
- float width = metrics.widthPixels / metrics.xdpi;
- float height = metrics.heightPixels / metrics.ydpi;
- float viewingDistance = AnimationUtils.getViewingDistance(width, height, uiMode);
- if (ANIM_DEBUG) {
- Log.d("ANIM_DEBUG", "width, height, viewing distance, uimode: "
- + width + ", " + height + ", " + viewingDistance + ", " + uiMode);
- }
- mScreenSizeBasedDurationScale = AnimationUtils
- .getScreenSizeBasedDurationScale(width, height, viewingDistance);
- mDpBasedDurationScale = AnimationUtils.getDpBasedDurationScale(
- metrics.density, metrics.xdpi, viewingDistance);
- if (ANIM_DEBUG) {
- Log.d("ANIM_DEBUG", "screen based scale, dp based scale: " +
- mScreenSizeBasedDurationScale + ", " + mDpBasedDurationScale);
- }
- }
- }
-
- // Copies duration scale hint and scaling factors to the new animation.
- void copyDurationScaleInfoTo(Animator anim) {
- anim.mDurationScaleHint = mDurationScaleHint;
- anim.mScreenSizeBasedDurationScale = mScreenSizeBasedDurationScale;
- anim.mDpBasedDurationScale = mDpBasedDurationScale;
- }
-
- /**
- * @return The scaled duration calculated based on distance of movement (as defined by the
- * animation) and perceived velocity (derived from the duration set on the animation for
- * baseline device)
- */
- public long getDistanceBasedDuration() {
- return (long) (getDuration() * getDistanceBasedDurationScale());
- }
-
- /**
- * @return scaling factor of duration based on the duration scale hint. A scaling factor of 1
- * means no scaling will be applied to the duration.
- */
- float getDistanceBasedDurationScale() {
- if (mDurationScaleHint == HINT_DISTANCE_PROPORTIONAL_TO_SCREEN_SIZE) {
- return mScreenSizeBasedDurationScale;
- } else if (mDurationScaleHint == HINT_DISTANCE_DEFINED_IN_DP) {
- return mDpBasedDurationScale;
- } else {
- return 1f;
- }
- }
-
- /**
* The time interpolator used in calculating the elapsed fraction of the
* animation. The interpolator determines whether the animation runs with
* linear or non-linear motion, such as acceleration and deceleration. The
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 224e8e9..427ecce 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -70,13 +70,6 @@ public class AnimatorInflater {
private static final int VALUE_TYPE_COLOR = 3;
private static final int VALUE_TYPE_UNDEFINED = 4;
- /**
- * Enum values used in XML attributes to indicate the duration scale hint.
- */
- private static final int HINT_NO_SCALE = 0;
- private static final int HINT_PROPORTIONAL_TO_SCREEN = 1;
- private static final int HINT_DEFINED_IN_DP = 2;
-
private static final boolean DBG_ANIMATOR_INFLATER = false;
// used to calculate changing configs for resource references
@@ -698,9 +691,6 @@ public class AnimatorInflater {
int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER);
createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering,
pixelSize);
- final int hint = a.getInt(R.styleable.AnimatorSet_durationScaleHint,
- HINT_NO_SCALE);
- anim.setDurationScaleHint(hint, res);
a.recycle();
} else if (name.equals("propertyValuesHolder")) {
PropertyValuesHolder[] values = loadValues(res, theme, parser,
@@ -790,6 +780,31 @@ public class AnimatorInflater {
return valuesArray;
}
+ // When no value type is provided in keyframe, we need to infer the type from the value. i.e.
+ // if value is defined in the style of a color value, then the color type is returned.
+ // Otherwise, default float type is returned.
+ private static int inferValueTypeOfKeyframe(Resources res, Theme theme, AttributeSet attrs) {
+ int valueType;
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, R.styleable.Keyframe, 0, 0);
+ } else {
+ a = res.obtainAttributes(attrs, R.styleable.Keyframe);
+ }
+
+ TypedValue keyframeValue = a.peekValue(R.styleable.Keyframe_value);
+ boolean hasValue = (keyframeValue != null);
+ // When no value type is provided, check whether it's a color type first.
+ // If not, fall back to default value type (i.e. float type).
+ if (hasValue && isColorType(keyframeValue.type)) {
+ valueType = VALUE_TYPE_COLOR;
+ } else {
+ valueType = VALUE_TYPE_FLOAT;
+ }
+ a.recycle();
+ return valueType;
+ }
+
private static void dumpKeyframes(Object[] keyframes, String header) {
if (keyframes == null || keyframes.length == 0) {
return;
@@ -817,6 +832,9 @@ public class AnimatorInflater {
type != XmlPullParser.END_DOCUMENT) {
String name = parser.getName();
if (name.equals("keyframe")) {
+ if (valueType == VALUE_TYPE_UNDEFINED) {
+ valueType = inferValueTypeOfKeyframe(res, theme, Xml.asAttributeSet(parser));
+ }
Keyframe keyframe = loadKeyframe(res, theme, Xml.asAttributeSet(parser), valueType);
if (keyframe != null) {
if (keyframes == null) {
@@ -1037,9 +1055,6 @@ public class AnimatorInflater {
anim.setInterpolator(interpolator);
}
- final int hint = arrayAnimator.getInt(R.styleable.Animator_durationScaleHint,
- HINT_NO_SCALE);
- anim.setDurationScaleHint(hint, res);
arrayAnimator.recycle();
if (arrayObjectAnimator != null) {
arrayObjectAnimator.recycle();
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index dd5f18e..6503d89 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -519,7 +519,6 @@ public final class AnimatorSet extends Animator {
for (Node node : mNodes) {
node.animation.setAllowRunningAsynchronously(false);
- copyDurationScaleInfoTo(node.animation);
}
if (mDuration >= 0) {
@@ -1095,7 +1094,8 @@ public final class AnimatorSet extends Animator {
public Node clone() {
try {
Node node = (Node) super.clone();
- node.animation = (Animator) animation.clone();
+ node.animation = animation.clone();
+ node.done = false;
return node;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 275e78e..a455f8b 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -17,12 +17,9 @@
package android.animation;
import android.annotation.CallSuper;
-import android.content.res.Configuration;
-import android.content.res.Resources;
import android.os.Looper;
import android.os.Trace;
import android.util.AndroidRuntimeException;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Choreographer;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -564,7 +561,7 @@ public class ValueAnimator extends Animator {
}
private void updateScaledDuration() {
- mDuration = (long)(mUnscaledDuration * sDurationScale * getDistanceBasedDurationScale());
+ mDuration = (long)(mUnscaledDuration * sDurationScale);
}
/**
@@ -1486,6 +1483,11 @@ public class ValueAnimator extends Animator {
anim.mPaused = false;
anim.mResumed = false;
anim.mStartListenersCalled = false;
+ anim.mStartTime = 0;
+ anim.mStartTimeCommitted = false;
+ anim.mPauseTime = 0;
+ anim.mCurrentFraction = 0;
+ anim.mDelayStartTime = 0;
PropertyValuesHolder[] oldValues = mValues;
if (oldValues != null) {
diff --git a/core/java/android/annotation/BinderThread.java b/core/java/android/annotation/BinderThread.java
new file mode 100644
index 0000000..c69ba10
--- /dev/null
+++ b/core/java/android/annotation/BinderThread.java
@@ -0,0 +1,42 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method should only be called on the binder thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on the binder thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ * (&#64;BinderThread
+ * public BeamShareData createBeamShareData() { ... }
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface BinderThread {
+} \ No newline at end of file
diff --git a/core/java/android/annotation/MainThread.java b/core/java/android/annotation/MainThread.java
new file mode 100644
index 0000000..18a4283
--- /dev/null
+++ b/core/java/android/annotation/MainThread.java
@@ -0,0 +1,42 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method should only be called on the main thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on the main thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ * &#64;MainThread
+ * public void deliverResult(D data) { ... }
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface MainThread {
+} \ No newline at end of file
diff --git a/core/java/android/annotation/RequiresPermission.java b/core/java/android/annotation/RequiresPermission.java
new file mode 100644
index 0000000..4aed5c1
--- /dev/null
+++ b/core/java/android/annotation/RequiresPermission.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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated element requires (or may require) one or more permissions.
+ * <p/>
+ * Example of requiring a single permission:
+ * <pre>{@code
+ * &#64;RequiresPermission(Manifest.permission.SET_WALLPAPER)
+ * public abstract void setWallpaper(Bitmap bitmap) throws IOException;
+ *
+ * &#64;RequiresPermission(ACCESS_COARSE_LOCATION)
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring at least one permission from a set:
+ * <pre>{@code
+ * &#64;RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring multiple permissions:
+ * <pre>{@code
+ * &#64;RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ * public abstract Location getLastKnownLocation(String provider);
+ * }</pre>
+ * Example of requiring separate read and write permissions for a content provider:
+ * <pre>{@code
+ * &#64;RequiresPermission.Read(&#64;RequiresPermission(READ_HISTORY_BOOKMARKS))
+ * &#64;RequiresPermission.Write(&#64;RequiresPermission(WRITE_HISTORY_BOOKMARKS))
+ * public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
+ * }</pre>
+ *
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD})
+public @interface RequiresPermission {
+ /**
+ * The name of the permission that is required, if precisely one permission
+ * is required. If more than one permission is required, specify either
+ * {@link #allOf()} or {@link #anyOf()} instead.
+ * <p>
+ * If specified, {@link #anyOf()} and {@link #allOf()} must both be null.
+ */
+ String value() default "";
+
+ /**
+ * Specifies a list of permission names that are all required.
+ * <p>
+ * If specified, {@link #anyOf()} and {@link #value()} must both be null.
+ */
+ String[] allOf() default {};
+
+ /**
+ * Specifies a list of permission names where at least one is required
+ * <p>
+ * If specified, {@link #allOf()} and {@link #value()} must both be null.
+ */
+ String[] anyOf() default {};
+
+ /**
+ * If true, the permission may not be required in all cases (e.g. it may only be
+ * enforced on certain platforms, or for certain call parameters, etc.
+ */
+ boolean conditional() default false;
+
+ /**
+ * Specifies that the given permission is required for read operations
+ */
+ @Target(FIELD)
+ @interface Read {
+ RequiresPermission value();
+ }
+
+ /**
+ * Specifies that the given permission is required for write operations
+ */
+ @Target(FIELD)
+ @interface Write {
+ RequiresPermission value();
+ }
+}
diff --git a/core/java/android/annotation/UiThread.java b/core/java/android/annotation/UiThread.java
new file mode 100644
index 0000000..b814600
--- /dev/null
+++ b/core/java/android/annotation/UiThread.java
@@ -0,0 +1,42 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method or constructor should only be called on the UI thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on the UI thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ * &#64;UiThread
+ * public abstract void setText(&#64;NonNull String text) { ... }
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface UiThread {
+} \ No newline at end of file
diff --git a/core/java/android/annotation/WorkerThread.java b/core/java/android/annotation/WorkerThread.java
new file mode 100644
index 0000000..dd12e05
--- /dev/null
+++ b/core/java/android/annotation/WorkerThread.java
@@ -0,0 +1,42 @@
+/*
+ * 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated method should only be called on a worker thread.
+ * If the annotated element is a class, then all methods in the class should be called
+ * on a worker thread.
+ * <p>
+ * Example:
+ * <pre>{@code
+ * (&#64;WorkerThread
+ * protected abstract FilterResults performFiltering(CharSequence constraint);
+ * }</pre>
+ *
+ * {@hide}
+ */
+@Retention(SOURCE)
+@Target({METHOD,CONSTRUCTOR,TYPE})
+public @interface WorkerThread {
+} \ No newline at end of file
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4ccde1c..69cba78 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6483,6 +6483,18 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Shows the user the system defined message for telling the user how to exit
+ * lock task mode. The task containing this activity must be in lock task mode at the time
+ * of this call for the message to be displayed.
+ */
+ public void showLockTaskEscapeMessage() {
+ try {
+ ActivityManagerNative.getDefault().showLockTaskEscapeMessage(mToken);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Interface for informing a translucent {@link Activity} once all visible activities below it
* have completed drawing. This is necessary only after an {@link Activity} has been made
* opaque using {@link Activity#convertFromTranslucent()} and before it has been drawn
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d56dc1e..bde8f39 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -16,6 +16,8 @@
package android.app;
+import android.annotation.NonNull;
+
/**
* Activity manager local system service interface.
*
@@ -27,4 +29,23 @@ public abstract class ActivityManagerInternal {
public abstract int startIsolatedProcess(String entryPoint, String[] mainArgs,
String processName, String abiOverride, int uid, Runnable crashHandler);
+
+ /**
+ * Acquires a sleep token with the specified tag.
+ *
+ * @param tag A string identifying the purpose of the token (eg. "Dream").
+ */
+ public abstract SleepToken acquireSleepToken(@NonNull String tag);
+
+ /**
+ * Sleep tokens cause the activity manager to put the top activity to sleep.
+ * They are used by components such as dreams that may hide and block interaction
+ * with underlying activities.
+ */
+ public static abstract class SleepToken {
+ /**
+ * Releases the sleep token.
+ */
+ public abstract void release();
+ }
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index add7af2..b11c509 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2345,6 +2345,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final IBinder token = data.readStrongBinder();
+ showLockTaskEscapeMessage(token);
+ reply.writeNoException();
+ return true;
+ }
+
case SET_TASK_DESCRIPTION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -2505,6 +2513,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case UPDATE_DEVICE_OWNER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String packageName = data.readString();
+ updateDeviceOwner(packageName);
+ reply.writeNoException();
+ return true;
+ }
+
case GET_PACKAGE_PROCESS_STATE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
@@ -5552,6 +5568,19 @@ class ActivityManagerProxy implements IActivityManager
}
@Override
+ public void showLockTaskEscapeMessage(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION, data, reply,
+ IBinder.FLAG_ONEWAY);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ @Override
public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values)
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -5780,6 +5809,18 @@ class ActivityManagerProxy implements IActivityManager
}
@Override
+ public void updateDeviceOwner(String packageName) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(packageName);
+ mRemote.transact(UPDATE_DEVICE_OWNER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
+ @Override
public int getPackageProcessState(String packageName) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 10d6d01..9bad9bb 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -255,23 +255,18 @@ public final class ActivityThread {
}
}
- static final class AcquiringProviderRecord {
- IActivityManager.ContentProviderHolder holder;
- boolean acquiring = true;
- int requests = 1;
- // Set if there was a runtime exception when trying to acquire the provider.
- RuntimeException runtimeException = null;
- }
-
// The lock of mProviderMap protects the following variables.
- final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<>();
- final ArrayMap<ProviderKey, AcquiringProviderRecord> mAcquiringProviderMap = new ArrayMap<>();
- final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap = new ArrayMap<>();
- final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders = new ArrayMap<>();
- final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName = new ArrayMap<>();
+ final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
+ = new ArrayMap<ProviderKey, ProviderClientRecord>();
+ final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
+ = new ArrayMap<IBinder, ProviderRefCount>();
+ final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
+ = new ArrayMap<IBinder, ProviderClientRecord>();
+ final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
+ = new ArrayMap<ComponentName, ProviderClientRecord>();
final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
- = new ArrayMap<>();
+ = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
final GcIdler mGcIdler = new GcIdler();
boolean mGcIdlerScheduled = false;
@@ -351,7 +346,7 @@ public final class ActivityThread {
}
}
- static final class ProviderClientRecord {
+ final class ProviderClientRecord {
final String[] mNames;
final IContentProvider mProvider;
final ContentProvider mLocalProvider;
@@ -2541,11 +2536,16 @@ public final class ActivityThread {
if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
data.putParcelable(AssistStructure.ASSIST_KEY, new AssistStructure(r.activity));
AssistContent content = new AssistContent();
- Intent intent = new Intent(r.activity.getIntent());
- intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
- intent.removeUnsafeExtras();
- content.setIntent(intent);
+ Intent activityIntent = r.activity.getIntent();
+ if (activityIntent != null) {
+ Intent intent = new Intent(activityIntent);
+ intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
+ intent.removeUnsafeExtras();
+ content.setIntent(intent);
+ } else {
+ content.setIntent(new Intent());
+ }
r.activity.onProvideAssistContent(content);
data.putParcelable(AssistContent.ASSIST_KEY, content);
}
@@ -4711,74 +4711,23 @@ public final class ActivityThread {
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
- final ProviderKey key = new ProviderKey(auth, userId);
- final IContentProvider provider = acquireExistingProvider(c, key, stable);
+ final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
- AcquiringProviderRecord r;
- boolean first = false;
- synchronized (mAcquiringProviderMap) {
- r = mAcquiringProviderMap.get(key);
- if (r == null) {
- r = new AcquiringProviderRecord();
- mAcquiringProviderMap.put(key, r);
- first = true;
- } else {
- r.requests++;
- }
- }
+ // There is a possible race here. Another thread may try to acquire
+ // the same provider at the same time. When this happens, we want to ensure
+ // that the first one wins.
+ // Note that we cannot hold the lock while acquiring and installing the
+ // provider since it might take a long time to run and it could also potentially
+ // be re-entrant in the case where the provider is in the same process.
IActivityManager.ContentProviderHolder holder = null;
try {
- if (first) {
- // Multiple threads may try to acquire the same provider at the same time.
- // When this happens, we only let the first one really gets provider.
- // Other threads just wait for its result.
- // Note that we cannot hold the lock while acquiring and installing the
- // provider since it might take a long time to run and it could also potentially
- // be re-entrant in the case where the provider is in the same process.
- holder = ActivityManagerNative.getDefault().getContentProvider(
- getApplicationThread(), auth, userId, stable);
- } else {
- synchronized (r) {
- while (r.acquiring) {
- try {
- r.wait();
- } catch (InterruptedException e) {
- }
- }
- holder = r.holder;
- }
- }
+ holder = ActivityManagerNative.getDefault().getContentProvider(
+ getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
- } catch (RuntimeException e) {
- synchronized (r) {
- r.runtimeException = e;
- }
- } finally {
- if (first) {
- synchronized (r) {
- r.holder = holder;
- r.acquiring = false;
- r.notifyAll();
- }
- }
-
- synchronized (mAcquiringProviderMap) {
- if (--r.requests == 0) {
- mAcquiringProviderMap.remove(key);
- }
- }
-
- if (r.runtimeException != null) {
- // Was set when the first thread tried to acquire the provider,
- // but we should make sure it is thrown for all threads trying to
- // acquire the provider.
- throw r.runtimeException;
- }
}
-
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
@@ -4861,12 +4810,8 @@ public final class ActivityThread {
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
- return acquireExistingProvider(c, new ProviderKey(auth, userId), stable);
- }
-
- final IContentProvider acquireExistingProvider(
- Context c, ProviderKey key, boolean stable) {
synchronized (mProviderMap) {
+ final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
@@ -4877,7 +4822,7 @@ public final class ActivityThread {
if (!jBinder.isBinderAlive()) {
// The hosting process of the provider has died; we can't
// use this one.
- Log.i(TAG, "Acquiring provider " + key.authority + " for user " + key.userId
+ Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
+ ": existing object's process dead");
handleUnstableProviderDiedLocked(jBinder, true);
return null;
@@ -5199,12 +5144,18 @@ public final class ActivityThread {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, updating ref count");
}
- // The provider has already been installed, so we need
- // to increase reference count to the existing one, but
- // only if release is needed (that is, it is not running
- // in the system process or local to the process).
+ // We need to transfer our new reference to the existing
+ // ref count, releasing the old one... but only if
+ // release is needed (that is, it is not running in the
+ // system process).
if (!noReleaseNeeded) {
incProviderRefLocked(prc, stable);
+ try {
+ ActivityManagerNative.getDefault().removeContentProvider(
+ holder.connection, stable);
+ } catch (RemoteException e) {
+ //do nothing content provider object is dead any way
+ }
}
} else {
ProviderClientRecord client = installProviderAuthoritiesLocked(
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 179957d..9d1d312 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -597,6 +597,15 @@ public class AlarmManager
}
}
+ /** @hide */
+ public long getNextWakeFromIdleTime() {
+ try {
+ return mService.getNextWakeFromIdleTime();
+ } catch (RemoteException ex) {
+ return Long.MAX_VALUE;
+ }
+ }
+
/**
* Gets information about the next alarm clock currently scheduled.
*
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index dfe7e18..10f5960 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -62,6 +62,9 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -81,7 +84,9 @@ import com.android.internal.util.UserIcons;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
/*package*/
final class ApplicationPackageManager extends PackageManager {
@@ -98,6 +103,9 @@ final class ApplicationPackageManager extends PackageManager {
@GuardedBy("mLock")
private PackageInstaller mInstaller;
+ @GuardedBy("mDelegates")
+ private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
+
UserManager getUserManager() {
synchronized (mLock) {
if (mUserManager == null) {
@@ -1410,57 +1418,100 @@ final class ApplicationPackageManager extends PackageManager {
}
@Override
- public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+ public String getInstallerPackageName(String packageName) {
+ try {
+ return mPM.getInstallerPackageName(packageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return null;
+ }
+
+ @Override
+ public int getMoveStatus(int moveId) {
try {
- mPM.movePackage(packageName, observer, flags);
+ return mPM.getMoveStatus(moveId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public void movePackageAndData(String packageName, String volumeUuid,
- IPackageMoveObserver observer) {
+ public void registerMoveCallback(MoveCallback callback, Handler handler) {
+ synchronized (mDelegates) {
+ final MoveCallbackDelegate delegate = new MoveCallbackDelegate(callback,
+ handler.getLooper());
+ try {
+ mPM.registerMoveCallback(delegate);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ mDelegates.add(delegate);
+ }
+ }
+
+ @Override
+ public void unregisterMoveCallback(MoveCallback callback) {
+ synchronized (mDelegates) {
+ for (Iterator<MoveCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) {
+ final MoveCallbackDelegate delegate = i.next();
+ if (delegate.mCallback == callback) {
+ try {
+ mPM.unregisterMoveCallback(delegate);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ i.remove();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int movePackage(String packageName, VolumeInfo vol) {
try {
- mPM.movePackageAndData(packageName, volumeUuid, observer);
+ final String volumeUuid;
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) {
+ volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL;
+ } else if (vol.isPrimaryPhysical()) {
+ volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
+ } else {
+ volumeUuid = Preconditions.checkNotNull(vol.fsUuid);
+ }
+
+ return mPM.movePackage(packageName, volumeUuid);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
+ public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
if (app.isInternal()) {
- return Preconditions.checkNotNull(
- storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));
+ return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
} else if (app.isExternalAsec()) {
- final List<VolumeInfo> vols = storage.getVolumes();
- for (VolumeInfo vol : vols) {
- if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) {
- return vol;
- }
- }
- throw new IllegalStateException("Failed to find primary public volume");
+ return storage.getPrimaryPhysicalVolume();
} else {
- return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid));
+ return storage.findVolumeByUuid(app.volumeUuid);
}
}
@Override
- public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
+ public @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ final VolumeInfo currentVol = getPackageCurrentVolume(app);
final List<VolumeInfo> vols = storage.getVolumes();
final List<VolumeInfo> candidates = new ArrayList<>();
for (VolumeInfo vol : vols) {
- if (isCandidateVolume(app, vol)) {
+ if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(app, vol)) {
candidates.add(vol);
}
}
return candidates;
}
- private static boolean isCandidateVolume(ApplicationInfo app, VolumeInfo vol) {
+ private static boolean isPackageCandidateVolume(ApplicationInfo app, VolumeInfo vol) {
// Private internal is always an option
if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) {
return true;
@@ -1473,10 +1524,14 @@ final class ApplicationPackageManager extends PackageManager {
return false;
}
- // Moving into an ASEC on public primary is only an option when app is
- // internal, or already in ASEC
- if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) {
- return app.isInternal() || app.isExternalAsec();
+ // Gotta be able to write there
+ if (!vol.isMountedWritable()) {
+ return false;
+ }
+
+ // Moving into an ASEC on public primary is only option internal
+ if (vol.isPrimaryPhysical()) {
+ return app.isInternal();
}
// Otherwise we can move to any private volume
@@ -1484,13 +1539,66 @@ final class ApplicationPackageManager extends PackageManager {
}
@Override
- public String getInstallerPackageName(String packageName) {
+ public int movePrimaryStorage(VolumeInfo vol) {
try {
- return mPM.getInstallerPackageName(packageName);
+ final String volumeUuid;
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) {
+ volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL;
+ } else if (vol.isPrimaryPhysical()) {
+ volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
+ } else {
+ volumeUuid = Preconditions.checkNotNull(vol.fsUuid);
+ }
+
+ return mPM.movePrimaryStorage(volumeUuid);
} catch (RemoteException e) {
- // Should never happen!
+ throw e.rethrowAsRuntimeException();
}
- return null;
+ }
+
+ public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ final String volumeUuid = storage.getPrimaryStorageUuid();
+ if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
+ return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
+ } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
+ return storage.getPrimaryPhysicalVolume();
+ } else {
+ return storage.findVolumeByUuid(volumeUuid);
+ }
+ }
+
+ public @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes() {
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ final VolumeInfo currentVol = getPrimaryStorageCurrentVolume();
+ final List<VolumeInfo> vols = storage.getVolumes();
+ final List<VolumeInfo> candidates = new ArrayList<>();
+ for (VolumeInfo vol : vols) {
+ if (Objects.equals(vol, currentVol) || isPrimaryStorageCandidateVolume(vol)) {
+ candidates.add(vol);
+ }
+ }
+ return candidates;
+ }
+
+ private static boolean isPrimaryStorageCandidateVolume(VolumeInfo vol) {
+ // Private internal is always an option
+ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) {
+ return true;
+ }
+
+ // Gotta be able to write there
+ if (!vol.isMountedWritable()) {
+ return false;
+ }
+
+ // We can move to public volumes on legacy devices
+ if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.getDisk().isDefaultPrimary()) {
+ return true;
+ }
+
+ // Otherwise we can move to any private volume
+ return (vol.getType() == VolumeInfo.TYPE_PRIVATE);
}
@Override
@@ -1941,6 +2049,45 @@ final class ApplicationPackageManager extends PackageManager {
return null;
}
+ /** {@hide} */
+ private static class MoveCallbackDelegate extends IPackageMoveObserver.Stub implements
+ Handler.Callback {
+ private static final int MSG_STARTED = 1;
+ private static final int MSG_STATUS_CHANGED = 2;
+
+ final MoveCallback mCallback;
+ final Handler mHandler;
+
+ public MoveCallbackDelegate(MoveCallback callback, Looper looper) {
+ mCallback = callback;
+ mHandler = new Handler(looper, this);
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ final int moveId = msg.arg1;
+ switch (msg.what) {
+ case MSG_STARTED:
+ mCallback.onStarted(moveId, (String) msg.obj);
+ return true;
+ case MSG_STATUS_CHANGED:
+ mCallback.onStatusChanged(moveId, msg.arg2, (long) msg.obj);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onStarted(int moveId, String title) {
+ mHandler.obtainMessage(MSG_STARTED, moveId, 0, title).sendToTarget();
+ }
+
+ @Override
+ public void onStatusChanged(int moveId, int status, long estMillis) {
+ mHandler.obtainMessage(MSG_STATUS_CHANGED, moveId, status, estMillis).sendToTarget();
+ }
+ }
+
private final ContextImpl mContext;
private final IPackageManager mPM;
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index 1e159a3..9946d79 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -218,6 +218,7 @@ final public class AssistStructure implements Parcelable {
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;
@@ -356,6 +357,10 @@ final public class AssistStructure implements Parcelable {
return mFlags&ViewNode.FLAGS_VISIBILITY_MASK;
}
+ public boolean isAssistBlocked() {
+ return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) == 0;
+ }
+
public boolean isEnabled() {
return (mFlags&ViewNode.FLAGS_DISABLED) == 0;
}
@@ -484,6 +489,12 @@ final public class AssistStructure implements Parcelable {
}
@Override
+ public void setAssistBlocked(boolean state) {
+ mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED)
+ | (state ? 0 : ViewNode.FLAGS_ASSIST_BLOCKED);
+ }
+
+ @Override
public void setEnabled(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED)
| (state ? 0 : ViewNode.FLAGS_DISABLED);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 59de281..00558fe 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -467,6 +467,8 @@ public interface IActivityManager extends IInterface {
public int getLockTaskModeState() throws RemoteException;
+ public void showLockTaskEscapeMessage(IBinder token) throws RemoteException;
+
public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values)
throws RemoteException;
public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException;
@@ -493,6 +495,7 @@ public interface IActivityManager extends IInterface {
public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake)
throws RemoteException;
public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
+ public void updateDeviceOwner(String packageName) throws RemoteException;
public int getPackageProcessState(String packageName) throws RemoteException;
@@ -834,4 +837,6 @@ public interface IActivityManager extends IInterface {
int NOTE_ALARM_START_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+291;
int NOTE_ALARM_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+292;
int GET_PACKAGE_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+293;
+ int SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+294;
+ int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295;
}
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index d5719f5..327c00b 100644
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -33,6 +33,7 @@ interface IAlarmManager {
boolean setTime(long millis);
void setTimeZone(String zone);
void remove(in PendingIntent operation);
+ long getNextWakeFromIdleTime();
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index e275df0..ac8d5d8 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -67,6 +67,8 @@ interface INotificationManager
void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
+ void setNotificationsShownFromListener(in INotificationListener token, in String[] keys);
+
ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim);
void requestHintsFromListener(in INotificationListener token, int hints);
int getHintsFromListener(in INotificationListener token);
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 56cd53e..ebb3c43 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -16,6 +16,8 @@
package android.app;
+import android.Manifest;
+import android.annotation.RequiresPermission;
import android.app.trust.ITrustManager;
import android.content.Context;
import android.content.Intent;
@@ -111,6 +113,7 @@ public class KeyguardManager {
*
* @see #reenableKeyguard()
*/
+ @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void disableKeyguard() {
try {
mWM.disableKeyguard(mToken, mTag);
@@ -132,6 +135,7 @@ public class KeyguardManager {
*
* @see #disableKeyguard()
*/
+ @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void reenableKeyguard() {
try {
mWM.reenableKeyguard(mToken);
@@ -302,6 +306,7 @@ public class KeyguardManager {
* once the user has gotten past the keyguard.
*/
@Deprecated
+ @RequiresPermission(Manifest.permission.DISABLE_KEYGUARD)
public void exitKeyguardSecurely(final OnKeyguardExitResult callback) {
try {
mWM.exitKeyguardSecurely(new IOnKeyguardExitResult.Stub() {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e7f8f6d..2cf23af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5648,6 +5648,13 @@ public class Notification implements Parcelable
/**
* Value to be used with {@link #setPricingInformation} to indicate that the content
+ * referred by the notification item is available currently as a pre-order, and the price
+ * value provided is the purchase price for the item.
+ */
+ public static final String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
+
+ /**
+ * Value to be used with {@link #setPricingInformation} to indicate that the content
* referred by the notification item is available as part of a subscription based service,
* and the price value provided is the subscription price for the service.
*/
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index fe284ce..aea413d 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -264,6 +264,20 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
public static final String EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE = "android.app.extra.CHOOSE_PRIVATE_KEY_RESPONSE";
/**
+ * Broadcast action: notify device owner that there is a pending system update.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_NOTIFY_PENDING_SYSTEM_UPDATE = "android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE";
+
+ /**
+ * A long type extra for {@link #onSystemUpdatePending} recording the system time as given by
+ * {@link System#currentTimeMillis()} when the current pending system update is first available.
+ * @hide
+ */
+ public static final String EXTRA_SYSTEM_UPDATE_RECEIVED_TIME = "android.app.extra.SYSTEM_UPDATE_RECEIVED_TIME";
+
+ /**
* Name under which a DevicePolicy component publishes information
* about itself. This meta-data must reference an XML resource containing
* a device-admin tag.
@@ -486,6 +500,22 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
}
/**
+ * Allows the receiver to be notified when information about a pending system update is
+ * available from the system update service. The same pending system update can trigger multiple
+ * calls to this method, so it is necessary to examine the incoming parameters for details about
+ * the update.
+ * <p>
+ * This callback is only applicable to device owners.
+ *
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param receivedTime The time as given by {@link System#currentTimeMillis()} indicating when
+ * the current pending update was first available. -1 if no pending update is available.
+ */
+ public void onSystemUpdatePending(Context context, Intent intent, long receivedTime) {
+ }
+
+ /**
* Intercept standard device administrator broadcasts. Implementations
* should not override this method; it is better to implement the
* convenience callbacks for each action.
@@ -530,6 +560,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
onLockTaskModeExiting(context, intent);
} else if (ACTION_READY_FOR_USER_INITIALIZATION.equals(action)) {
onReadyForUserInitialization(context, intent);
+ } else if (ACTION_NOTIFY_PENDING_SYSTEM_UPDATE.equals(action)) {
+ long receivedTime = intent.getLongExtra(EXTRA_SYSTEM_UPDATE_RECEIVED_TIME, -1);
+ onSystemUpdatePending(context, intent, receivedTime);
}
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9e2da61..47133d4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2651,14 +2651,12 @@ public class DevicePolicyManager {
/**
* @hide
- * Sets the given package as the device owner. The package must already be installed and there
- * shouldn't be an existing device owner registered, for this call to succeed. Also, this
- * method must be called before the device is provisioned.
+ * Sets the given package as the device owner.
+ * Same as {@link #setDeviceOwner(String, String)} but without setting a device owner name.
* @param packageName the package name of the application to be registered as the device owner.
* @return whether the package was successfully registered as the device owner.
* @throws IllegalArgumentException if the package name is null or invalid
- * @throws IllegalStateException if a device owner is already registered or the device has
- * already been provisioned.
+ * @throws IllegalStateException If the preconditions mentioned are not met.
*/
public boolean setDeviceOwner(String packageName) throws IllegalArgumentException,
IllegalStateException {
@@ -2667,15 +2665,17 @@ public class DevicePolicyManager {
/**
* @hide
- * Sets the given package as the device owner. The package must already be installed and there
- * shouldn't be an existing device owner registered, for this call to succeed. Also, this
- * method must be called before the device is provisioned.
+ * Sets the given package as the device owner. The package must already be installed. There
+ * must not already be a device owner.
+ * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
+ * this method.
+ * Calling this after the setup phase of the primary user has completed is allowed only if
+ * the caller is the shell uid, and there are no additional users and no accounts.
* @param packageName the package name of the application to be registered as the device owner.
* @param ownerName the human readable name of the institution that owns this device.
* @return whether the package was successfully registered as the device owner.
* @throws IllegalArgumentException if the package name is null or invalid
- * @throws IllegalStateException if a device owner is already registered or the device has
- * already been provisioned.
+ * @throws IllegalStateException If the preconditions mentioned are not met.
*/
public boolean setDeviceOwner(String packageName, String ownerName)
throws IllegalArgumentException, IllegalStateException {
@@ -2961,14 +2961,18 @@ public class DevicePolicyManager {
/**
* @hide
* Sets the given component as the profile owner of the given user profile. The package must
- * already be installed and there shouldn't be an existing profile owner registered for this
- * user. Only the system can call this API if the user has already completed setup.
+ * already be installed. There must not already be a profile owner for this user.
+ * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
+ * this method.
+ * Calling this after the setup phase of the specified user has completed is allowed only if:
+ * - the caller is SYSTEM_UID.
+ * - or the caller is the shell uid, and there are no accounts on the specified user.
* @param admin the component name to be registered as profile owner.
* @param ownerName the human readable name of the organisation associated with this DPM.
* @param userHandle the userId to set the profile owner for.
* @return whether the component was successfully registered as the profile owner.
- * @throws IllegalArgumentException if admin is null, the package isn't installed, or
- * the user has already been set up.
+ * @throws IllegalArgumentException if admin is null, the package isn't installed, or the
+ * preconditions mentioned are not met.
*/
public boolean setProfileOwner(ComponentName admin, String ownerName, int userHandle)
throws IllegalArgumentException {
@@ -4301,4 +4305,24 @@ public class DevicePolicyManager {
Log.w(TAG, "Failed talking with device policy service", re);
}
}
+
+ /**
+ * Callable by the system update service to notify device owners about pending updates.
+ * The caller must hold {@link android.Manifest.permission#NOTIFY_PENDING_SYSTEM_UPDATE}
+ * permission.
+ *
+ * @param updateReceivedTime The time as given by {@link System#currentTimeMillis()} indicating
+ * when the current pending update was first available. -1 if no update is available.
+ * @hide
+ */
+ @SystemApi
+ public void notifyPendingSystemUpdate(long updateReceivedTime) {
+ if (mService != null) {
+ try {
+ mService.notifyPendingSystemUpdate(updateReceivedTime);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not notify device owner about pending system update", re);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 1f7498e..087fc88 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -225,4 +225,6 @@ interface IDevicePolicyManager {
boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled);
void setStatusBarEnabledState(in ComponentName who, boolean enabled);
boolean getDoNotAskCredentialsOnBoot();
+
+ void notifyPendingSystemUpdate(in long updateReceivedTime);
}
diff --git a/core/java/android/app/backup/BlobBackupHelper.java b/core/java/android/app/backup/BlobBackupHelper.java
index 8e4002d..cdc62dc 100644
--- a/core/java/android/app/backup/BlobBackupHelper.java
+++ b/core/java/android/app/backup/BlobBackupHelper.java
@@ -133,7 +133,7 @@ public abstract class BlobBackupHelper implements BackupHelper {
out.writeInt(mCurrentBlobVersion);
- final int N = state.size();
+ final int N = (state != null) ? state.size() : 0;
out.writeInt(N);
for (int i = 0; i < N; i++) {
out.writeUTF(state.keyAt(i));
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 4ed1489..23659e3 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -30,4 +30,6 @@ interface IUsageStatsManager {
ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
String callingPackage);
UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
+ void setAppIdle(String packageName, boolean idle, int userId);
+ boolean isAppIdle(String packageName, int userId);
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index bc6099a..8a01d66 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -19,6 +19,7 @@ package android.app.usage;
import android.content.Context;
import android.content.pm.ParceledListSlice;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import java.util.Collections;
@@ -217,4 +218,20 @@ public final class UsageStatsManager {
}
return aggregatedStats;
}
+
+ /**
+ * Returns whether the specified app is currently considered idle. This will be true if the
+ * app hasn't been used directly or indirectly for a period of time defined by the system. This
+ * could be of the order of several hours or days.
+ * @param packageName The package name of the app to query
+ * @return whether the app is currently considered idle
+ */
+ public boolean isAppIdle(String packageName) {
+ try {
+ return mService.isAppIdle(packageName, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ // fall through and return default
+ }
+ return false;
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 2418e82..79e560f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -792,7 +792,6 @@ public final class BluetoothAdapter {
// mService is null, handle that case
}
} catch (RemoteException e) {Log.e(TAG, "", e);}
- if (DBG) Log.d(TAG, "" + hashCode() + ": getState() : mService = null. Returning STATE_OFF");
return STATE_OFF;
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 393cf8e..fd65d56 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -26,6 +26,7 @@ import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncTask;
@@ -204,8 +205,13 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
- return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
- CancellationSignal.fromTransport(cancellationSignal));
+ // The caller has no access to the data, so return an empty cursor with
+ // the columns in the requested order. The caller may ask for an invalid
+ // column and we would not catch that but this is not a problem in practice.
+ // We do not call ContentProvider#query with a modified where clause since
+ // the implementation is not guaranteed to be backed by a SQL database, hence
+ // it may not handle properly the tautology where clause we would have created.
+ return new MatrixCursor(projection, 0);
}
final String original = setCallingPackage(callingPkg);
try {
@@ -817,31 +823,6 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
/**
- * @hide
- * Implementation when a caller has performed a query on the content
- * provider, but that call has been rejected for the operation given
- * to {@link #setAppOps(int, int)}. The default implementation
- * rewrites the <var>selection</var> argument to include a condition
- * that is never true (so will always result in an empty cursor)
- * and calls through to {@link #query(android.net.Uri, String[], String, String[],
- * String, android.os.CancellationSignal)} with that.
- */
- public Cursor rejectQuery(Uri uri, String[] projection,
- String selection, String[] selectionArgs, String sortOrder,
- CancellationSignal cancellationSignal) {
- // The read is not allowed... to fake it out, we replace the given
- // selection statement with a dummy one that will always be false.
- // This way we will get a cursor back that has the correct structure
- // but contains no rows.
- if (selection == null || selection.isEmpty()) {
- selection = "'A' = 'B'";
- } else {
- selection = "'A' = 'B' AND (" + selection + ")";
- }
- return query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal);
- }
-
- /**
* Implement this to handle query requests from clients.
* This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 3bf3f85..5eacce3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3098,7 +3098,6 @@ public abstract class Context {
* {@link android.media.midi.MidiManager} for accessing the MIDI service.
*
* @see #getSystemService
- * @hide
*/
public static final String MIDI_SERVICE = "midi";
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index e2701ee..6c32873 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -339,8 +339,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* (e.g., HTTP rather than HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP
* without STARTTLS or TLS). If {@code false}, the app declares that it does not intend to use
* cleartext network traffic, in which case platform components (e.g., HTTP stacks,
- * {@code WebView}, {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to
- * use cleartext traffic. Third-party libraries are encouraged to honor this flag as well.
+ * {@code DownloadManager}, {@code MediaPlayer}) will refuse app's requests to use cleartext
+ * traffic. Third-party libraries are encouraged to honor this flag as well.
+ *
+ * <p>NOTE: {@code WebView} does not honor this flag.
+ *
+ * <p>This flag comes from
+ * {@link android.R.styleable#AndroidManifestApplication_usesCleartextTraffic
+ * android:usesCleartextTraffic} of the &lt;application&gt; tag.
*/
public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 447c668..ae59bfc 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -50,7 +50,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.content.IntentSender;
-import com.android.internal.os.IResultReceiver;
/**
* See {@link PackageManager} for documentation on most of the APIs
@@ -431,8 +430,13 @@ interface IPackageManager {
PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
- void movePackage(String packageName, IPackageMoveObserver observer, int flags);
- void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer);
+ int getMoveStatus(int moveId);
+
+ void registerMoveCallback(in IPackageMoveObserver callback);
+ void unregisterMoveCallback(in IPackageMoveObserver callback);
+
+ int movePackage(in String packageName, in String volumeUuid);
+ int movePrimaryStorage(in String volumeUuid);
boolean addPermissionAsync(in PermissionInfo info);
diff --git a/core/java/android/content/pm/IPackageMoveObserver.aidl b/core/java/android/content/pm/IPackageMoveObserver.aidl
index baa1595..50ab3b5 100644
--- a/core/java/android/content/pm/IPackageMoveObserver.aidl
+++ b/core/java/android/content/pm/IPackageMoveObserver.aidl
@@ -22,6 +22,6 @@ package android.content.pm;
* @hide
*/
oneway interface IPackageMoveObserver {
- void packageMoved(in String packageName, int returnCode);
+ void onStarted(int moveId, String title);
+ void onStatusChanged(int moveId, int status, long estMillis);
}
-
diff --git a/core/java/android/content/pm/PackageCleanItem.java b/core/java/android/content/pm/PackageCleanItem.java
index b1896aa..e1656d6 100644
--- a/core/java/android/content/pm/PackageCleanItem.java
+++ b/core/java/android/content/pm/PackageCleanItem.java
@@ -20,7 +20,7 @@ import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class PackageCleanItem {
+public class PackageCleanItem implements Parcelable {
public final int userId;
public final String packageName;
public final boolean andCode;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a0cec50..e1c271d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -42,6 +42,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
@@ -875,7 +876,8 @@ public abstract class PackageManager {
*
* @hide
*/
- public static final int MOVE_SUCCEEDED = 1;
+ public static final int MOVE_SUCCEEDED = -100;
+
/**
* Error code that is passed to the {@link IPackageMoveObserver} by
* {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
@@ -941,6 +943,7 @@ public abstract class PackageManager {
* been installed on external media.
* @hide
*/
+ @Deprecated
public static final int MOVE_INTERNAL = 0x00000001;
/**
@@ -948,8 +951,12 @@ public abstract class PackageManager {
* the package should be moved to external media.
* @hide
*/
+ @Deprecated
public static final int MOVE_EXTERNAL_MEDIA = 0x00000002;
+ /** {@hide} */
+ public static final String EXTRA_MOVE_ID = "android.content.pm.extra.MOVE_ID";
+
/**
* Usable by the required verifier as the {@code verificationCode} argument
* for {@link PackageManager#verifyPendingInstall} to indicate that it will
@@ -1574,6 +1581,21 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: This is a device dedicated to showing UI
+ * on a vehicle headunit. A headunit here is defined to be inside a
+ * vehicle that may or may not be moving. A headunit uses either a
+ * primary display in the center console and/or additional displays in
+ * the instrument cluster or elsewhere in the vehicle. Headunit display(s)
+ * have limited size and resolution. The user will likely be focused on
+ * driving so limiting driver distraction is a primary concern. User input
+ * can be a variety of hard buttons, touch, rotary controllers and even mouse-
+ * like interfaces.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: This is a device dedicated to showing UI
* on a television. Television here is defined to be a typical living
* room television experience: displayed on a big screen, where the user
* is sitting far away from it, and the dominant form of input will be
@@ -4168,17 +4190,42 @@ public abstract class PackageManager {
* @hide
*/
@Deprecated
- public abstract void movePackage(String packageName, IPackageMoveObserver observer, int flags);
+ public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+ throw new UnsupportedOperationException();
+ }
/** {@hide} */
- public abstract void movePackageAndData(String packageName, String volumeUuid,
- IPackageMoveObserver observer);
+ public static boolean isMoveStatusFinished(int status) {
+ return (status < 0 || status > 100);
+ }
/** {@hide} */
- public abstract @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app);
+ public static abstract class MoveCallback {
+ public abstract void onStarted(int moveId, String title);
+ public abstract void onStatusChanged(int moveId, int status, long estMillis);
+ }
+
+ /** {@hide} */
+ public abstract int getMoveStatus(int moveId);
+
+ /** {@hide} */
+ public abstract void registerMoveCallback(MoveCallback callback, Handler handler);
+ /** {@hide} */
+ public abstract void unregisterMoveCallback(MoveCallback callback);
/** {@hide} */
- public abstract @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app);
+ public abstract int movePackage(String packageName, VolumeInfo vol);
+ /** {@hide} */
+ public abstract @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app);
+ /** {@hide} */
+ public abstract @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app);
+
+ /** {@hide} */
+ public abstract int movePrimaryStorage(VolumeInfo vol);
+ /** {@hide} */
+ public abstract @Nullable VolumeInfo getPrimaryStorageCurrentVolume();
+ /** {@hide} */
+ public abstract @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes();
/**
* Returns the device identity that verifiers can use to associate their scheme to a particular
diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java
index 335a45e..e5c2203 100644
--- a/core/java/android/content/pm/ParceledListSlice.java
+++ b/core/java/android/content/pm/ParceledListSlice.java
@@ -55,6 +55,7 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable {
mList = list;
}
+ @SuppressWarnings("unchecked")
private ParceledListSlice(Parcel p, ClassLoader loader) {
final int N = p.readInt();
mList = new ArrayList<T>(N);
@@ -63,7 +64,7 @@ public class ParceledListSlice<T extends Parcelable> implements Parcelable {
return;
}
- Parcelable.Creator<T> creator = p.readParcelableCreator(loader);
+ Parcelable.Creator<?> creator = p.readParcelableCreator(loader);
Class<?> listElementClass = null;
int i = 0;
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 24a7d33..3cda39a 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -91,8 +91,6 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
- private static final Pattern TRIM_SQL_PATTERN = Pattern.compile("[\\s]*\\n+[\\s]*");
-
private final CloseGuard mCloseGuard = CloseGuard.get();
private final SQLiteConnectionPool mPool;
@@ -1203,7 +1201,11 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
}
private static String trimSqlForDisplay(String sql) {
- return TRIM_SQL_PATTERN.matcher(sql).replaceAll(" ");
+ // Note: Creating and caching a regular expression is expensive at preload-time
+ // and stops compile-time initialization. This pattern is only used when
+ // dumping the connection, which is a rare (mainly error) case. So:
+ // DO NOT CACHE.
+ return sql.replaceAll("[\\s]*\\n+[\\s]*", " ");
}
/**
@@ -1437,9 +1439,6 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
}
private static final class Operation {
- private static final SimpleDateFormat sDateFormat =
- new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-
public long mStartTime;
public long mEndTime;
public String mKind;
@@ -1494,7 +1493,11 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
}
private String getFormattedStartTime() {
- return sDateFormat.format(new Date(mStartTime));
+ // Note: SimpleDateFormat is not thread-safe, cannot be compile-time created, and is
+ // relatively expensive to create during preloading. This method is only used
+ // when dumping a connection, which is a rare (mainly error) case. So:
+ // DO NOT CACHE.
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(mStartTime));
}
}
}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 31e6e25..0cf8df1 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -65,6 +65,12 @@ import java.util.List;
public abstract class CameraCaptureSession implements AutoCloseable {
/**
+ * Used to identify invalid session ID.
+ * @hide
+ */
+ public static final int SESSION_ID_NONE = -1;
+
+ /**
* Get the camera device that this session is created for.
*/
public abstract CameraDevice getDevice();
@@ -168,10 +174,11 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
* configured as outputs for this session; or a reprocess
* capture request is submitted in a non-reprocessible capture
- * session; or the capture targets a Surface in the middle
- * of being {@link #prepare prepared}; or the handler is
- * null, the listener is not null, and the calling thread has
- * no looper.
+ * session; or the reprocess capture request was created with
+ * a {@link TotalCaptureResult} from a different session; or
+ * the capture targets a Surface in the middle of being
+ * {@link #prepare prepared}; or the handler is null, the
+ * listener is not null, and the calling thread has no looper.
*
* @see #captureBurst
* @see #setRepeatingRequest
@@ -226,7 +233,9 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* capture request is submitted in a non-reprocessible capture
* session; or the list of requests contains both requests to
* capture images from the camera and reprocess capture
- * requests; or one of the captures targets a Surface in the
+ * requests; or one of the reprocess capture requests was
+ * created with a {@link TotalCaptureResult} from a different
+ * session; or one of the captures targets a Surface in the
* middle of being {@link #prepare prepared}; or if the handler
* is null, the listener is not null, and the calling thread
* has no looper.
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 35727e8..19d17b1 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -158,6 +158,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
private final HashSet<Surface> mSurfaceSet;
private final CameraMetadataNative mSettings;
private boolean mIsReprocess;
+ // Each reprocess request must be tied to a reprocessible session ID.
+ // Valid only for reprocess requests (mIsReprocess == true).
+ private int mReprocessibleSessionId;
private Object mUserTag;
@@ -170,6 +173,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
mSettings = new CameraMetadataNative();
mSurfaceSet = new HashSet<Surface>();
mIsReprocess = false;
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
/**
@@ -182,6 +186,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
mSettings = new CameraMetadataNative(source.mSettings);
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
mIsReprocess = source.mIsReprocess;
+ mReprocessibleSessionId = source.mReprocessibleSessionId;
mUserTag = source.mUserTag;
}
@@ -189,11 +194,36 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* Take ownership of passed-in settings.
*
* Used by the Builder to create a mutable CaptureRequest.
+ *
+ * @param settings Settings for this capture request.
+ * @param isReprocess Indicates whether to create a reprocess capture request. {@code true}
+ * to create a reprocess capture request. {@code false} to create a regular
+ * capture request.
+ * @param reprocessibleSessionId The ID of the camera capture session this capture is created
+ * for. This is used to validate if the application submits a
+ * reprocess capture request to the same session where
+ * the {@link TotalCaptureResult}, used to create the reprocess
+ * capture, came from.
+ *
+ * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
+ * reprocessibleSessionId.
+ *
+ * @see CameraDevice#createReprocessCaptureRequest
*/
- private CaptureRequest(CameraMetadataNative settings, boolean isReprocess) {
+ private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
+ int reprocessibleSessionId) {
mSettings = CameraMetadataNative.move(settings);
mSurfaceSet = new HashSet<Surface>();
mIsReprocess = isReprocess;
+ if (isReprocess) {
+ if (reprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) {
+ throw new IllegalArgumentException("Create a reprocess capture request with an " +
+ "invalid session ID: " + reprocessibleSessionId);
+ }
+ mReprocessibleSessionId = reprocessibleSessionId;
+ } else {
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
+ }
}
/**
@@ -277,6 +307,23 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
}
/**
+ * Get the reprocessible session ID this reprocess capture request is associated with.
+ *
+ * @return the reprocessible session ID this reprocess capture request is associated with
+ *
+ * @throws IllegalStateException if this capture request is not a reprocess capture request.
+ * @hide
+ */
+ public int getReprocessibleSessionId() {
+ if (mIsReprocess == false ||
+ mReprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) {
+ throw new IllegalStateException("Getting the reprocessible session ID for a "+
+ "non-reprocess capture request is illegal.");
+ }
+ return mReprocessibleSessionId;
+ }
+
+ /**
* Determine whether this CaptureRequest is equal to another CaptureRequest.
*
* <p>A request is considered equal to another is if it's set of key/values is equal, it's
@@ -298,7 +345,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
&& Objects.equals(mUserTag, other.mUserTag)
&& mSurfaceSet.equals(other.mSurfaceSet)
&& mSettings.equals(other.mSettings)
- && mIsReprocess == other.mIsReprocess;
+ && mIsReprocess == other.mIsReprocess
+ && mReprocessibleSessionId == other.mReprocessibleSessionId;
}
@Override
@@ -347,6 +395,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
}
mIsReprocess = (in.readInt() == 0) ? false : true;
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
@Override
@@ -397,10 +446,23 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* Initialize the builder using the template; the request takes
* ownership of the template.
*
+ * @param template Template settings for this capture request.
+ * @param reprocess Indicates whether to create a reprocess capture request. {@code true}
+ * to create a reprocess capture request. {@code false} to create a regular
+ * capture request.
+ * @param reprocessibleSessionId The ID of the camera capture session this capture is
+ * created for. This is used to validate if the application
+ * submits a reprocess capture request to the same session
+ * where the {@link TotalCaptureResult}, used to create the
+ * reprocess capture, came from.
+ *
+ * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
+ * reprocessibleSessionId.
* @hide
*/
- public Builder(CameraMetadataNative template, boolean reprocess) {
- mRequest = new CaptureRequest(template, reprocess);
+ public Builder(CameraMetadataNative template, boolean reprocess,
+ int reprocessibleSessionId) {
+ mRequest = new CaptureRequest(template, reprocess, reprocessibleSessionId);
}
/**
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index 6f7dd78..fb3c098 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -50,6 +50,7 @@ import java.util.List;
public final class TotalCaptureResult extends CaptureResult {
private final List<CaptureResult> mPartialResults;
+ private final int mSessionId;
/**
* Takes ownership of the passed-in camera metadata and the partial results
@@ -58,7 +59,7 @@ public final class TotalCaptureResult extends CaptureResult {
* @hide
*/
public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
- CaptureResultExtras extras, List<CaptureResult> partials) {
+ CaptureResultExtras extras, List<CaptureResult> partials, int sessionId) {
super(results, parent, extras);
if (partials == null) {
@@ -66,6 +67,8 @@ public final class TotalCaptureResult extends CaptureResult {
} else {
mPartialResults = partials;
}
+
+ mSessionId = sessionId;
}
/**
@@ -78,6 +81,7 @@ public final class TotalCaptureResult extends CaptureResult {
super(results, sequenceId);
mPartialResults = new ArrayList<>();
+ mSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
/**
@@ -95,4 +99,14 @@ public final class TotalCaptureResult extends CaptureResult {
public List<CaptureResult> getPartialResults() {
return Collections.unmodifiableList(mPartialResults);
}
+
+ /**
+ * Get the ID of the session where the capture request of this result was submitted.
+ *
+ * @return The session ID
+ * @hide
+ */
+ public int getSessionId() {
+ return mSessionId;
+ }
}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index c74204d..3c19529 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -156,9 +156,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
} else if (request.isReprocess() && !isReprocessible()) {
throw new IllegalArgumentException("this capture session cannot handle reprocess " +
"requests");
+ } else if (request.isReprocess() && request.getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another session");
}
-
checkNotClosed();
handler = checkHandler(handler, callback);
@@ -185,12 +186,17 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
if (reprocess && !isReprocessible()) {
throw new IllegalArgumentException("this capture session cannot handle reprocess " +
"requests");
+ } else if (reprocess && requests.get(0).getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another session");
}
for (int i = 1; i < requests.size(); i++) {
if (requests.get(i).isReprocess() != reprocess) {
throw new IllegalArgumentException("cannot mix regular and reprocess capture " +
" requests");
+ } else if (reprocess && requests.get(i).getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another " +
+ "session");
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 1e680dfd..ff4ad79 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -585,8 +585,8 @@ public class CameraDeviceImpl extends CameraDevice {
return null;
}
- CaptureRequest.Builder builder =
- new CaptureRequest.Builder(templatedRequest, /*reprocess*/false);
+ CaptureRequest.Builder builder = new CaptureRequest.Builder(
+ templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
return builder;
}
@@ -601,7 +601,8 @@ public class CameraDeviceImpl extends CameraDevice {
CameraMetadataNative resultMetadata = new
CameraMetadataNative(inputResult.getNativeCopy());
- return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true);
+ return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
+ inputResult.getSessionId());
}
}
@@ -763,7 +764,7 @@ public class CameraDeviceImpl extends CameraDevice {
if (callback != null) {
mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback,
- requestList, handler, repeating));
+ requestList, handler, repeating, mNextSessionId - 1));
} else {
if (DEBUG) {
Log.d(TAG, "Listen for request " + requestId + " is null");
@@ -1095,9 +1096,10 @@ public class CameraDeviceImpl extends CameraDevice {
private final CaptureCallback mCallback;
private final List<CaptureRequest> mRequestList;
private final Handler mHandler;
+ private final int mSessionId;
CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
- Handler handler, boolean repeating) {
+ Handler handler, boolean repeating, int sessionId) {
if (callback == null || handler == null) {
throw new UnsupportedOperationException(
"Must have a valid handler and a valid callback");
@@ -1106,6 +1108,7 @@ public class CameraDeviceImpl extends CameraDevice {
mHandler = handler;
mRequestList = new ArrayList<CaptureRequest>(requestList);
mCallback = callback;
+ mSessionId = sessionId;
}
public boolean isRepeating() {
@@ -1140,6 +1143,10 @@ public class CameraDeviceImpl extends CameraDevice {
return mHandler;
}
+ public int getSessionId() {
+ return mSessionId;
+ }
+
}
/**
@@ -1643,8 +1650,8 @@ public class CameraDeviceImpl extends CameraDevice {
List<CaptureResult> partialResults =
mFrameNumberTracker.popPartialResults(frameNumber);
- final TotalCaptureResult resultAsCapture =
- new TotalCaptureResult(result, request, resultExtras, partialResults);
+ final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
+ request, resultExtras, partialResults, holder.getSessionId());
// Final capture result
resultDispatch = new Runnable() {
@@ -1665,7 +1672,8 @@ public class CameraDeviceImpl extends CameraDevice {
holder.getHandler().post(resultDispatch);
// Collect the partials for a total result; or mark the frame as totally completed
- mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, isReprocess);
+ mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
+ isReprocess);
// Fire onCaptureSequenceCompleted
if (!isPartialResult) {
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 0d7b261..2257b0a 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -587,6 +587,25 @@ public class FingerprintManager {
return false;
}
+ /**
+ * Retrieves the authenticator token for binding keys to the lifecycle
+ * of the current set of fingerprints. Used only by internal clients.
+ *
+ * @hide
+ */
+ public long getAuthenticatorId() {
+ if (mService != null) {
+ try {
+ return mService.getAuthenticatorId();
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e);
+ }
+ } else {
+ Log.w(TAG, "getAuthenticatorId(): Service not connected!");
+ }
+ return 0;
+ }
+
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch(msg.what) {
@@ -792,4 +811,5 @@ public class FingerprintManager {
}
};
-} \ No newline at end of file
+}
+
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 51a0e4c..c5ec08c 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -63,4 +63,6 @@ interface IFingerprintService {
// Gets the unique device id for hardware enumerated at i
// long getHardwareDevice(int i);
+ // Gets the authenticator ID for fingerprint
+ long getAuthenticatorId();
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 481fc2f..1b57055 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1440,7 +1440,8 @@ public class InputMethodService extends AbstractInputMethodService {
void showWindowInner(boolean showInput) {
boolean doShowInput = false;
- boolean wasVisible = mWindowVisible;
+ final int previousImeWindowStatus =
+ (mWindowVisible ? IME_ACTIVE : 0) | (isInputViewShown() ? IME_VISIBLE : 0);
mWindowVisible = true;
if (!mShowInputRequested) {
if (mInputStarted) {
@@ -1485,9 +1486,12 @@ public class InputMethodService extends AbstractInputMethodService {
startExtractingText(false);
}
- if (!wasVisible) {
+ final int nextImeWindowStatus = IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
+ if (previousImeWindowStatus != nextImeWindowStatus) {
+ mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
+ }
+ if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
- mImm.setImeWindowStatus(mToken, IME_ACTIVE, mBackDisposition);
onWindowShown();
mWindow.show();
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 3e9b3d6..63f48cf 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -305,6 +305,9 @@ public class ConnectivityManager {
* same network interface as {@link #TYPE_MOBILE} or it may use a different
* one. This is used by applications needing to talk to the carrier's
* Multimedia Messaging Service servers.
+ *
+ * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability.
*/
public static final int TYPE_MOBILE_MMS = 2;
/**
@@ -312,6 +315,9 @@ public class ConnectivityManager {
* same network interface as {@link #TYPE_MOBILE} or it may use a different
* one. This is used by applications needing to talk to the carrier's
* Secure User Plane Location servers for help locating the device.
+ *
+ * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability.
*/
public static final int TYPE_MOBILE_SUPL = 3;
/**
@@ -324,9 +330,10 @@ public class ConnectivityManager {
/**
* A High Priority Mobile data connection. This network type uses the
* same network interface as {@link #TYPE_MOBILE} but the routing setup
- * is different. Only requesting processes will have access to the
- * Mobile DNS servers and only IP's explicitly requested via {@link #requestRouteToHost}
- * will route over this interface if no default route exists.
+ * is different.
+ *
+ * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport.
*/
public static final int TYPE_MOBILE_HIPRI = 5;
/**
@@ -386,7 +393,7 @@ public class ConnectivityManager {
*/
public static final int TYPE_MOBILE_IA = 14;
-/**
+ /**
* Emergency PDN connection for emergency calls
* {@hide}
*/
@@ -736,7 +743,7 @@ public class ConnectivityManager {
}
/**
- * Returns an array of of {@link NetworkCapabilities} objects, representing
+ * Returns an array of {@link android.net.NetworkCapabilities} objects, representing
* the Networks that applications run by the given user will use by default.
* @hide
*/
@@ -826,11 +833,11 @@ public class ConnectivityManager {
}
/**
- * Get the {@link NetworkCapabilities} for the given {@link Network}. This
+ * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This
* will return {@code null} if the network is unknown.
*
* @param network The {@link Network} object identifying the network in question.
- * @return The {@link NetworkCapabilities} for the network, or {@code null}.
+ * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
*/
public NetworkCapabilities getNetworkCapabilities(Network network) {
try {
@@ -854,6 +861,7 @@ public class ConnectivityManager {
* always indicates failure.
*
* @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
+ * @removed
*/
public int startUsingNetworkFeature(int networkType, String feature) {
NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -901,6 +909,7 @@ public class ConnectivityManager {
* always indicates failure.
*
* @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
+ * @removed
*/
public int stopUsingNetworkFeature(int networkType, String feature) {
NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -1179,6 +1188,7 @@ public class ConnectivityManager {
*
* @deprecated Deprecated in favor of the {@link #requestNetwork},
* {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} api.
+ * @removed
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
@@ -1197,6 +1207,7 @@ 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 {
@@ -2057,7 +2068,7 @@ public class ConnectivityManager {
* changes capabilities but still satisfies the stated need.
*
* @param network The {@link Network} whose capabilities have changed.
- * @param networkCapabilities The new {@link NetworkCapabilities} for this network.
+ * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this network.
*/
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {}
@@ -2299,7 +2310,7 @@ public class ConnectivityManager {
}
/**
- * Request a network to satisfy a set of {@link NetworkCapabilities}.
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This {@link NetworkRequest} will live until released via
* {@link #unregisterNetworkCallback} or the calling application exits.
@@ -2318,7 +2329,7 @@ public class ConnectivityManager {
}
/**
- * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
* by a timeout.
*
* This function behaves identically to the non-timedout version, but if a suitable
@@ -2365,7 +2376,7 @@ public class ConnectivityManager {
/**
- * Request a network to satisfy a set of {@link NetworkCapabilities}.
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This function behaves identically to the version that takes a NetworkCallback, but instead
* of {@link NetworkCallback} a {@link PendingIntent} is used. This means
@@ -2441,17 +2452,21 @@ public class ConnectivityManager {
}
/**
- * Request connectivityservice to refresh network capabilities for the given
- * {@link network}. This method returns true if the network is still active, false
- * otherwise. Notice the method call assumes the caller has registered for
- * listening NetworkCapabilities updates.
+ * Requests bandwidth update for a given {@link Network} and returns whether the update request
+ * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
+ * network connection for updated bandwidth information. The caller will be notified via
+ * {@link ConnectivityManager.NetworkCallback} if there is an update. Notice that this
+ * method assumes that the caller has previously called {@link #registerNetworkCallback} to
+ * listen for network changes.
*
* @param network{@link Network} specifying which network you're interested.
+ * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+ *
* @hide
*/
- public boolean requestBwUpdate(Network network) {
+ public boolean requestBandwidthUpdate(Network network) {
try {
- return mService.requestBwUpdate(network);
+ return mService.requestBandwidthUpdate(network);
} catch (RemoteException e) {
return false;
}
@@ -2563,7 +2578,12 @@ public class ConnectivityManager {
if (NetworkUtils.bindProcessToNetwork(netId)) {
// Set HTTP proxy system properties to match network.
// TODO: Deprecate this static method and replace it with a non-static version.
- Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy());
+ try {
+ Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy());
+ } catch (SecurityException e) {
+ // The process doesn't have ACCESS_NETWORK_STATE, so we can't fetch the proxy.
+ Log.e(TAG, "Can't set proxy properties", e);
+ }
// Must flush DNS cache as new network may have different DNS resolutions.
InetAddress.clearDnsCache();
// Must flush socket pool as idle sockets will be bound to previous network and may
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d6c0693..efc76b3 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -134,7 +134,7 @@ interface IConnectivityManager
void registerNetworkFactory(in Messenger messenger, in String name);
- boolean requestBwUpdate(in Network network);
+ boolean requestBandwidthUpdate(in Network network);
void unregisterNetworkFactory(in Messenger messenger);
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index f10e530..3d065e3 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -17,6 +17,7 @@
package android.nfc.cardemulation;
import android.content.ComponentName;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -28,6 +29,7 @@ import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.ResultReceiver;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
@@ -88,12 +90,24 @@ public final class ApduServiceInfo implements Parcelable {
* The uid of the package the service belongs to
*/
final int mUid;
+
+ /**
+ * Whether this service has dynamic resources
+ */
+ final boolean mHasDynamicResources;
+
+ /**
+ * Settings Activity for this service
+ */
+ final String mSettingsActivityName;
+
/**
* @hide
*/
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
- boolean requiresUnlock, int bannerResource, int uid) {
+ boolean requiresUnlock, int bannerResource, int uid, boolean hasDynamicResources,
+ String settingsActivityName) {
this.mService = info;
this.mDescription = description;
this.mStaticAidGroups = new HashMap<String, AidGroup>();
@@ -108,6 +122,8 @@ public final class ApduServiceInfo implements Parcelable {
}
this.mBannerResourceId = bannerResource;
this.mUid = uid;
+ this.mHasDynamicResources = hasDynamicResources;
+ this.mSettingsActivityName = settingsActivityName;
}
public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws
@@ -156,6 +172,10 @@ public final class ApduServiceInfo implements Parcelable {
false);
mBannerResourceId = sa.getResourceId(
com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1);
+ mHasDynamicResources = sa.getBoolean(
+ com.android.internal.R.styleable.HostApduService_dynamicResources, false);
+ mSettingsActivityName = sa.getString(
+ com.android.internal.R.styleable.HostApduService_settingsActivity);
sa.recycle();
} else {
TypedArray sa = res.obtainAttributes(attrs,
@@ -166,6 +186,10 @@ public final class ApduServiceInfo implements Parcelable {
mRequiresDeviceUnlock = false;
mBannerResourceId = sa.getResourceId(
com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1);
+ mHasDynamicResources = sa.getBoolean(
+ com.android.internal.R.styleable.OffHostApduService_dynamicResources, false);
+ mSettingsActivityName = sa.getString(
+ com.android.internal.R.styleable.HostApduService_settingsActivity);
sa.recycle();
}
@@ -359,6 +383,15 @@ public final class ApduServiceInfo implements Parcelable {
return mService.loadLabel(pm);
}
+ public CharSequence loadAppLabel(PackageManager pm) {
+ try {
+ return pm.getApplicationLabel(pm.getApplicationInfo(
+ mService.resolvePackageName, PackageManager.GET_META_DATA));
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
public Drawable loadIcon(PackageManager pm) {
return mService.loadIcon(pm);
}
@@ -377,6 +410,11 @@ public final class ApduServiceInfo implements Parcelable {
return null;
}
}
+ public boolean hasDynamicResources() {
+ return mHasDynamicResources;
+ }
+
+ public String getSettingsActivityName() { return mSettingsActivityName; }
@Override
public String toString() {
@@ -430,6 +468,8 @@ public final class ApduServiceInfo implements Parcelable {
dest.writeInt(mRequiresDeviceUnlock ? 1 : 0);
dest.writeInt(mBannerResourceId);
dest.writeInt(mUid);
+ dest.writeInt(mHasDynamicResources ? 1 : 0);
+ dest.writeString(mSettingsActivityName);
};
public static final Parcelable.Creator<ApduServiceInfo> CREATOR =
@@ -452,8 +492,11 @@ public final class ApduServiceInfo implements Parcelable {
boolean requiresUnlock = source.readInt() != 0;
int bannerResource = source.readInt();
int uid = source.readInt();
+ boolean dynamicResources = source.readInt() != 0;
+ String settingsActivityName = source.readString();
return new ApduServiceInfo(info, onHost, description, staticAidGroups,
- dynamicAidGroups, requiresUnlock, bannerResource, uid);
+ dynamicAidGroups, requiresUnlock, bannerResource, uid, dynamicResources,
+ settingsActivityName);
}
@Override
@@ -479,5 +522,6 @@ public final class ApduServiceInfo implements Parcelable {
pw.println(" AID: " + aid);
}
}
+ pw.println(" Settings Activity: " + mSettingsActivityName);
}
}
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 64c2bc2..b94d4a6 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -90,6 +90,37 @@ public final class CardEmulation {
public static final String CATEGORY_OTHER = "other";
/**
+ * Ordered broadcast that can be sent to your app to
+ * request a description and banner to be shown in
+ * Android Settings UI.
+ * When sent to you, this broadcast will contain the
+ * {@link #EXTRA_SERVICE_COMPONENT} extra to identify
+ * the service.
+ *
+ * Note that this broadcast will only be sent to your
+ * app, if a card emulation service in your app has requested
+ * its resources to be loaded dynamically.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_REQUEST_SERVICE_RESOURCES =
+ "android.nfc.cardemulation.action.REQUEST_SERVICE_RESOURCES";
+
+ /**
+ * The description of the service. Note that this must
+ * be localized by your app, as the String will be shown
+ * as is.
+ */
+ public static final String EXTRA_DESCRIPTION =
+ "android.nfc.cardemulation.extra.DESCRIPTION";
+
+ /**
+ * The resource ID of the service banner to be shown
+ * for this service.
+ */
+ public static final String EXTRA_BANNER_RES_ID =
+ "android.nfc.cardemulation.extra.BANNER_RES_ID";
+
+ /**
* Return value for {@link #getSelectionModeForCategory(String)}.
*
* <p>In this mode, the user has set a default service for this
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index b302f95..931cd3e 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,11 +16,13 @@
package android.os;
+import android.provider.DocumentsContract.Document;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
+import android.webkit.MimeTypeMap;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
@@ -34,6 +36,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Objects;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
@@ -533,4 +536,76 @@ public class FileUtils {
}
return null;
}
+
+ /**
+ * Generates a unique file name under the given parent directory. If the display name doesn't
+ * have an extension that matches the requested MIME type, the default extension for that MIME
+ * type is appended. If a file already exists, the name is appended with a numerical value to
+ * make it unique.
+ *
+ * For example, the display name 'example' with 'text/plain' MIME might produce
+ * 'example.txt' or 'example (1).txt', etc.
+ *
+ * @throws FileNotFoundException
+ */
+ public static File buildUniqueFile(File parent, String mimeType, String displayName)
+ throws FileNotFoundException {
+ String name;
+ String ext;
+
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ name = displayName;
+ ext = null;
+ } else {
+ String mimeTypeFromExt;
+
+ // Extract requested extension from display name
+ final int lastDot = displayName.lastIndexOf('.');
+ if (lastDot >= 0) {
+ name = displayName.substring(0, lastDot);
+ ext = displayName.substring(lastDot + 1);
+ mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+ ext.toLowerCase());
+ } else {
+ name = displayName;
+ ext = null;
+ mimeTypeFromExt = null;
+ }
+
+ if (mimeTypeFromExt == null) {
+ mimeTypeFromExt = "application/octet-stream";
+ }
+
+ final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
+ mimeType);
+ if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
+ // Extension maps back to requested MIME type; allow it
+ } else {
+ // No match; insist that create file matches requested MIME
+ name = displayName;
+ ext = extFromMimeType;
+ }
+ }
+
+ File file = buildFile(parent, name, ext);
+
+ // If conflicting file, try adding counter suffix
+ int n = 0;
+ while (file.exists()) {
+ if (n++ >= 32) {
+ throw new FileNotFoundException("Failed to create unique file");
+ }
+ file = buildFile(parent, name + " (" + n + ")", ext);
+ }
+
+ return file;
+ }
+
+ private static File buildFile(File parent, String name, String ext) {
+ if (TextUtils.isEmpty(ext)) {
+ return new File(parent, name);
+ } else {
+ return new File(parent, name + "." + ext);
+ }
+ }
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index af23f11..8c1f44f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -34,6 +34,7 @@ import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -114,15 +115,15 @@ import java.util.Set;
* later reading.</p>
*
* <p>There are also some methods that provide a more efficient way to work
- * with Parcelables: {@link #writeTypedArray},
- * {@link #writeTypedList(List)},
- * {@link #readTypedArray} and {@link #readTypedList}. These methods
+ * with Parcelables: {@link #writeTypedObject}, {@link #writeTypedArray},
+ * {@link #writeTypedList}, {@link #readTypedObject},
+ * {@link #createTypedArray} and {@link #createTypedArrayList}. These methods
* do not write the class information of the original object: instead, the
* caller of the read function must know what type to expect and pass in the
* appropriate {@link Parcelable.Creator Parcelable.Creator} instead to
* properly construct the new object and read its data. (To more efficient
- * write and read a single Parceable object, you can directly call
- * {@link Parcelable#writeToParcel Parcelable.writeToParcel} and
+ * write and read a single Parceable object that is not null, you can directly
+ * call {@link Parcelable#writeToParcel Parcelable.writeToParcel} and
* {@link Parcelable.Creator#createFromParcel Parcelable.Creator.createFromParcel}
* yourself.)</p>
*
@@ -1222,6 +1223,24 @@ public final class Parcel {
}
/**
+ * Flatten the Parcelable object into the parcel.
+ *
+ * @param val The Parcelable object to be written.
+ * @param parcelableFlags Contextual flags as per
+ * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+ *
+ * @see #readTypedObject
+ */
+ public final <T extends Parcelable> void writeTypedObject(T val, int parcelableFlags) {
+ if (val != null) {
+ writeInt(1);
+ val.writeToParcel(this, parcelableFlags);
+ } else {
+ writeInt(0);
+ }
+ }
+
+ /**
* Flatten a generic object in to a parcel. The given Object value may
* currently be one of the following types:
*
@@ -1375,8 +1394,7 @@ public final class Parcel {
writeString(null);
return;
}
- String name = p.getClass().getName();
- writeString(name);
+ writeParcelableCreator(p);
p.writeToParcel(this, parcelableFlags);
}
@@ -2138,6 +2156,25 @@ public final class Parcel {
}
/**
+ * Read and return a typed Parcelable object from a parcel.
+ * Returns null if the previous written object was null.
+ * The object <em>must</em> have previous been written via
+ * {@link #writeTypedObject} with the same object type.
+ *
+ * @return A newly created object of the type that was previously
+ * written.
+ *
+ * @see #writeTypedObject
+ */
+ public final <T> T readTypedObject(Parcelable.Creator<T> c) {
+ if (readInt() != 0) {
+ return c.createFromParcel(this);
+ } else {
+ return null;
+ }
+ }
+
+ /**
* Write a heterogeneous array of Parcelable objects into the Parcel.
* Each object in the array is written along with its class name, so
* that the correct class can later be instantiated. As a result, this
@@ -2277,78 +2314,94 @@ public final class Parcel {
* @throws BadParcelableException Throws BadParcelableException if there
* was an error trying to instantiate the Parcelable.
*/
+ @SuppressWarnings("unchecked")
public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
- Parcelable.Creator<T> creator = readParcelableCreator(loader);
+ Parcelable.Creator<?> creator = readParcelableCreator(loader);
if (creator == null) {
return null;
}
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
- return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader);
+ Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+ (Parcelable.ClassLoaderCreator<?>) creator;
+ return (T) classLoaderCreator.createFromParcel(this, loader);
}
- return creator.createFromParcel(this);
+ return (T) creator.createFromParcel(this);
}
/** @hide */
- public final <T extends Parcelable> T readCreator(Parcelable.Creator<T> creator,
+ @SuppressWarnings("unchecked")
+ public final <T extends Parcelable> T readCreator(Parcelable.Creator<?> creator,
ClassLoader loader) {
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
- return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader);
+ Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+ (Parcelable.ClassLoaderCreator<?>) creator;
+ return (T) classLoaderCreator.createFromParcel(this, loader);
}
- return creator.createFromParcel(this);
+ return (T) creator.createFromParcel(this);
}
/** @hide */
- public final <T extends Parcelable> Parcelable.Creator<T> readParcelableCreator(
- ClassLoader loader) {
+ public final Parcelable.Creator<?> readParcelableCreator(ClassLoader loader) {
String name = readString();
if (name == null) {
return null;
}
- Parcelable.Creator<T> creator;
+ Parcelable.Creator<?> creator;
synchronized (mCreators) {
- HashMap<String,Parcelable.Creator> map = mCreators.get(loader);
+ HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader);
if (map == null) {
- map = new HashMap<String,Parcelable.Creator>();
+ map = new HashMap<>();
mCreators.put(loader, map);
}
creator = map.get(name);
if (creator == null) {
try {
- Class c = loader == null ?
- Class.forName(name) : Class.forName(name, true, loader);
- Field f = c.getField("CREATOR");
- creator = (Parcelable.Creator)f.get(null);
+ // If loader == null, explicitly emulate Class.forName(String) "caller
+ // classloader" behavior.
+ ClassLoader parcelableClassLoader =
+ (loader == null ? getClass().getClassLoader() : loader);
+ // Avoid initializing the Parcelable class until we know it implements
+ // Parcelable and has the necessary CREATOR field. http://b/1171613.
+ Class<?> parcelableClass = Class.forName(name, false /* initialize */,
+ parcelableClassLoader);
+ if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
+ throw new BadParcelableException("Parcelable protocol requires that the "
+ + "class implements Parcelable");
+ }
+ Field f = parcelableClass.getField("CREATOR");
+ if ((f.getModifiers() & Modifier.STATIC) == 0) {
+ throw new BadParcelableException("Parcelable protocol requires "
+ + "the CREATOR object to be static on class " + name);
+ }
+ Class<?> creatorType = f.getType();
+ if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) {
+ // Fail before calling Field.get(), not after, to avoid initializing
+ // parcelableClass unnecessarily.
+ throw new BadParcelableException("Parcelable protocol requires a "
+ + "Parcelable.Creator object called "
+ + "CREATOR on class " + name);
+ }
+ creator = (Parcelable.Creator<?>) f.get(null);
}
catch (IllegalAccessException e) {
- Log.e(TAG, "Illegal access when unmarshalling: "
- + name, e);
+ Log.e(TAG, "Illegal access when unmarshalling: " + name, e);
throw new BadParcelableException(
"IllegalAccessException when unmarshalling: " + name);
}
catch (ClassNotFoundException e) {
- Log.e(TAG, "Class not found when unmarshalling: "
- + name, e);
+ Log.e(TAG, "Class not found when unmarshalling: " + name, e);
throw new BadParcelableException(
"ClassNotFoundException when unmarshalling: " + name);
}
- catch (ClassCastException e) {
- throw new BadParcelableException("Parcelable protocol requires a "
- + "Parcelable.Creator object called "
- + " CREATOR on class " + name);
- }
catch (NoSuchFieldException e) {
throw new BadParcelableException("Parcelable protocol requires a "
- + "Parcelable.Creator object called "
- + " CREATOR on class " + name);
- }
- catch (NullPointerException e) {
- throw new BadParcelableException("Parcelable protocol requires "
- + "the CREATOR object to be static on class " + name);
+ + "Parcelable.Creator object called "
+ + "CREATOR on class " + name);
}
if (creator == null) {
throw new BadParcelableException("Parcelable protocol requires a "
- + "Parcelable.Creator object called "
- + " CREATOR on class " + name);
+ + "non-null Parcelable.Creator object called "
+ + "CREATOR on class " + name);
}
map.put(name, creator);
@@ -2371,7 +2424,7 @@ public final class Parcel {
}
Parcelable[] p = new Parcelable[N];
for (int i = 0; i < N; i++) {
- p[i] = (Parcelable) readParcelable(loader);
+ p[i] = readParcelable(loader);
}
return p;
}
@@ -2426,8 +2479,8 @@ public final class Parcel {
// Cache of previously looked up CREATOR.createFromParcel() methods for
// particular classes. Keys are the names of the classes, values are
// Method objects.
- private static final HashMap<ClassLoader,HashMap<String,Parcelable.Creator>>
- mCreators = new HashMap<ClassLoader,HashMap<String,Parcelable.Creator>>();
+ private static final HashMap<ClassLoader,HashMap<String,Parcelable.Creator<?>>>
+ mCreators = new HashMap<>();
/** @hide for internal use only. */
static protected final Parcel obtain(int obj) {
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 594fbb2..448b591 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -19,9 +19,8 @@ package android.os;
/**
* Interface for classes whose instances can be written to
* and restored from a {@link Parcel}. Classes implementing the Parcelable
- * interface must also have a static field called <code>CREATOR</code>, which
- * is an object implementing the {@link Parcelable.Creator Parcelable.Creator}
- * interface.
+ * interface must also have a non-null static field called <code>CREATOR</code>
+ * of a type that implements the {@link Parcelable.Creator} interface.
*
* <p>A typical implementation of Parcelable is:</p>
*
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index 9f38de8..9623695 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -36,7 +36,10 @@ import java.util.Objects;
* @hide
*/
public class DiskInfo implements Parcelable {
- public static final String EXTRA_DISK_ID = "android.os.storage.extra.DISK_ID";
+ public static final String ACTION_DISK_SCANNED =
+ "android.os.storage.action.DISK_SCANNED";
+ public static final String EXTRA_DISK_ID =
+ "android.os.storage.extra.DISK_ID";
public static final int FLAG_ADOPTABLE = 1 << 0;
public static final int FLAG_DEFAULT_PRIMARY = 1 << 1;
@@ -68,6 +71,9 @@ public class DiskInfo implements Parcelable {
if (TextUtils.isEmpty(label)) {
return false;
}
+ if (label.equalsIgnoreCase("ata")) {
+ return false;
+ }
if (label.toLowerCase().contains("generic")) {
return false;
}
@@ -93,6 +99,14 @@ public class DiskInfo implements Parcelable {
}
}
+ public boolean isAdoptable() {
+ return (flags & FLAG_ADOPTABLE) != 0;
+ }
+
+ public boolean isDefaultPrimary() {
+ return (flags & FLAG_DEFAULT_PRIMARY) != 0;
+ }
+
public boolean isSd() {
return (flags & FLAG_SD) != 0;
}
@@ -101,10 +115,6 @@ public class DiskInfo implements Parcelable {
return (flags & FLAG_USB) != 0;
}
- public boolean isAdoptable() {
- return (flags & FLAG_ADOPTABLE) != 0;
- }
-
@Override
public String toString() {
final CharArrayWriter writer = new CharArrayWriter();
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 0a8187e..0b1031c 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1063,6 +1063,38 @@ public interface IMountService extends IInterface {
_data.recycle();
}
}
+
+ @Override
+ public String getPrimaryStorageUuid() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ String _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_getPrimaryStorageUuid, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readString();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
+ @Override
+ public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(volumeUuid);
+ mRemote.transact(Stub.TRANSACTION_setPrimaryStorageUuid, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
}
private static final String DESCRIPTOR = "IMountService";
@@ -1169,6 +1201,9 @@ public interface IMountService extends IInterface {
static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 52;
static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 53;
+ static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 54;
+ static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 55;
+
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -1669,6 +1704,20 @@ public interface IMountService extends IInterface {
reply.writeNoException();
return true;
}
+ case TRANSACTION_getPrimaryStorageUuid: {
+ data.enforceInterface(DESCRIPTOR);
+ String volumeUuid = getPrimaryStorageUuid();
+ reply.writeNoException();
+ reply.writeString(volumeUuid);
+ return true;
+ }
+ case TRANSACTION_setPrimaryStorageUuid: {
+ data.enforceInterface(DESCRIPTOR);
+ String volumeUuid = data.readString();
+ setPrimaryStorageUuid(volumeUuid);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -1969,4 +2018,7 @@ public interface IMountService extends IInterface {
public void setVolumeNickname(String volId, String nickname) throws RemoteException;
public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException;
+
+ public String getPrimaryStorageUuid() throws RemoteException;
+ public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException;
}
diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java
index 8e878a4..fcb4779 100644
--- a/core/java/android/os/storage/IMountServiceListener.java
+++ b/core/java/android/os/storage/IMountServiceListener.java
@@ -98,10 +98,11 @@ public interface IMountServiceListener extends IInterface {
reply.writeNoException();
return true;
}
- case TRANSACTION_onDiskUnsupported: {
+ case TRANSACTION_onDiskScanned: {
data.enforceInterface(DESCRIPTOR);
final DiskInfo disk = (DiskInfo) data.readParcelable(null);
- onDiskUnsupported(disk);
+ final int volumeCount = data.readInt();
+ onDiskScanned(disk, volumeCount);
reply.writeNoException();
return true;
}
@@ -207,13 +208,14 @@ public interface IMountServiceListener extends IInterface {
}
@Override
- public void onDiskUnsupported(DiskInfo disk) throws RemoteException {
+ public void onDiskScanned(DiskInfo disk, int volumeCount) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeParcelable(disk, 0);
- mRemote.transact(Stub.TRANSACTION_onDiskUnsupported, _data, _reply,
+ _data.writeInt(volumeCount);
+ mRemote.transact(Stub.TRANSACTION_onDiskScanned, _data, _reply,
android.os.IBinder.FLAG_ONEWAY);
_reply.readException();
} finally {
@@ -224,12 +226,10 @@ public interface IMountServiceListener extends IInterface {
}
static final int TRANSACTION_onUsbMassStorageConnectionChanged = (IBinder.FIRST_CALL_TRANSACTION + 0);
-
static final int TRANSACTION_onStorageStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 1);
-
static final int TRANSACTION_onVolumeStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_onVolumeMetadataChanged = (IBinder.FIRST_CALL_TRANSACTION + 3);
- static final int TRANSACTION_onDiskUnsupported = (IBinder.FIRST_CALL_TRANSACTION + 4);
+ static final int TRANSACTION_onDiskScanned = (IBinder.FIRST_CALL_TRANSACTION + 4);
}
/**
@@ -255,5 +255,5 @@ public interface IMountServiceListener extends IInterface {
public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException;
- public void onDiskUnsupported(DiskInfo disk) throws RemoteException;
+ public void onDiskScanned(DiskInfo disk, int volumeCount) throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java
index ad2fae0..6a0140e 100644
--- a/core/java/android/os/storage/StorageEventListener.java
+++ b/core/java/android/os/storage/StorageEventListener.java
@@ -44,6 +44,6 @@ public class StorageEventListener {
public void onVolumeMetadataChanged(VolumeInfo vol) {
}
- public void onDiskUnsupported(DiskInfo disk) {
+ public void onDiskScanned(DiskInfo disk, int volumeCount) {
}
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index efa3ef2..747fb40 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -69,6 +69,13 @@ public class StorageManager {
/** {@hide} */
public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
+ /** {@hide} */
+ public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
+
+ /** {@hide} */
+ public static final String UUID_PRIVATE_INTERNAL = null;
+ /** {@hide} */
+ public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
/** {@hide} */
public static final int FLAG_ALL_METADATA = 1 << 0;
@@ -87,7 +94,7 @@ public class StorageManager {
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
private static final int MSG_VOLUME_METADATA_CHANGED = 3;
- private static final int MSG_DISK_UNSUPPORTED = 4;
+ private static final int MSG_DISK_SCANNED = 4;
final StorageEventListener mCallback;
final Handler mHandler;
@@ -114,8 +121,8 @@ public class StorageManager {
mCallback.onVolumeMetadataChanged((VolumeInfo) args.arg1);
args.recycle();
return true;
- case MSG_DISK_UNSUPPORTED:
- mCallback.onDiskUnsupported((DiskInfo) args.arg1);
+ case MSG_DISK_SCANNED:
+ mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
args.recycle();
return true;
}
@@ -154,10 +161,11 @@ public class StorageManager {
}
@Override
- public void onDiskUnsupported(DiskInfo disk) {
+ public void onDiskScanned(DiskInfo disk, int volumeCount) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = disk;
- mHandler.obtainMessage(MSG_DISK_UNSUPPORTED, args).sendToTarget();
+ args.argi2 = volumeCount;
+ mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget();
}
}
@@ -532,17 +540,26 @@ public class StorageManager {
/** {@hide} */
public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
String descrip = vol.getDescription();
-
if (vol.disk != null) {
if (TextUtils.isEmpty(descrip)) {
descrip = vol.disk.getDescription();
}
}
-
return descrip;
}
/** {@hide} */
+ public @Nullable VolumeInfo getPrimaryPhysicalVolume() {
+ final List<VolumeInfo> vols = getVolumes();
+ for (VolumeInfo vol : vols) {
+ if (vol.isPrimaryPhysical()) {
+ return vol;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
public void mount(String volId) {
try {
mMountService.mount(volId);
@@ -626,6 +643,24 @@ public class StorageManager {
}
/** {@hide} */
+ public String getPrimaryStorageUuid() {
+ try {
+ return mMountService.getPrimaryStorageUuid();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void setPrimaryStorageUuid(String volumeUuid) {
+ try {
+ mMountService.setPrimaryStorageUuid(volumeUuid);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
public @Nullable StorageVolume getStorageVolume(File file) {
return getStorageVolume(getVolumeList(), file);
}
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index f3498d5..4e9cfc7 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -242,6 +242,10 @@ public class VolumeInfo implements Parcelable {
return (mountFlags & MOUNT_FLAG_PRIMARY) != 0;
}
+ public boolean isPrimaryPhysical() {
+ return isPrimary() && (getType() == TYPE_PUBLIC);
+ }
+
public boolean isVisible() {
return (mountFlags & MOUNT_FLAG_VISIBLE) != 0;
}
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 69a05c4..bae06b8 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.annotation.RequiresPermission;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -32,6 +33,9 @@ import android.provider.BrowserContract.Searches;
import android.util.Log;
import android.webkit.WebIconDatabase;
+import static android.Manifest.permission.READ_HISTORY_BOOKMARKS;
+import static android.Manifest.permission.WRITE_HISTORY_BOOKMARKS;
+
public class Browser {
private static final String LOGTAG = "browser";
@@ -41,6 +45,8 @@ public class Browser {
* {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it
* requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission.
*/
+ @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
/**
@@ -122,6 +128,8 @@ public class Browser {
* {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it
* requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission.
*/
+ @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri SEARCHES_URI = Uri.parse("content://browser/searches");
/**
@@ -233,6 +241,7 @@ public class Browser {
*
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final Cursor getAllBookmarks(ContentResolver cr) throws
IllegalStateException {
return cr.query(Bookmarks.CONTENT_URI,
@@ -248,6 +257,7 @@ public class Browser {
*
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final Cursor getAllVisitedUrls(ContentResolver cr) throws
IllegalStateException {
return cr.query(Combined.CONTENT_URI,
@@ -308,6 +318,7 @@ public class Browser {
* @param real If true, this is an actual visit, and should add to the
* number of visits. If false, the user entered it manually.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
public static final void updateVisitedHistory(ContentResolver cr,
String url, boolean real) {
long now = System.currentTimeMillis();
@@ -358,6 +369,7 @@ public class Browser {
* @param cr The ContentResolver used to access the database.
* @hide pending API council approval
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final String[] getVisitedHistory(ContentResolver cr) {
Cursor c = null;
String[] str = null;
@@ -393,6 +405,7 @@ public class Browser {
*
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
public static final void truncateHistory(ContentResolver cr) {
// TODO make a single request to the provider to do this in a single transaction
Cursor cursor = null;
@@ -424,6 +437,7 @@ public class Browser {
* @param cr The ContentResolver used to access the database.
* @return boolean True if the history can be cleared.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final boolean canClearHistory(ContentResolver cr) {
Cursor cursor = null;
boolean ret = false;
@@ -446,6 +460,7 @@ public class Browser {
* Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void clearHistory(ContentResolver cr) {
deleteHistoryWhere(cr, null);
}
@@ -461,6 +476,7 @@ public class Browser {
* @param whereClause String to limit the items affected.
* null means all items.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
private static final void deleteHistoryWhere(ContentResolver cr, String whereClause) {
Cursor cursor = null;
try {
@@ -486,6 +502,7 @@ public class Browser {
* @param end Last date to remove. If -1, all dates after begin.
* Non-inclusive.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void deleteHistoryTimeFrame(ContentResolver cr,
long begin, long end) {
String whereClause;
@@ -511,6 +528,7 @@ public class Browser {
* @param cr The ContentResolver used to access the database.
* @param url url to remove.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void deleteFromHistory(ContentResolver cr,
String url) {
cr.delete(History.CONTENT_URI, History.URL + "=?", new String[] { url });
@@ -523,6 +541,7 @@ public class Browser {
* @param cr The ContentResolver used to access the database.
* @param search The string to add to the searches database.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
public static final void addSearchUrl(ContentResolver cr, String search) {
// The content provider will take care of updating existing searches instead of duplicating
ContentValues values = new ContentValues();
@@ -536,6 +555,7 @@ public class Browser {
* Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void clearSearches(ContentResolver cr) {
// FIXME: Should this clear the urls to which these searches lead?
// (i.e. remove google.com/query= blah blah blah)
@@ -557,6 +577,7 @@ public class Browser {
* @param listener IconListener that gets the icons once they are
* retrieved.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final void requestAllIcons(ContentResolver cr, String where,
WebIconDatabase.IconListener listener) {
// Do nothing: this is no longer used.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index bf7f3cb..396cf19 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2201,7 +2201,6 @@ public final class ContactsContract {
public static final long DAYS_KEPT_MILLISECONDS = 1000L * 60L * 60L * 24L * (long)DAYS_KEPT;
}
-
protected interface RawContactsColumns {
/**
* A reference to the {@link ContactsContract.Contacts#_ID} that this
@@ -8921,4 +8920,147 @@ public final class ContactsContract {
public static final String EXTRA_DATA_SET = "android.provider.extra.DATA_SET";
}
}
+
+ /**
+ * @hide
+ */
+ protected interface MetadataSyncColumns {
+
+ /**
+ * The raw contact backup id.
+ * A reference to the {@link ContactsContract.RawContacts#BACKUP_ID} that save the
+ * persistent unique id for each raw contact within its source system.
+ *
+ * @hide
+ */
+ public static final String RAW_CONTACT_BACKUP_ID = "raw_contact_backup_id";
+
+ /**
+ * The account type to which the raw_contact of this item is associated. See
+ * {@link RawContacts#ACCOUNT_TYPE}
+ *
+ * @hide
+ */
+ public static final String ACCOUNT_TYPE = "account_type";
+
+ /**
+ * The account name to which the raw_contact of this item is associated. See
+ * {@link RawContacts#ACCOUNT_NAME}
+ *
+ * @hide
+ */
+ public static final String ACCOUNT_NAME = "account_name";
+
+ /**
+ * The data set within the account that the raw_contact of this row belongs to. This allows
+ * multiple sync adapters for the same account type to distinguish between
+ * each others' data.
+ * {@link RawContacts#DATA_SET}
+ *
+ * @hide
+ */
+ public static final String DATA_SET = "data_set";
+
+ /**
+ * A text column contains the Json string got from People API. The Json string contains
+ * all the metadata related to the raw contact, i.e., all the data fields and
+ * aggregation exceptions.
+ *
+ * Here is an example of the Json string got from the actual schema.
+ * <pre>
+ * {
+ * "unique_contact_id": {
+ * "account_type": "CUSTOM_ACCOUNT",
+ * "custom_account_type": "facebook",
+ * "account_name": "android-test",
+ * "contact_id": "1111111",
+ * "data_set": "FOCUS"
+ * },
+ * "contact_prefs": {
+ * "send_to_voicemail": true,
+ * "starred": false,
+ * "pinned": 2
+ * },
+ * "aggregation_data": [
+ * {
+ * "type": "TOGETHER",
+ * "contact_ids": [
+ * {
+ * "account_type": "GOOGLE_ACCOUNT",
+ * "account_name": "android-test2",
+ * "contact_id": "2222222",
+ * "data_set": "GOOGLE_PLUS"
+ * },
+ * {
+ * "account_type": "GOOGLE_ACCOUNT",
+ * "account_name": "android-test3",
+ * "contact_id": "3333333",
+ * "data_set": "CUSTOM",
+ * "custom_data_set": "custom type"
+ * }
+ * ]
+ * }
+ * ],
+ * "field_data": [
+ * {
+ * "field_data_id": "1001",
+ * "field_data_prefs": {
+ * "is_primary": true,
+ * "is_super_primary": true
+ * },
+ * "usage_stats": [
+ * {
+ * "usage_type": "CALL",
+ * "last_time_used": 10000001,
+ * "usage_count": 10
+ * }
+ * ]
+ * }
+ * ]
+ * }
+ * </pre>
+ *
+ * @hide
+ */
+ public static final String DATA = "data";
+
+ /**
+ * The "deleted" flag: "0" by default, "1" if the row has been marked
+ * for deletion. When {@link android.content.ContentResolver#delete} is
+ * called on a raw contact, updating MetadataSync table to set the flag of the raw contact
+ * as "1", then metadata sync adapter deletes the raw contact metadata on the server.
+ * <P>Type: INTEGER</P>
+ *
+ * @hide
+ */
+ public static final String DELETED = "deleted";
+ }
+
+ /**
+ * Constants for the metadata sync table. This table is used to cache the metadata_sync data
+ * from server before it is merged into other CP2 tables.
+ *
+ * @hide
+ */
+ public static final class MetadataSync implements BaseColumns, MetadataSyncColumns {
+
+ /** The authority for the contacts metadata */
+ public static final String METADATA_AUTHORITY = "com.android.contacts.metadata";
+
+ /** A content:// style uri to the authority for the contacts metadata */
+ public static final Uri METADATA_AUTHORITY_URI = Uri.parse(
+ "content://" + METADATA_AUTHORITY);
+
+ /**
+ * This utility class cannot be instantiated
+ */
+ private MetadataSync() {
+ }
+
+ /**
+ * The content:// style URI for this table.
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(METADATA_AUTHORITY_URI,
+ "metadata_sync");
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a622a21..00c851b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -189,6 +189,36 @@ public final class Settings {
"android.settings.USAGE_ACCESS_SETTINGS";
/**
+ * Activity Category: Show application settings related to usage access.
+ * <p>
+ * An activity that provides a user interface for adjusting usage access related
+ * preferences for its containing application. Optional but recommended for apps that
+ * use {@link android.Manifest.permission#PACKAGE_USAGE_STATS}.
+ * <p>
+ * The activity may define meta-data to describe what usage access is
+ * used for within their app with {@link #METADATA_USAGE_ACCESS_REASON}, which
+ * will be displayed in Settings.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG =
+ "android.intent.category.USAGE_ACCESS_CONFIG";
+
+ /**
+ * Metadata key: Reason for needing usage access.
+ * <p>
+ * A key for metadata attached to an activity that receives action
+ * {@link #INTENT_CATEGORY_USAGE_ACCESS_CONFIG}, shown to the
+ * user as description of how the app uses usage access.
+ * <p>
+ */
+ public static final String METADATA_USAGE_ACCESS_REASON =
+ "android.settings.metadata.USAGE_ACCESS_REASON";
+
+ /**
* Activity Action: Show settings to allow configuration of security and
* location privacy.
* <p>
@@ -5361,6 +5391,12 @@ public final class Settings {
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
+ * Specifies the package name currently configured to be the default dialer application
+ * @hide
+ */
+ public static final String DIALER_DEFAULT_APPLICATION = "dialer_default_application";
+
+ /**
* Specifies the package name currently configured to be the emergency assistance application
*
* @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index 70cd388..7e87717 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -46,9 +46,9 @@ public class NetworkSecurityPolicy {
* without TLS or STARTTLS) is permitted for this process.
*
* <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP and
- * FTP stacks, {@link android.webkit.WebView}, {@link android.app.DownloadManager},
- * {@link android.media.MediaPlayer}) will refuse this process's requests to use cleartext
- * traffic. Third-party libraries are strongly encouraged to honor this setting as well.
+ * FTP stacks, {@link android.app.DownloadManager}, {@link android.media.MediaPlayer}) will
+ * refuse this process's requests to use cleartext traffic. Third-party libraries are strongly
+ * encouraged to honor this setting as well.
*
* <p>This flag is honored on a best effort basis because it's impossible to prevent all
* cleartext traffic from Android applications given the level of access provided to them. For
@@ -56,6 +56,8 @@ public class NetworkSecurityPolicy {
* because it cannot determine whether its traffic is in cleartext. However, most network
* traffic from applications is handled by higher-level network stacks/components which can
* honor this aspect of the policy.
+ *
+ * <p>NOTE: {@link android.webkit.WebView} does not honor this flag.
*/
public boolean isCleartextTrafficPermitted() {
return libcore.net.NetworkSecurityPolicy.isCleartextTrafficPermitted();
diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java
index d21cc3c..f0ca276 100644
--- a/core/java/android/service/chooser/ChooserTarget.java
+++ b/core/java/android/service/chooser/ChooserTarget.java
@@ -78,7 +78,8 @@ public final class ChooserTarget implements Parcelable {
* <p>The creator of a target may supply a ranking score. This score is assumed to be relative
* to the other targets supplied by the same
* {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
- * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).</p>
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
*
* <p>Before being sent, the PendingIntent supplied will be
* {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
@@ -113,7 +114,8 @@ public final class ChooserTarget implements Parcelable {
* <p>The creator of a target may supply a ranking score. This score is assumed to be relative
* to the other targets supplied by the same
* {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
- * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).</p>
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
*
* <p>Before being sent, the IntentSender supplied will be
* {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
@@ -144,6 +146,32 @@ public final class ChooserTarget implements Parcelable {
mIntentSender = intentSender;
}
+ /**
+ * Construct a deep link target for presentation by a chooser UI.
+ *
+ * <p>A target is composed of a title and an icon for presentation to the user.
+ * The UI presenting this target may truncate the title if it is too long to be presented
+ * in the available space, as well as crop, resize or overlay the supplied icon.</p>
+ *
+ * <p>The creator of a target may supply a ranking score. This score is assumed to be relative
+ * to the other targets supplied by the same
+ * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
+ * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
+ * Scores for a set of targets do not need to sum to 1.</p>
+ *
+ * <p>Before being sent, the Intent supplied will be
+ * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
+ * to the chooser.</p>
+ *
+ * <p>Take care not to place custom {@link android.os.Parcelable} types into
+ * the Intent as extras, as the system will not be able to unparcel it to merge
+ * additional extras.</p>
+ *
+ * @param title title of this target that will be shown to a user
+ * @param icon icon to represent this target
+ * @param score ranking score for this target between 0.0f and 1.0f, inclusive
+ * @param intent Intent to fill in and send if the user chooses this target
+ */
public ChooserTarget(CharSequence title, Bitmap icon, float score, Intent intent) {
mTitle = title;
mIcon = icon;
@@ -358,6 +386,10 @@ public final class ChooserTarget implements Parcelable {
}
dest.writeFloat(mScore);
IntentSender.writeIntentSenderOrNullToParcel(mIntentSender, dest);
+ dest.writeInt(mIntent != null ? 1 : 0);
+ if (mIntent != null) {
+ mIntent.writeToParcel(dest, 0);
+ }
}
public static final Creator<ChooserTarget> CREATOR
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index cc7f880..35b8819 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -358,6 +358,20 @@ public abstract class NotificationListenerService extends Service {
}
/**
+ * Inform the notification manager that these notifications have been viewed by the
+ * user.
+ * @param keys Notifications to mark as seen.
+ */
+ public final void setNotificationsShown(String[] keys) {
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().setNotificationsShownFromListener(mWrapper, keys);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
+ }
+
+ /**
* Sets the notification trim that will be received via {@link #onNotificationPosted}.
*
* <p>
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 5cf2c5c..b055efe 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -130,7 +130,7 @@ public class ScaleGestureDetector {
private float mFocusY;
private boolean mQuickScaleEnabled;
- private boolean mButtonScaleEnabled;
+ private boolean mStylusScaleEnabled;
private float mCurrSpan;
private float mPrevSpan;
@@ -162,7 +162,7 @@ public class ScaleGestureDetector {
private static final float SCALE_FACTOR = .5f;
private static final int ANCHORED_SCALE_MODE_NONE = 0;
private static final int ANCHORED_SCALE_MODE_DOUBLE_TAP = 1;
- private static final int ANCHORED_SCALE_MODE_BUTTON = 2;
+ private static final int ANCHORED_SCALE_MODE_STYLUS = 2;
/**
@@ -212,9 +212,14 @@ public class ScaleGestureDetector {
mMinSpan = res.getDimensionPixelSize(com.android.internal.R.dimen.config_minScalingSpan);
mHandler = handler;
// Quick scale is enabled by default after JB_MR2
- if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+ if (targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) {
setQuickScaleEnabled(true);
}
+ // Stylus scale is enabled by default after LOLLIPOP_MR1
+ if (targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
+ setStylusScaleEnabled(true);
+ }
}
/**
@@ -315,14 +320,11 @@ public class ScaleGestureDetector {
}
final int count = event.getPointerCount();
- final int toolType = event.getToolType(0);
- final boolean isButtonTool = toolType == MotionEvent.TOOL_TYPE_STYLUS
- || toolType == MotionEvent.TOOL_TYPE_MOUSE;
- final boolean isAnchoredScaleButtonDown = isButtonTool && (count == 1)
+ final boolean isStylusButtonDown = (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS)
&& (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0;
final boolean anchoredScaleCancelled =
- mAnchoredScaleMode == ANCHORED_SCALE_MODE_BUTTON && !isAnchoredScaleButtonDown;
+ mAnchoredScaleMode == ANCHORED_SCALE_MODE_STYLUS && !isStylusButtonDown;
final boolean streamComplete = action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled;
@@ -347,12 +349,12 @@ public class ScaleGestureDetector {
}
}
- if (!mInProgress && mButtonScaleEnabled && !inAnchoredScaleMode()
- && !streamComplete && isAnchoredScaleButtonDown) {
+ if (!mInProgress && mStylusScaleEnabled && !inAnchoredScaleMode()
+ && !streamComplete && isStylusButtonDown) {
// Start of a button scale gesture
mAnchoredScaleStartX = event.getX();
mAnchoredScaleStartY = event.getY();
- mAnchoredScaleMode = ANCHORED_SCALE_MODE_BUTTON;
+ mAnchoredScaleMode = ANCHORED_SCALE_MODE_STYLUS;
mInitialSpan = 0;
}
@@ -503,24 +505,22 @@ public class ScaleGestureDetector {
}
/**
- * Sets whether the associates {@link OnScaleGestureListener} should receive onScale callbacks
- * when the user presses a {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus
- * first button) and drags the pointer on the screen. Note that this is enabled by default if
- * the app targets API 23 and newer.
+ * Sets whether the associates {@link OnScaleGestureListener} should receive
+ * onScale callbacks when the user uses a stylus and presses the button.
+ * Note that this is enabled by default if the app targets API 23 and newer.
*
- * @param scales true to enable stylus or mouse scaling, false to disable.
+ * @param scales true to enable stylus scaling, false to disable.
*/
- public void setSecondaryButtonScaleEnabled(boolean scales) {
- mButtonScaleEnabled = scales;
+ public void setStylusScaleEnabled(boolean scales) {
+ mStylusScaleEnabled = scales;
}
/**
- * Return whether the button scale gesture, in which the user presses a
- * {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus first button) and drags the
- * pointer on the screen, should perform scaling. {@see #setButtonScaleEnabled(boolean)}.
+ * Return whether the stylus scale gesture, in which the user uses a stylus
+ * and presses the button, should preform scaling. {@see #setButtonScaleEnabled(boolean)}.
*/
- public boolean isSecondaryButtonScaleEnabled() {
- return mButtonScaleEnabled;
+ public boolean isStylusScaleEnabled() {
+ return mStylusScaleEnabled;
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 52e0235..5c6ce76 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -631,6 +631,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* </p>
*
* @attr ref android.R.styleable#View_alpha
+ * @attr ref android.R.styleable#View_assistBlocked
* @attr ref android.R.styleable#View_background
* @attr ref android.R.styleable#View_clickable
* @attr ref android.R.styleable#View_contentDescription
@@ -2324,6 +2325,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 1 PFLAG3_IS_LAID_OUT
* 1 PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT
* 1 PFLAG3_CALLED_SUPER
+ * 1 PFLAG3_APPLYING_INSETS
+ * 1 PFLAG3_FITTING_SYSTEM_WINDOWS
+ * 1 PFLAG3_NESTED_SCROLLING_ENABLED
+ * 1 PFLAG3_ASSIST_BLOCKED
* |-------|-------|-------|-------|
*/
@@ -2381,6 +2386,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
/**
+ * <p>Indicates that we are allowing {@link android.view.ViewAssistStructure} to traverse
+ * into this view.<p>
+ */
+ static final int PFLAG3_ASSIST_BLOCKED = 0x100;
+
+ /**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
@@ -3869,6 +3880,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
viewFlagMasks |= SAVE_DISABLED_MASK;
}
break;
+ case com.android.internal.R.styleable.View_assistBlocked:
+ if (a.getBoolean(attr, false)) {
+ mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED;
+ }
+ break;
case com.android.internal.R.styleable.View_duplicateParentState:
if (a.getBoolean(attr, false)) {
viewFlagValues |= DUPLICATE_PARENT_STATE;
@@ -5775,7 +5791,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
structure.setId(id, null, null, null);
}
- structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight-mLeft, mBottom-mTop);
+ structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop);
structure.setVisibility(getVisibility());
structure.setEnabled(isEnabled());
if (isClickable()) {
@@ -5890,8 +5906,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* {@link #onProvideVirtualAssistStructure}.
*/
public void dispatchProvideAssistStructure(ViewAssistStructure structure) {
- onProvideAssistStructure(structure);
- onProvideVirtualAssistStructure(structure);
+ if (!isAssistBlocked()) {
+ onProvideAssistStructure(structure);
+ onProvideVirtualAssistStructure(structure);
+ } else {
+ structure.setClassName(getAccessibilityClassName().toString());
+ structure.setAssistBlocked(true);
+ }
}
/**
@@ -7458,6 +7479,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Indicates whether this view will participate in data collection through
+ * {@link android.view.ViewAssistStructure}. If true, it will not provide any data
+ * for itself or its children. If false, the normal data collection will be allowed.
+ *
+ * @return Returns false if assist data collection is not blocked, else true.
+ *
+ * @see #setAssistBlocked(boolean)
+ * @attr ref android.R.styleable#View_assistBlocked
+ */
+ public boolean isAssistBlocked() {
+ return (mPrivateFlags3 & PFLAG3_ASSIST_BLOCKED) != 0;
+ }
+
+ /**
+ * Controls whether assist data collection from this view and its children is enabled
+ * (that is, whether {@link #onProvideAssistStructure} and
+ * {@link #onProvideVirtualAssistStructure} will be called). The default value is false,
+ * allowing normal assist collection. Setting this to false will disable assist collection.
+ *
+ * @param enabled Set to true to <em>disable</em> assist data collection, or false
+ * (the default) to allow it.
+ *
+ * @see #isAssistBlocked()
+ * @see #onProvideAssistStructure
+ * @see #onProvideVirtualAssistStructure
+ * @attr ref android.R.styleable#View_assistBlocked
+ */
+ public void setAssistBlocked(boolean enabled) {
+ if (enabled) {
+ mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED;
+ } else {
+ mPrivateFlags3 &= ~PFLAG3_ASSIST_BLOCKED;
+ }
+ }
+
+ /**
* Indicates whether this view will save its state (that is,
* whether its {@link #onSaveInstanceState} method will be called).
*
@@ -15301,9 +15358,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
RenderNode renderNode = null;
Bitmap cache = null;
- int layerType = getLayerType();
+ int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local
if (layerType == LAYER_TYPE_SOFTWARE
|| (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE)) {
+ // If not drawing with RenderNode, treat HW layers as SW
layerType = LAYER_TYPE_SOFTWARE;
buildDrawingCache(true);
cache = getDrawingCache(true);
@@ -15330,10 +15388,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sy = mScrollY;
}
- final boolean hasNoCache = cache == null || drawingWithRenderNode;
- final boolean offsetForScroll = cache == null
- && !drawingWithRenderNode
- && layerType != LAYER_TYPE_HARDWARE;
+ final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode;
+ final boolean offsetForScroll = cache == null && !drawingWithRenderNode;
int restoreTo = -1;
if (!drawingWithRenderNode || transformToApply != null) {
@@ -15406,17 +15462,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_ALPHA;
}
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
- if (hasNoCache) {
+ if (!drawingWithDrawingCache) {
final int multipliedAlpha = (int) (255 * alpha);
if (!onSetAlpha(multipliedAlpha)) {
- int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
- if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0
- || layerType != LAYER_TYPE_NONE) {
- layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
- }
if (drawingWithRenderNode) {
renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha());
} else if (layerType == LAYER_TYPE_NONE) {
+ int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
+ if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0) {
+ layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
+ }
canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(),
multipliedAlpha, layerFlags);
}
@@ -15451,33 +15506,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
- if (hasNoCache) {
- boolean layerRendered = false;
- if (layerType == LAYER_TYPE_HARDWARE && !drawingWithRenderNode) {
- final HardwareLayer layer = getHardwareLayer();
- if (layer != null && layer.isValid()) {
- int restoreAlpha = mLayerPaint.getAlpha();
- mLayerPaint.setAlpha((int) (alpha * 255));
- ((DisplayListCanvas) canvas).drawHardwareLayer(layer, 0, 0, mLayerPaint);
- mLayerPaint.setAlpha(restoreAlpha);
- layerRendered = true;
- } else {
- canvas.saveLayer(sx, sy, sx + getWidth(), sy + getHeight(), mLayerPaint);
- }
- }
-
- if (!layerRendered) {
- if (!drawingWithRenderNode) {
- // Fast path for layouts with no backgrounds
- if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
- mPrivateFlags &= ~PFLAG_DIRTY_MASK;
- dispatchDraw(canvas);
- } else {
- draw(canvas);
- }
- } else {
+ if (!drawingWithDrawingCache) {
+ if (drawingWithRenderNode) {
+ mPrivateFlags &= ~PFLAG_DIRTY_MASK;
+ ((DisplayListCanvas) canvas).drawRenderNode(renderNode, parentFlags);
+ } else {
+ // Fast path for layouts with no backgrounds
+ if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
- ((DisplayListCanvas) canvas).drawRenderNode(renderNode, parentFlags);
+ dispatchDraw(canvas);
+ } else {
+ draw(canvas);
}
}
} else if (cache != null) {
diff --git a/core/java/android/view/ViewAssistStructure.java b/core/java/android/view/ViewAssistStructure.java
index 7d263c5..346b8ec 100644
--- a/core/java/android/view/ViewAssistStructure.java
+++ b/core/java/android/view/ViewAssistStructure.java
@@ -32,6 +32,8 @@ public abstract class ViewAssistStructure {
public abstract void setVisibility(int visibility);
+ public abstract void setAssistBlocked(boolean state);
+
public abstract void setEnabled(boolean state);
public abstract void setClickable(boolean state);
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index a237afd..27304f5 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1040,14 +1040,10 @@ public class ViewDebug {
return methods;
}
- final ArrayList<Method> declaredMethods = new ArrayList();
- klass.getDeclaredMethodsUnchecked(false, declaredMethods);
+ methods = klass.getDeclaredMethodsUnchecked(false);
final ArrayList<Method> foundMethods = new ArrayList<Method>();
- final int count = declaredMethods.size();
- for (int i = 0; i < count; i++) {
- final Method method = declaredMethods.get(i);
-
+ for (final Method method : methods) {
// Ensure the method return and parameter types can be resolved.
try {
method.getReturnType();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8f2be99..4324e75 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2881,21 +2881,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
public void dispatchProvideAssistStructure(ViewAssistStructure structure) {
super.dispatchProvideAssistStructure(structure);
- if (structure.getChildCount() == 0) {
- final int childrenCount = getChildCount();
- if (childrenCount > 0) {
- structure.setChildCount(childrenCount);
- final ArrayList<View> preorderedList = buildOrderedChildList();
- final boolean customOrder = preorderedList == null
- && isChildrenDrawingOrderEnabled();
- final View[] children = mChildren;
- for (int i=0; i<childrenCount; i++) {
- final int childIndex = customOrder
- ? getChildDrawingOrder(childrenCount, i) : i;
- final View child = (preorderedList == null)
- ? children[childIndex] : preorderedList.get(childIndex);
- ViewAssistStructure cstructure = structure.newChild(i);
- child.dispatchProvideAssistStructure(cstructure);
+ if (!isAssistBlocked()) {
+ if (structure.getChildCount() == 0) {
+ final int childrenCount = getChildCount();
+ if (childrenCount > 0) {
+ structure.setChildCount(childrenCount);
+ final ArrayList<View> preorderedList = buildOrderedChildList();
+ final boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ final View[] children = mChildren;
+ for (int i=0; i<childrenCount; i++) {
+ final int childIndex = customOrder
+ ? getChildDrawingOrder(childrenCount, i) : i;
+ final View child = (preorderedList == null)
+ ? children[childIndex] : preorderedList.get(childIndex);
+ ViewAssistStructure cstructure = structure.newChild(i);
+ child.dispatchProvideAssistStructure(cstructure);
+ }
}
}
}
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 0417921..4d1209a 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -16,7 +16,6 @@
package android.view.animation;
-import android.content.res.Configuration;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -45,16 +44,6 @@ public class AnimationUtils {
private static final int TOGETHER = 0;
private static final int SEQUENTIALLY = 1;
- private static final float RECOMMENDED_FIELD_OF_VIEW_FOR_TV = 40f;
- private static final float ESTIMATED_VIEWING_DISTANCE_FOR_WATCH = 11f;
- private static final float AVERAGE_VIEWING_DISTANCE_FOR_PHONES = 14.2f;
- private static final float N5_DIAGONAL_VIEW_ANGLE = 19.58f;
- private static final float N5_DENSITY = 3.0f;
- private static final float N5_DPI = 443f;
-
- private static final float COTANGENT_OF_HALF_TV_ANGLE = (float) (1 / Math.tan(Math.toRadians
- (RECOMMENDED_FIELD_OF_VIEW_FOR_TV / 2)));
-
/**
* Returns the current animation time in milliseconds. This time should be used when invoking
@@ -378,78 +367,4 @@ public class AnimationUtils {
}
return interpolator;
}
-
- /**
- * Derives the viewing distance of a device based on the device size (in inches), and the
- * device type.
- * @hide
- */
- public static float getViewingDistance(float width, float height, int uiMode) {
- if (uiMode == Configuration.UI_MODE_TYPE_TELEVISION) {
- // TV
- return (width / 2) * COTANGENT_OF_HALF_TV_ANGLE;
- } else if (uiMode == Configuration.UI_MODE_TYPE_WATCH) {
- // Watch
- return ESTIMATED_VIEWING_DISTANCE_FOR_WATCH;
- } else {
- // Tablet, phone, etc
- return AVERAGE_VIEWING_DISTANCE_FOR_PHONES;
- }
- }
-
- /**
- * Calculates the duration scaling factor of an animation based on the hint that the animation
- * will move across the entire screen. A scaling factor of 1 means the duration on this given
- * device will be the same as the duration set through
- * {@link android.animation.Animator#setDuration(long)}. The calculation uses Nexus 5 as a
- * baseline device. That is, the duration of the animation on a given device will scale its
- * duration so that it has the same look and feel as the animation on Nexus 5. In order to
- * achieve the same perceived effect of the animation across different devices, we maintain
- * the same angular speed of the same animation in users' field of view. Therefore, the
- * duration scale factor is determined by the ratio of the angular movement on current
- * devices to that on the baseline device.
- *
- * @param width width of the screen (in inches)
- * @param height height of the screen (in inches)
- * @param viewingDistance the viewing distance of the device (i.e. watch, phone, TV, etc) in
- * inches
- * @return scaling factor (or multiplier) of the duration set through
- * {@link android.animation.Animator#setDuration(long)} on current device.
- * @hide
- */
- public static float getScreenSizeBasedDurationScale(float width, float height,
- float viewingDistance) {
- // Animation's moving distance is proportional to the screen size.
- float diagonal = (float) Math.sqrt(width * width + height * height);
- float diagonalViewAngle = (float) Math.toDegrees(Math.atan((diagonal / 2f)
- / viewingDistance) * 2);
- return diagonalViewAngle / N5_DIAGONAL_VIEW_ANGLE;
- }
-
- /**
- * Calculates the duration scaling factor of an animation under the assumption that the
- * animation is defined to move the same amount of distance (in dp) across all devices. A
- * scaling factor of 1 means the duration on this given device will be the same as the
- * duration set through {@link android.animation.Animator#setDuration(long)}. The calculation
- * uses Nexus 5 as a baseline device. That is, the duration of the animation on a given
- * device will scale its duration so that it has the same look and feel as the animation on
- * Nexus 5. In order to achieve the same perceived effect of the animation across different
- * devices, we maintain the same angular velocity of the same animation in users' field of
- * view. Therefore, the duration scale factor is determined by the ratio of the angular
- * movement on current devices to that on the baseline device.
- *
- * @param density logical density of the display. {@link android.util.DisplayMetrics#density}
- * @param dpi pixels per inch
- * @param viewingDistance viewing distance of the device (in inches)
- * @return the scaling factor of duration
- * @hide
- */
- public static float getDpBasedDurationScale(float density, float dpi,
- float viewingDistance) {
- // Angle in users' field of view per dp:
- float anglePerDp = (float) Math.atan2((density / dpi) / 2, viewingDistance) * 2;
- float baselineAnglePerDp = (float) Math.atan2((N5_DENSITY / N5_DPI) / 2,
- AVERAGE_VIEWING_DISTANCE_FOR_PHONES) * 2;
- return anglePerDp / baselineAnglePerDp;
- }
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 943beb0..453e4f5 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -900,7 +900,9 @@ public abstract class WebSettings {
* and therefore secure policy, this setting should be disabled.
* Note that this setting affects only JavaScript access to file scheme
* resources. Other access to such resources, for example, from image HTML
- * elements, is unaffected.
+ * elements, is unaffected. To prevent possible violation of same domain policy
+ * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier
+ * devices, you should explicitly set this value to {@code false}.
* <p>
* The default value is true for API level
* {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
@@ -920,7 +922,9 @@ public abstract class WebSettings {
* the value of {@link #getAllowUniversalAccessFromFileURLs} is true.
* Note too, that this setting affects only JavaScript access to file scheme
* resources. Other access to such resources, for example, from image HTML
- * elements, is unaffected.
+ * elements, is unaffected. To prevent possible violation of same domain policy
+ * on {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and earlier
+ * devices, you should explicitly set this value to {@code false}.
* <p>
* The default value is true for API level
* {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 3340c73..9782d72 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -76,6 +76,18 @@ public final class WebViewFactory {
private static boolean sAddressSpaceReserved = false;
private static PackageInfo sPackageInfo;
+ // Error codes for loadWebViewNativeLibraryFromPackage
+ public static final int LIBLOAD_SUCCESS = 0;
+ public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1;
+ public static final int LIBLOAD_ADDRESS_SPACE_NOT_RESERVED = 2;
+ public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3;
+ public static final int LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES = 4;
+
+ // native relro loading error codes
+ public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5;
+ public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6;
+ public static final int LIBLOAD_FAILED_JNI_CALL = 7;
+
private static class MissingWebViewPackageException extends AndroidRuntimeException {
public MissingWebViewPackageException(String message) { super(message); }
public MissingWebViewPackageException(Exception e) { super(e); }
@@ -136,6 +148,18 @@ public final class WebViewFactory {
return sPackageInfo;
}
+ /**
+ * Load the native library for the given package name iff that package
+ * name is the same as the one providing the current webview.
+ */
+ public static int loadWebViewNativeLibraryFromPackage(String packageName) {
+ sPackageInfo = findPreferredWebViewPackage();
+ if (packageName != null && packageName.equals(sPackageInfo.packageName)) {
+ return loadNativeLibrary();
+ }
+ return LIBLOAD_WRONG_PACKAGE_NAME;
+ }
+
static WebViewFactoryProvider getProvider() {
synchronized (sProviderLock) {
// For now the main purpose of this function (and the factory abstraction) is to keep
@@ -434,32 +458,34 @@ public final class WebViewFactory {
}
}
- private static void loadNativeLibrary() {
+ private static int loadNativeLibrary() {
if (!sAddressSpaceReserved) {
Log.e(LOGTAG, "can't load with relro file; address space not reserved");
- return;
+ return LIBLOAD_ADDRESS_SPACE_NOT_RESERVED;
}
try {
getUpdateService().waitForRelroCreationCompleted(VMRuntime.getRuntime().is64Bit());
} catch (RemoteException e) {
Log.e(LOGTAG, "error waiting for relro creation, proceeding without", e);
- return;
+ return LIBLOAD_FAILED_WAITING_FOR_RELRO;
}
try {
String[] args = getWebViewNativeLibraryPaths();
- boolean result = nativeLoadWithRelroFile(args[0] /* path32 */,
+ int result = nativeLoadWithRelroFile(args[0] /* path32 */,
args[1] /* path64 */,
CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
- if (!result) {
+ if (result != LIBLOAD_SUCCESS) {
Log.w(LOGTAG, "failed to load with relro file, proceeding without");
} else if (DEBUG) {
Log.v(LOGTAG, "loaded with relro file");
}
+ return result;
} catch (MissingWebViewPackageException e) {
Log.e(LOGTAG, "Failed to list WebView package libraries for loadNativeLibrary", e);
+ return LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
}
}
@@ -470,6 +496,6 @@ public final class WebViewFactory {
private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
private static native boolean nativeCreateRelroFile(String lib32, String lib64,
String relro32, String relro64);
- private static native boolean nativeLoadWithRelroFile(String lib32, String lib64,
+ private static native int nativeLoadWithRelroFile(String lib32, String lib64,
String relro32, String relro64);
}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index c6b4d7e..113e597 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -196,9 +196,23 @@ class DayPickerView extends ViewGroup {
}
@Override
+ public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ requestLayout();
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final ImageButton leftButton = mPrevButton;
- final ImageButton rightButton = mNextButton;
+ final ImageButton leftButton;
+ final ImageButton rightButton;
+ if (isLayoutRtl()) {
+ leftButton = mNextButton;
+ rightButton = mPrevButton;
+ } else {
+ leftButton = mPrevButton;
+ rightButton = mNextButton;
+ }
final int width = right - left;
final int height = bottom - top;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 955ad06..652fff2 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -39,7 +39,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.ExtractEditText;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
@@ -104,7 +103,6 @@ import android.widget.TextView.OnEditorActionListener;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
-import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.widget.EditableInputConnection;
import java.text.BreakIterator;
@@ -1764,15 +1762,55 @@ public class Editor {
}
/**
- * @return <code>true</code> if the cursor/current selection overlaps a {@link SuggestionSpan}.
+ * @return <code>true</code> if it's reasonable to offer to show suggestions depending on
+ * the current cursor position or selection range. This method is consistent with the
+ * method to show suggestions {@link SuggestionsPopupWindow#updateSuggestions}.
*/
- private boolean isCursorInsideSuggestionSpan() {
+ private boolean shouldOfferToShowSuggestions() {
CharSequence text = mTextView.getText();
if (!(text instanceof Spannable)) return false;
- SuggestionSpan[] suggestionSpans = ((Spannable) text).getSpans(
- mTextView.getSelectionStart(), mTextView.getSelectionEnd(), SuggestionSpan.class);
- return (suggestionSpans.length > 0);
+ final Spannable spannable = (Spannable) text;
+ final int selectionStart = mTextView.getSelectionStart();
+ final int selectionEnd = mTextView.getSelectionEnd();
+ final SuggestionSpan[] suggestionSpans = spannable.getSpans(selectionStart, selectionEnd,
+ SuggestionSpan.class);
+ if (suggestionSpans.length == 0) {
+ return false;
+ }
+ if (selectionStart == selectionEnd) {
+ // Spans overlap the cursor.
+ return true;
+ }
+ int minSpanStart = mTextView.getText().length();
+ int maxSpanEnd = 0;
+ int unionOfSpansCoveringSelectionStartStart = mTextView.getText().length();
+ int unionOfSpansCoveringSelectionStartEnd = 0;
+ for (int i = 0; i < suggestionSpans.length; i++) {
+ final int spanStart = spannable.getSpanStart(suggestionSpans[i]);
+ final int spanEnd = spannable.getSpanEnd(suggestionSpans[i]);
+ minSpanStart = Math.min(minSpanStart, spanStart);
+ maxSpanEnd = Math.max(maxSpanEnd, spanEnd);
+ if (selectionStart < spanStart || selectionStart > spanEnd) {
+ // The span doesn't cover the current selection start point.
+ continue;
+ }
+ unionOfSpansCoveringSelectionStartStart =
+ Math.min(unionOfSpansCoveringSelectionStartStart, spanStart);
+ unionOfSpansCoveringSelectionStartEnd =
+ Math.max(unionOfSpansCoveringSelectionStartEnd, spanEnd);
+ }
+ if (unionOfSpansCoveringSelectionStartStart >= unionOfSpansCoveringSelectionStartEnd) {
+ // No spans cover the selection start point.
+ return false;
+ }
+ if (minSpanStart < unionOfSpansCoveringSelectionStartStart
+ || maxSpanEnd > unionOfSpansCoveringSelectionStartEnd) {
+ // There is a span that is not covered by the union. In this case, we soouldn't offer
+ // to show suggestions as it's confusing.
+ return false;
+ }
+ return true;
}
/**
@@ -1919,6 +1957,9 @@ public class Editor {
if (mPositionListener != null) {
mPositionListener.onScrollChanged();
}
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.invalidateContentRect();
+ }
}
/**
@@ -3020,18 +3061,8 @@ public class Editor {
}
private void populateMenuWithItems(Menu menu) {
- final boolean legacy = mTextView.getContext().getApplicationInfo().targetSdkVersion <
- Build.VERSION_CODES.LOLLIPOP;
- final Context context = !legacy && menu instanceof MenuBuilder ?
- ((MenuBuilder) menu).getContext() :
- mTextView.getContext();
- final TypedArray styledAttributes = context.obtainStyledAttributes(
- com.android.internal.R.styleable.SelectionModeDrawables);
-
if (mTextView.canCut()) {
menu.add(0, TextView.ID_CUT, 0, com.android.internal.R.string.cut).
- setIcon(styledAttributes.getResourceId(
- R.styleable.SelectionModeDrawables_actionModeCutDrawable, 0)).
setAlphabeticShortcut('x').
setShowAsAction(
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
@@ -3039,8 +3070,6 @@ public class Editor {
if (mTextView.canCopy()) {
menu.add(0, TextView.ID_COPY, 0, com.android.internal.R.string.copy).
- setIcon(styledAttributes.getResourceId(
- R.styleable.SelectionModeDrawables_actionModeCopyDrawable, 0)).
setAlphabeticShortcut('c').
setShowAsAction(
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
@@ -3048,23 +3077,25 @@ public class Editor {
if (mTextView.canPaste()) {
menu.add(0, TextView.ID_PASTE, 0, com.android.internal.R.string.paste).
- setIcon(styledAttributes.getResourceId(
- R.styleable.SelectionModeDrawables_actionModePasteDrawable, 0)).
setAlphabeticShortcut('v').
setShowAsAction(
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
- menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
- setIcon(styledAttributes.getResourceId(
- R.styleable.SelectionModeDrawables_actionModeSelectAllDrawable, 0)).
- setAlphabeticShortcut('a').
- setShowAsAction(
- MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ if (mTextView.canShare()) {
+ menu.add(0, TextView.ID_SHARE, 0, com.android.internal.R.string.share).
+ setShowAsAction(
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ }
- updateReplaceItem(menu);
+ if (canSelectText() && !hasPasswordTransformationMethod()) {
+ menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
+ setAlphabeticShortcut('a').
+ setShowAsAction(
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ }
- styledAttributes.recycle();
+ updateReplaceItem(menu);
}
private void addIntentMenuItemsForTextProcessing(Menu menu) {
@@ -3104,7 +3135,7 @@ public class Editor {
}
private void updateReplaceItem(Menu menu) {
- boolean canReplace = mTextView.isSuggestionsEnabled() && isCursorInsideSuggestionSpan();
+ boolean canReplace = mTextView.isSuggestionsEnabled() && shouldOfferToShowSuggestions();
boolean replaceItemExists = menu.findItem(TextView.ID_REPLACE) != null;
if (canReplace && !replaceItemExists) {
menu.add(0, TextView.ID_REPLACE, 0, com.android.internal.R.string.replace).
@@ -4025,9 +4056,9 @@ public class Editor {
positionCursor = true;
} else if (offset + mTouchWordOffset < mPreviousOffset) {
// User is shrinking the selection.
- if (currLine > mPrevLine) {
+ if (currLine < mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
- offset = getWordStart(offset);
+ offset = start;
}
offset += mTouchWordOffset;
positionCursor = true;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 9ecdc9c..c959774 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1854,20 +1854,19 @@ public class GridView extends AbsListView {
moved = true;
}
break;
- case FOCUS_LEFT:
- if (selectedPosition > startOfRowPos) {
- mLayoutMode = LAYOUT_MOVE_SELECTION;
- setSelectionInt(Math.max(0, selectedPosition - 1));
- moved = true;
- }
- break;
- case FOCUS_RIGHT:
- if (selectedPosition < endOfRowPos) {
- mLayoutMode = LAYOUT_MOVE_SELECTION;
- setSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));
- moved = true;
- }
- break;
+ }
+
+ final boolean isLayoutRtl = isLayoutRtl();
+ if (selectedPosition > startOfRowPos && ((direction == FOCUS_LEFT && !isLayoutRtl) ||
+ (direction == FOCUS_RIGHT && isLayoutRtl))) {
+ mLayoutMode = LAYOUT_MOVE_SELECTION;
+ setSelectionInt(Math.max(0, selectedPosition - 1));
+ moved = true;
+ } else if (selectedPosition < endOfRowPos && ((direction == FOCUS_LEFT && isLayoutRtl) ||
+ (direction == FOCUS_RIGHT && !isLayoutRtl))) {
+ mLayoutMode = LAYOUT_MOVE_SELECTION;
+ setSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));
+ moved = true;
}
if (moved) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index a50941b..10e4db3 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -1335,10 +1335,6 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// If we are not connected, queue up the notifyDataSetChanged to be handled when we do
// connect
if (!mServiceConnection.isConnected()) {
- if (mNotifyDataSetChangedAfterOnServiceConnected) {
- return;
- }
-
mNotifyDataSetChangedAfterOnServiceConnected = true;
requestBindService();
return;
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 0249c22..2778f0f 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -162,7 +162,6 @@ class SimpleMonthView extends View {
mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
- setClickable(true);
initPaints(res);
}
@@ -318,7 +317,8 @@ class SimpleMonthView extends View {
final int x = (int) (event.getX() + 0.5f);
final int y = (int) (event.getY() + 0.5f);
- switch (event.getAction()) {
+ final int action = event.getAction();
+ switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
final int touchedItem = getDayAtLocation(x, y);
@@ -326,6 +326,10 @@ class SimpleMonthView extends View {
mTouchedItem = touchedItem;
invalidate();
}
+ if (action == MotionEvent.ACTION_DOWN && touchedItem < 0) {
+ // Touch something that's not an item, reject event.
+ return false;
+ }
break;
case MotionEvent.ACTION_UP:
@@ -376,9 +380,16 @@ class SimpleMonthView extends View {
for (int col = 0; col < DAYS_IN_WEEK; col++) {
final int colCenter = colWidth * col + colWidth / 2;
+ final int colCenterRtl;
+ if (isLayoutRtl()) {
+ colCenterRtl = mPaddedWidth - colCenter;
+ } else {
+ colCenterRtl = colCenter;
+ }
+
final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK;
final String label = getDayOfWeekLabel(dayOfWeek);
- canvas.drawText(label, colCenter, rowCenter - halfLineHeight, p);
+ canvas.drawText(label, colCenterRtl, rowCenter - halfLineHeight, p);
}
}
@@ -402,6 +413,13 @@ class SimpleMonthView extends View {
for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) {
final int colCenter = colWidth * col + colWidth / 2;
+ final int colCenterRtl;
+ if (isLayoutRtl()) {
+ colCenterRtl = mPaddedWidth - colCenter;
+ } else {
+ colCenterRtl = colCenter;
+ }
+
int stateMask = 0;
if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
@@ -413,12 +431,12 @@ class SimpleMonthView extends View {
stateMask |= StateSet.VIEW_STATE_ACTIVATED;
// Adjust the circle to be centered on the row.
- canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
+ canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
} else if (mTouchedItem == day) {
stateMask |= StateSet.VIEW_STATE_PRESSED;
// Adjust the circle to be centered on the row.
- canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
+ canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
}
final boolean isDayToday = mToday == day;
@@ -431,7 +449,7 @@ class SimpleMonthView extends View {
}
p.setColor(dayTextColor);
- canvas.drawText(Integer.toString(day), colCenter, rowCenter - halfLineHeight, p);
+ canvas.drawText(Integer.toString(day), colCenterRtl, rowCenter - halfLineHeight, p);
col++;
@@ -583,6 +601,13 @@ class SimpleMonthView extends View {
}
@Override
+ public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ requestLayout();
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (!changed) {
return;
@@ -657,8 +682,16 @@ class SimpleMonthView extends View {
return -1;
}
+ // Adjust for RTL after applying padding.
+ final int paddedXRtl;
+ if (isLayoutRtl()) {
+ paddedXRtl = mPaddedWidth - paddedX;
+ } else {
+ paddedXRtl = paddedX;
+ }
+
final int row = (paddedY - headerHeight) / mDayHeight;
- final int col = (paddedX * DAYS_IN_WEEK) / mPaddedWidth;
+ final int col = (paddedXRtl * DAYS_IN_WEEK) / mPaddedWidth;
final int index = col + row * DAYS_IN_WEEK;
final int day = index + 1 - findDayOffset();
if (day < 1 || day > mDaysInMonth) {
@@ -681,10 +714,15 @@ class SimpleMonthView extends View {
final int index = id - 1 + findDayOffset();
- // Compute left edge.
+ // Compute left edge, taking into account RTL.
final int col = index % DAYS_IN_WEEK;
final int colWidth = mCellWidth;
- final int left = getPaddingLeft() + col * colWidth;
+ final int left;
+ if (isLayoutRtl()) {
+ left = getWidth() - getPaddingRight() - (col + 1) * colWidth;
+ } else {
+ left = getPaddingLeft() + col * colWidth;
+ }
// Compute top edge.
final int row = index / DAYS_IN_WEEK;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 726b89a..3e8df08 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8955,13 +8955,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
static final int ID_CUT = android.R.id.cut;
static final int ID_COPY = android.R.id.copy;
static final int ID_PASTE = android.R.id.paste;
+ static final int ID_SHARE = android.R.id.shareText;
static final int ID_PASTE_AS_PLAIN_TEXT = android.R.id.pasteAsPlainText;
static final int ID_REPLACE = android.R.id.replaceText;
/**
* Called when a context menu option for the text view is selected. Currently
* this will be one of {@link android.R.id#selectAll}, {@link android.R.id#cut},
- * {@link android.R.id#copy} or {@link android.R.id#paste}.
+ * {@link android.R.id#copy}, {@link android.R.id#paste} or {@link android.R.id#shareText}.
*
* @return true if the context menu item action was performed.
*/
@@ -9014,6 +9015,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
stopSelectionActionMode();
return true;
+
+ case ID_SHARE:
+ shareSelectedText();
+ return true;
}
return false;
}
@@ -9091,15 +9096,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* If provided, this ActionMode.Callback will be used to create the ActionMode when text
* selection is initiated in this View.
*
- * The standard implementation populates the menu with a subset of Select All, Cut, Copy and
- * Paste actions, depending on what this View supports.
+ * The standard implementation populates the menu with a subset of Select All, Cut, Copy,
+ * Paste and Share actions, depending on what this View supports.
*
* A custom implementation can add new entries in the default menu in its
* {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The
* default actions can also be removed from the menu using
* {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll},
- * {@link android.R.id#cut}, {@link android.R.id#copy} or {@link android.R.id#paste} ids as
- * parameters.
+ * {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste} or
+ * {@link android.R.id#shareText} ids as parameters.
*
* Returning false from
* {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent
@@ -9168,6 +9173,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return false;
}
+ boolean canShare() {
+ return canCopy();
+ }
+
boolean canPaste() {
return (mText instanceof Editable &&
mEditor != null && mEditor.mKeyListener != null &&
@@ -9241,6 +9250,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ private void shareSelectedText() {
+ String selectedText = getSelectedText();
+ if (selectedText != null && !selectedText.isEmpty()) {
+ Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
+ sharingIntent.setType("text/plain");
+ sharingIntent.removeExtra(android.content.Intent.EXTRA_TEXT);
+ sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
+ getContext().startActivity(Intent.createChooser(sharingIntent, null));
+ stopSelectionActionMode();
+ }
+ }
+
private void setPrimaryClip(ClipData clip) {
ClipboardManager clipboard = (ClipboardManager) getContext().
getSystemService(Context.CLIPBOARD_SERVICE);
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index f7e9648..c0c8aec 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -403,7 +403,7 @@ public class ZoomButtonsController implements View.OnTouchListener {
// No longer care about configuration changes
mContext.unregisterReceiver(mConfigurationChangedReceiver);
- mWindowManager.removeView(mContainer);
+ mWindowManager.removeViewImmediate(mContainer);
mHandler.removeCallbacks(mPostedVisibleInitializer);
if (mCallback != null) {
@@ -490,7 +490,7 @@ public class ZoomButtonsController implements View.OnTouchListener {
setVisible(false);
return true;
}
-
+
} else {
dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8403e77..a6c39e6 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -401,6 +401,11 @@ public class ChooserActivity extends ResolverActivity {
}
@Override
+ public boolean shouldGetResolvedFilter() {
+ return true;
+ }
+
+ @Override
public int getCount() {
int count = super.getCount();
if (mServiceTargets != null) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3cd69a1..7f51d92 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1062,7 +1062,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
} else {
currentResolveList = mOrigResolveList = mPm.queryIntentActivities(mIntent,
PackageManager.MATCH_DEFAULT_ONLY
- | (mFilterLastUsed ? PackageManager.GET_RESOLVED_FILTER : 0)
+ | (shouldGetResolvedFilter() ? PackageManager.GET_RESOLVED_FILTER : 0)
| (shouldGetActivityMetadata() ? PackageManager.GET_META_DATA : 0)
);
// Filter out any activities that the launched uid does not
@@ -1188,6 +1188,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic
// This space for rent
}
+ public boolean shouldGetResolvedFilter() {
+ return mFilterLastUsed;
+ }
+
private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
CharSequence roLabel) {
// Process labels from start to i
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 48f0e71..5ed4f70 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -269,11 +269,11 @@ public class ZygoteInit {
if (false) {
Log.v(TAG, "Preloading " + line + "...");
}
- // Load and explicitly initialize the given class. Use the tree-argument version
- // of forName to avoid repeated stack lookups (to derive the caller's
- // class-loader). Use true to force initialization, and null for the boot
- // classpath class-loader (could as well cache the class-loader of this class in
- // a variable).
+ // Load and explicitly initialize the given class. Use
+ // Class.forName(String, boolean, ClassLoader) to avoid repeated stack lookups
+ // (to derive the caller's class-loader). Use true to force initialization, and
+ // null for the boot classpath class-loader (could as well cache the
+ // class-loader of this class in a variable).
Class.forName(line, true, null);
count++;
} catch (ClassNotFoundException e) {
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 414b7bc..b692a18 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -24,6 +24,12 @@ import java.util.Collection;
*/
public class Preconditions {
+ public static void checkArgument(boolean expression) {
+ if (!expression) {
+ throw new IllegalArgumentException();
+ }
+ }
+
/**
* Ensures that an object reference passed as a parameter to the calling
* method is not null.
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index a14e98d..3a1e0ca 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -346,6 +346,17 @@ public final class FloatingToolbar {
};
private final Region mTouchableRegion = new Region();
+ private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
+ new ViewTreeObserver.OnComputeInternalInsetsListener() {
+ public void onComputeInternalInsets(
+ ViewTreeObserver.InternalInsetsInfo info) {
+ info.contentInsets.setEmpty();
+ info.visibleInsets.setEmpty();
+ info.touchableRegion.set(mTouchableRegion);
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo
+ .TOUCHABLE_INSETS_REGION);
+ }
+ };
private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
private boolean mHidden; // tracks whether this popup is hidden or hiding.
@@ -382,21 +393,6 @@ public final class FloatingToolbar {
mPopupWindow.dismiss();
}
});
- // Make the touchable area of this popup be the area specified by mTouchableRegion.
- mPopupWindow.getContentView()
- .getRootView()
- .getViewTreeObserver()
- .addOnComputeInternalInsetsListener(
- new ViewTreeObserver.OnComputeInternalInsetsListener() {
- public void onComputeInternalInsets(
- ViewTreeObserver.InternalInsetsInfo info) {
- info.contentInsets.setEmpty();
- info.visibleInsets.setEmpty();
- info.touchableRegion.set(mTouchableRegion);
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo
- .TOUCHABLE_INSETS_REGION);
- }
- });
mMarginHorizontal = parent.getResources()
.getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
mMarginVertical = parent.getResources()
@@ -437,16 +433,15 @@ public final class FloatingToolbar {
mHidden = false;
mDismissed = false;
- cancelAllAnimations();
+ cancelDismissAndHideAnimations();
+ cancelOverflowAnimations();
// Make sure a panel is set as the content.
if (mContentContainer.getChildCount() == 0) {
setMainPanelAsContent();
}
preparePopupContent();
- // If we're yet to show the popup, set the container visibility to zero.
- // The "show" animation will make this visible.
- mContentContainer.setAlpha(0);
mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, x, y);
+ setTouchableSurfaceInsetsComputer();
runShowAnimation();
}
@@ -454,12 +449,13 @@ public final class FloatingToolbar {
* Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
*/
public void dismiss() {
- if (!isShowing()) {
+ if (mDismissed) {
return;
}
mHidden = false;
mDismissed = true;
+ mHideAnimation.cancel();
runDismissAnimation();
setZeroTouchableSurface();
}
@@ -502,7 +498,7 @@ public final class FloatingToolbar {
return;
}
- cancelAllAnimations();
+ cancelOverflowAnimations();
preparePopupContent();
mPopupWindow.update(x, y, getWidth(), getHeight());
}
@@ -566,10 +562,12 @@ public final class FloatingToolbar {
mHideAnimation.start();
}
- private void cancelAllAnimations() {
- mShowAnimation.cancel();
+ private void cancelDismissAndHideAnimations() {
mDismissAnimation.cancel();
mHideAnimation.cancel();
+ }
+
+ private void cancelOverflowAnimations() {
mOpenOverflowAnimation.cancel();
mCloseOverflowAnimation.cancel();
}
@@ -804,6 +802,19 @@ public final class FloatingToolbar {
(int) mContentContainer.getX() + width,
(int) mContentContainer.getY() + height);
}
+
+ /**
+ * Make the touchable area of this popup be the area specified by mTouchableRegion.
+ * This should be called after the popup window has been dismissed (dismiss/hide)
+ * and is probably being re-shown with a new content root view.
+ */
+ private void setTouchableSurfaceInsetsComputer() {
+ ViewTreeObserver viewTreeObserver = mPopupWindow.getContentView()
+ .getRootView()
+ .getViewTreeObserver();
+ viewTreeObserver.removeOnComputeInternalInsetsListener(mInsetsComputer);
+ viewTreeObserver.addOnComputeInternalInsetsListener(mInsetsComputer);
+ }
}
/**
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 89990c2..191662c 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -111,7 +111,7 @@ public class SwipeDismissLayout extends FrameLayout {
}
private void init(Context context) {
- ViewConfiguration vc = ViewConfiguration.get(getContext());
+ ViewConfiguration vc = ViewConfiguration.get(context);
mSlop = vc.getScaledTouchSlop();
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
@@ -290,7 +290,7 @@ public class SwipeDismissLayout extends FrameLayout {
float deltaX = ev.getRawX() - mDownX;
float deltaY = ev.getRawY() - mDownY;
if ((deltaX * deltaX) + (deltaY * deltaY) > mSlop * mSlop) {
- mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2;
+ mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < Math.abs(deltaX);
} else {
mSwiping = false;
}
@@ -299,9 +299,9 @@ public class SwipeDismissLayout extends FrameLayout {
private void updateDismiss(MotionEvent ev) {
float deltaX = ev.getRawX() - mDownX;
+ mVelocityTracker.addMovement(ev);
+ mVelocityTracker.computeCurrentVelocity(1000);
if (!mDismissed) {
- mVelocityTracker.addMovement(ev);
- mVelocityTracker.computeCurrentVelocity(1000);
if (deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) &&
ev.getRawX() >= mLastX) {
@@ -311,7 +311,9 @@ public class SwipeDismissLayout extends FrameLayout {
// Check if the user tried to undo this.
if (mDismissed && mSwiping) {
// Check if the user's finger is actually back
- if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO)) {
+ if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) ||
+ // or user is flinging back left
+ mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {
mDismissed = false;
}
}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 5c08daf..441e640 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -27,9 +27,9 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.MathUtils;
import android.view.FocusFinder;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -43,7 +43,6 @@ import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.accessibility.AccessibilityRecord;
import android.view.animation.Interpolator;
import android.widget.EdgeEffect;
import android.widget.Scroller;
@@ -84,8 +83,9 @@ import java.util.Comparator;
*/
public class ViewPager extends ViewGroup {
private static final String TAG = "ViewPager";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
+ private static final int MAX_SCROLL_X = 2 << 23;
private static final boolean USE_CACHE = false;
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
@@ -108,9 +108,13 @@ public class ViewPager extends ViewGroup {
static class ItemInfo {
Object object;
- int position;
boolean scrolling;
float widthFactor;
+
+ /** Logical position of the item within the pager adapter. */
+ int position;
+
+ /** Offset between the starting edges of the item and its container. */
float offset;
}
@@ -146,6 +150,12 @@ public class ViewPager extends ViewGroup {
private int mTopPageBounds;
private int mBottomPageBounds;
+ /**
+ * The increment used to move in the "left" direction. Dependent on layout
+ * direction.
+ */
+ private int mLeftIncr = -1;
+
// Offsets of the first and last items, if known.
// Set during population, used to determine if we are at the beginning
// or end of the pager data set during touch scrolling.
@@ -198,14 +208,10 @@ public class ViewPager extends ViewGroup {
// "catching" the flinging pager.
private static final int CLOSE_ENOUGH = 2; // dp
- private boolean mFakeDragging;
- private long mFakeDragBeginTime;
-
private final EdgeEffect mLeftEdge;
private final EdgeEffect mRightEdge;
private boolean mFirstLayout = true;
- private boolean mNeedCalculatePageOffsets = false;
private boolean mCalledSuper;
private int mDecorChildCount;
@@ -473,7 +479,7 @@ public class ViewPager extends ViewGroup {
mAdapterChangeListener = listener;
}
- private int getClientWidth() {
+ private int getPaddedWidth() {
return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
}
@@ -504,36 +510,33 @@ public class ViewPager extends ViewGroup {
return mCurItem;
}
- void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
- setCurrentItemInternal(item, smoothScroll, always, 0);
+ boolean setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
+ return setCurrentItemInternal(item, smoothScroll, always, 0);
}
- void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
+ boolean setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
if (mAdapter == null || mAdapter.getCount() <= 0) {
setScrollingCacheEnabled(false);
- return;
+ return false;
}
+
+ item = MathUtils.constrain(item, 0, mAdapter.getCount() - 1);
if (!always && mCurItem == item && mItems.size() != 0) {
setScrollingCacheEnabled(false);
- return;
+ return false;
}
- if (item < 0) {
- item = 0;
- } else if (item >= mAdapter.getCount()) {
- item = mAdapter.getCount() - 1;
- }
final int pageLimit = mOffscreenPageLimit;
if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
// We are doing a jump by more than one page. To avoid
// glitches, we want to keep all current pages in the view
// until the scroll ends.
- for (int i=0; i<mItems.size(); i++) {
+ for (int i = 0; i < mItems.size(); i++) {
mItems.get(i).scrolling = true;
}
}
- final boolean dispatchSelected = mCurItem != item;
+ final boolean dispatchSelected = mCurItem != item;
if (mFirstLayout) {
// We don't have any idea how big we are yet and shouldn't have any pages either.
// Just set things up and let the pending layout handle things.
@@ -549,38 +552,55 @@ public class ViewPager extends ViewGroup {
populate(item);
scrollToItem(item, smoothScroll, velocity, dispatchSelected);
}
+
+ return true;
}
- private void scrollToItem(int item, boolean smoothScroll, int velocity,
+ private void scrollToItem(int position, boolean smoothScroll, int velocity,
boolean dispatchSelected) {
- final ItemInfo curInfo = infoForPosition(item);
- int destX = 0;
- if (curInfo != null) {
- final int width = getClientWidth();
- destX = (int) (width * Math.max(mFirstOffset,
- Math.min(curInfo.offset, mLastOffset)));
- }
+ final int destX = getLeftEdgeForItem(position);
+
if (smoothScroll) {
smoothScrollTo(destX, 0, velocity);
+
if (dispatchSelected && mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageSelected(item);
+ mOnPageChangeListener.onPageSelected(position);
}
if (dispatchSelected && mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageSelected(item);
+ mInternalPageChangeListener.onPageSelected(position);
}
} else {
if (dispatchSelected && mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageSelected(item);
+ mOnPageChangeListener.onPageSelected(position);
}
if (dispatchSelected && mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageSelected(item);
+ mInternalPageChangeListener.onPageSelected(position);
}
+
completeScroll(false);
scrollTo(destX, 0);
pageScrolled(destX);
}
}
+ private int getLeftEdgeForItem(int position) {
+ final ItemInfo info = infoForPosition(position);
+ if (info == null) {
+ return 0;
+ }
+
+ final int width = getPaddedWidth();
+ final int scaledOffset = (int) (width * MathUtils.constrain(
+ info.offset, mFirstOffset, mLastOffset));
+
+ if (isLayoutRtl()) {
+ final int itemWidth = (int) (width * info.widthFactor + 0.5f);
+ return MAX_SCROLL_X - itemWidth - scaledOffset;
+ } else {
+ return scaledOffset;
+ }
+ }
+
/**
* Set a listener that will be invoked whenever the page changes or is incrementally
* scrolled. See {@link OnPageChangeListener}.
@@ -784,7 +804,7 @@ public class ViewPager extends ViewGroup {
setScrollingCacheEnabled(true);
setScrollState(SCROLL_STATE_SETTLING);
- final int width = getClientWidth();
+ final int width = getPaddedWidth();
final int halfWidth = width / 2;
final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
final float distance = halfWidth + halfWidth *
@@ -968,7 +988,7 @@ public class ViewPager extends ViewGroup {
float extraWidthLeft = 0.f;
int itemIndex = curIndex - 1;
ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
- final int clientWidth = getClientWidth();
+ final int clientWidth = getPaddedWidth();
final float leftWidthNeeded = clientWidth <= 0 ? 0 :
2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;
for (int pos = mCurItem - 1; pos >= 0; pos--) {
@@ -981,7 +1001,7 @@ public class ViewPager extends ViewGroup {
mAdapter.destroyItem(this, pos, ii.object);
if (DEBUG) {
Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
- " view: " + ((View) ii.object));
+ " view: " + ii.object);
}
itemIndex--;
curIndex--;
@@ -1015,7 +1035,7 @@ public class ViewPager extends ViewGroup {
mAdapter.destroyItem(this, pos, ii.object);
if (DEBUG) {
Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
- " view: " + ((View) ii.object));
+ " view: " + ii.object);
}
ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
}
@@ -1099,49 +1119,51 @@ public class ViewPager extends ViewGroup {
private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {
final int N = mAdapter.getCount();
- final int width = getClientWidth();
+ final int width = getPaddedWidth();
final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
+
// Fix up offsets for later layout.
if (oldCurInfo != null) {
final int oldCurPosition = oldCurInfo.position;
+
// Base offsets off of oldCurInfo.
if (oldCurPosition < curItem.position) {
int itemIndex = 0;
- ItemInfo ii = null;
float offset = oldCurInfo.offset + oldCurInfo.widthFactor + marginOffset;
- for (int pos = oldCurPosition + 1;
- pos <= curItem.position && itemIndex < mItems.size(); pos++) {
- ii = mItems.get(itemIndex);
+ for (int pos = oldCurPosition + 1; pos <= curItem.position && itemIndex < mItems.size(); pos++) {
+ ItemInfo ii = mItems.get(itemIndex);
while (pos > ii.position && itemIndex < mItems.size() - 1) {
itemIndex++;
ii = mItems.get(itemIndex);
}
+
while (pos < ii.position) {
// We don't have an item populated for this,
// ask the adapter for an offset.
offset += mAdapter.getPageWidth(pos) + marginOffset;
pos++;
}
+
ii.offset = offset;
offset += ii.widthFactor + marginOffset;
}
} else if (oldCurPosition > curItem.position) {
int itemIndex = mItems.size() - 1;
- ItemInfo ii = null;
float offset = oldCurInfo.offset;
- for (int pos = oldCurPosition - 1;
- pos >= curItem.position && itemIndex >= 0; pos--) {
- ii = mItems.get(itemIndex);
+ for (int pos = oldCurPosition - 1; pos >= curItem.position && itemIndex >= 0; pos--) {
+ ItemInfo ii = mItems.get(itemIndex);
while (pos < ii.position && itemIndex > 0) {
itemIndex--;
ii = mItems.get(itemIndex);
}
+
while (pos > ii.position) {
// We don't have an item populated for this,
// ask the adapter for an offset.
offset -= mAdapter.getPageWidth(pos) + marginOffset;
pos--;
}
+
offset -= ii.widthFactor + marginOffset;
ii.offset = offset;
}
@@ -1155,6 +1177,7 @@ public class ViewPager extends ViewGroup {
mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;
mLastOffset = curItem.position == N - 1 ?
curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE;
+
// Previous pages
for (int i = curIndex - 1; i >= 0; i--, pos--) {
final ItemInfo ii = mItems.get(i);
@@ -1165,8 +1188,10 @@ public class ViewPager extends ViewGroup {
ii.offset = offset;
if (ii.position == 0) mFirstOffset = offset;
}
+
offset = curItem.offset + curItem.widthFactor + marginOffset;
pos = curItem.position + 1;
+
// Next pages
for (int i = curIndex + 1; i < itemCount; i++, pos++) {
final ItemInfo ii = mItems.get(i);
@@ -1179,8 +1204,6 @@ public class ViewPager extends ViewGroup {
ii.offset = offset;
offset += ii.widthFactor + marginOffset;
}
-
- mNeedCalculatePageOffsets = false;
}
/**
@@ -1546,34 +1569,47 @@ public class ViewPager extends ViewGroup {
// Page views. Do this once we have the right padding offsets from above.
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- ItemInfo ii;
- if (!lp.isDecor && (ii = infoForChild(child)) != null) {
- int loff = (int) (childWidth * ii.offset);
- int childLeft = paddingLeft + loff;
- int childTop = paddingTop;
- if (lp.needsMeasure) {
- // This was added during layout and needs measurement.
- // Do it now that we know what we're working with.
- lp.needsMeasure = false;
- final int widthSpec = MeasureSpec.makeMeasureSpec(
- (int) (childWidth * lp.widthFactor),
- MeasureSpec.EXACTLY);
- final int heightSpec = MeasureSpec.makeMeasureSpec(
- (int) (height - paddingTop - paddingBottom),
- MeasureSpec.EXACTLY);
- child.measure(widthSpec, heightSpec);
- }
- if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
- + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
- + "x" + child.getMeasuredHeight());
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(),
- childTop + child.getMeasuredHeight());
- }
+ if (child.getVisibility() == GONE) {
+ continue;
}
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.isDecor) {
+ continue;
+ }
+
+ final ItemInfo ii = infoForChild(child);
+ if (ii == null) {
+ continue;
+ }
+
+ if (lp.needsMeasure) {
+ // This was added during layout and needs measurement.
+ // Do it now that we know what we're working with.
+ lp.needsMeasure = false;
+ final int widthSpec = MeasureSpec.makeMeasureSpec(
+ (int) (childWidth * lp.widthFactor),
+ MeasureSpec.EXACTLY);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(
+ (int) (height - paddingTop - paddingBottom),
+ MeasureSpec.EXACTLY);
+ child.measure(widthSpec, heightSpec);
+ }
+
+ final int childMeasuredWidth = child.getMeasuredWidth();
+ final int startOffset = (int) (childWidth * ii.offset);
+ final int childLeft;
+ if (isLayoutRtl()) {
+ childLeft = MAX_SCROLL_X - paddingRight - startOffset - childMeasuredWidth;
+ } else {
+ childLeft = paddingLeft + startOffset;
+ }
+
+ final int childTop = paddingTop;
+ child.layout(childLeft, childTop, childLeft + childMeasuredWidth,
+ childTop + child.getMeasuredHeight());
}
+
mTopPageBounds = paddingTop;
mBottomPageBounds = height - paddingBottom;
mDecorChildCount = decorCount;
@@ -1587,13 +1623,14 @@ public class ViewPager extends ViewGroup {
@Override
public void computeScroll() {
if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
- int oldX = getScrollX();
- int oldY = getScrollY();
- int x = mScroller.getCurrX();
- int y = mScroller.getCurrY();
+ final int oldX = getScrollX();
+ final int oldY = getScrollY();
+ final int x = mScroller.getCurrX();
+ final int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
scrollTo(x, y);
+
if (!pageScrolled(x)) {
mScroller.abortAnimation();
scrollTo(0, y);
@@ -1609,7 +1646,7 @@ public class ViewPager extends ViewGroup {
completeScroll(true);
}
- private boolean pageScrolled(int xpos) {
+ private boolean pageScrolled(int scrollX) {
if (mItems.size() == 0) {
mCalledSuper = false;
onPageScrolled(0, 0, 0);
@@ -1619,12 +1656,21 @@ public class ViewPager extends ViewGroup {
}
return false;
}
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int width = getClientWidth();
+
+ // Translate to scrollX to scrollStart for RTL.
+ final int scrollStart;
+ if (isLayoutRtl()) {
+ scrollStart = MAX_SCROLL_X - scrollX;
+ } else {
+ scrollStart = scrollX;
+ }
+
+ final ItemInfo ii = infoForFirstVisiblePage();
+ final int width = getPaddedWidth();
final int widthWithMargin = width + mPageMargin;
final float marginOffset = (float) mPageMargin / width;
final int currentPage = ii.position;
- final float pageOffset = (((float) xpos / width) - ii.offset) /
+ final float pageOffset = (((float) scrollStart / width) - ii.offset) /
(ii.widthFactor + marginOffset);
final int offsetPixels = (int) (pageOffset * widthWithMargin);
@@ -1706,7 +1752,7 @@ public class ViewPager extends ViewGroup {
if (lp.isDecor) continue;
- final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
+ final float transformPos = (float) (child.getLeft() - scrollX) / getPaddedWidth();
mPageTransformer.transformPage(child, transformPos);
}
}
@@ -1785,11 +1831,11 @@ public class ViewPager extends ViewGroup {
// are dragging.
if (action != MotionEvent.ACTION_DOWN) {
if (mIsBeingDragged) {
- if (DEBUG) Log.v(TAG, "Intercept returning true!");
+ if (DEBUG) Log.v(TAG, "Being dragged, intercept returning true!");
return true;
}
if (mIsUnableToDrag) {
- if (DEBUG) Log.v(TAG, "Intercept returning false!");
+ if (DEBUG) Log.v(TAG, "Unable to drag, intercept returning false!");
return false;
}
}
@@ -1903,13 +1949,6 @@ public class ViewPager extends ViewGroup {
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (mFakeDragging) {
- // A fake drag is in progress already, ignore this real one
- // but still eat the touch events.
- // (It is likely that the user is multi-touching the screen.)
- return true;
- }
-
if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
// Don't handle edge touches immediately -- they may actually belong to one of our
// descendants.
@@ -1978,19 +2017,26 @@ public class ViewPager extends ViewGroup {
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
+ final int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
+
mPopulatePending = true;
- final int width = getClientWidth();
- final int scrollX = getScrollX();
- final ItemInfo ii = infoForCurrentScrollPosition();
+
+ final float scrollStart = getScrollStart();
+ final float scrolledPages = scrollStart / getPaddedWidth();
+ final ItemInfo ii = infoForFirstVisiblePage();
final int currentPage = ii.position;
- final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
- final int activePointerIndex =
- ev.findPointerIndex(mActivePointerId);
+ final float nextPageOffset;
+ if (isLayoutRtl()) {
+ nextPageOffset = (ii.offset - scrolledPages) / ii.widthFactor;
+ } else {
+ nextPageOffset = (scrolledPages - ii.offset) / ii.widthFactor;
+ }
+
+ final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(activePointerIndex);
final int totalDelta = (int) (x - mInitialMotionX);
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
+ final int nextPage = determineTargetPage(
+ currentPage, nextPageOffset, initialVelocity, totalDelta);
setCurrentItemInternal(nextPage, true, true, initialVelocity);
mActivePointerId = INVALID_POINTER;
@@ -2038,48 +2084,79 @@ public class ViewPager extends ViewGroup {
private boolean performDrag(float x) {
boolean needsInvalidate = false;
+ final int width = getPaddedWidth();
final float deltaX = mLastMotionX - x;
mLastMotionX = x;
- float oldScrollX = getScrollX();
- float scrollX = oldScrollX + deltaX;
- final int width = getClientWidth();
+ final EdgeEffect startEdge;
+ final EdgeEffect endEdge;
+ if (isLayoutRtl()) {
+ startEdge = mRightEdge;
+ endEdge = mLeftEdge;
+ } else {
+ startEdge = mLeftEdge;
+ endEdge = mRightEdge;
+ }
- float leftBound = width * mFirstOffset;
- float rightBound = width * mLastOffset;
- boolean leftAbsolute = true;
- boolean rightAbsolute = true;
+ // Translate scroll to relative coordinates.
+ final float nextScrollX = getScrollX() + deltaX;
+ final float scrollStart;
+ if (isLayoutRtl()) {
+ scrollStart = MAX_SCROLL_X - nextScrollX;
+ } else {
+ scrollStart = nextScrollX;
+ }
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- leftAbsolute = false;
- leftBound = firstItem.offset * width;
+ final float startBound;
+ final ItemInfo startItem = mItems.get(0);
+ final boolean startAbsolute = startItem.position == 0;
+ if (startAbsolute) {
+ startBound = startItem.offset * width;
+ } else {
+ startBound = width * mFirstOffset;
}
- if (lastItem.position != mAdapter.getCount() - 1) {
- rightAbsolute = false;
- rightBound = lastItem.offset * width;
+
+ final float endBound;
+ final ItemInfo endItem = mItems.get(mItems.size() - 1);
+ final boolean endAbsolute = endItem.position == mAdapter.getCount() - 1;
+ if (endAbsolute) {
+ endBound = endItem.offset * width;
+ } else {
+ endBound = width * mLastOffset;
}
- if (scrollX < leftBound) {
- if (leftAbsolute) {
- float over = leftBound - scrollX;
- mLeftEdge.onPull(Math.abs(over) / width);
+ final float clampedScrollStart;
+ if (scrollStart < startBound) {
+ if (startAbsolute) {
+ final float over = startBound - scrollStart;
+ startEdge.onPull(Math.abs(over) / width);
needsInvalidate = true;
}
- scrollX = leftBound;
- } else if (scrollX > rightBound) {
- if (rightAbsolute) {
- float over = scrollX - rightBound;
- mRightEdge.onPull(Math.abs(over) / width);
+ clampedScrollStart = startBound;
+ } else if (scrollStart > endBound) {
+ if (endAbsolute) {
+ final float over = scrollStart - endBound;
+ endEdge.onPull(Math.abs(over) / width);
needsInvalidate = true;
}
- scrollX = rightBound;
+ clampedScrollStart = endBound;
+ } else {
+ clampedScrollStart = scrollStart;
}
- // Don't lose the rounded component
- mLastMotionX += scrollX - (int) scrollX;
- scrollTo((int) scrollX, getScrollY());
- pageScrolled((int) scrollX);
+
+ // Translate back to absolute coordinates.
+ final float targetScrollX;
+ if (isLayoutRtl()) {
+ targetScrollX = MAX_SCROLL_X - clampedScrollStart;
+ } else {
+ targetScrollX = clampedScrollStart;
+ }
+
+ // Don't lose the rounded component.
+ mLastMotionX += targetScrollX - (int) targetScrollX;
+
+ scrollTo((int) targetScrollX, getScrollY());
+ pageScrolled((int) targetScrollX);
return needsInvalidate;
}
@@ -2088,19 +2165,23 @@ public class ViewPager extends ViewGroup {
* @return Info about the page at the current scroll position.
* This can be synthetic for a missing middle page; the 'object' field can be null.
*/
- private ItemInfo infoForCurrentScrollPosition() {
- final int width = getClientWidth();
- final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0;
+ private ItemInfo infoForFirstVisiblePage() {
+ final int startOffset = getScrollStart();
+ final int width = getPaddedWidth();
+ final float scrollOffset = width > 0 ? (float) startOffset / width : 0;
final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
+
int lastPos = -1;
float lastOffset = 0.f;
float lastWidth = 0.f;
boolean first = true;
-
ItemInfo lastItem = null;
- for (int i = 0; i < mItems.size(); i++) {
+
+ final int N = mItems.size();
+ for (int i = 0; i < N; i++) {
ItemInfo ii = mItems.get(i);
- float offset;
+
+ // Seek to position.
if (!first && ii.position != lastPos + 1) {
// Create a synthetic item for a missing page.
ii = mTempItem;
@@ -2109,17 +2190,18 @@ public class ViewPager extends ViewGroup {
ii.widthFactor = mAdapter.getPageWidth(ii.position);
i--;
}
- offset = ii.offset;
- final float leftBound = offset;
- final float rightBound = offset + ii.widthFactor + marginOffset;
- if (first || scrollOffset >= leftBound) {
- if (scrollOffset < rightBound || i == mItems.size() - 1) {
+ final float offset = ii.offset;
+ final float startBound = offset;
+ if (first || scrollOffset >= startBound) {
+ final float endBound = offset + ii.widthFactor + marginOffset;
+ if (scrollOffset < endBound || i == mItems.size() - 1) {
return ii;
}
} else {
return lastItem;
}
+
first = false;
lastPos = ii.position;
lastOffset = offset;
@@ -2130,13 +2212,28 @@ public class ViewPager extends ViewGroup {
return lastItem;
}
+ private int getScrollStart() {
+ if (isLayoutRtl()) {
+ return MAX_SCROLL_X - getScrollX();
+ } else {
+ return getScrollX();
+ }
+ }
+
+ /**
+ * @param currentPage the position of the page with the first visible starting edge
+ * @param pageOffset the fraction of the right-hand page that's visible
+ * @param velocity the velocity of the touch event stream
+ * @param deltaX the distance of the touch event stream
+ * @return the position of the target page
+ */
private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) {
int targetPage;
if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
- targetPage = velocity > 0 ? currentPage : currentPage + 1;
+ targetPage = currentPage - (velocity < 0 ? mLeftIncr : 0);
} else {
final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;
- targetPage = (int) (currentPage + pageOffset + truncator);
+ targetPage = (int) (currentPage - mLeftIncr * (pageOffset + truncator));
}
if (mItems.size() > 0) {
@@ -2144,7 +2241,7 @@ public class ViewPager extends ViewGroup {
final ItemInfo lastItem = mItems.get(mItems.size() - 1);
// Only let the user target pages we have items for
- targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
+ targetPage = MathUtils.constrain(targetPage, firstItem.position, lastItem.position);
}
return targetPage;
@@ -2205,6 +2302,7 @@ public class ViewPager extends ViewGroup {
int itemIndex = 0;
ItemInfo ii = mItems.get(0);
float offset = ii.offset;
+
final int itemCount = mItems.size();
final int firstPos = ii.position;
final int lastPos = mItems.get(itemCount - 1).position;
@@ -2213,156 +2311,39 @@ public class ViewPager extends ViewGroup {
ii = mItems.get(++itemIndex);
}
- float drawAt;
+ final float itemOffset;
+ final float widthFactor;
if (pos == ii.position) {
- drawAt = (ii.offset + ii.widthFactor) * width;
- offset = ii.offset + ii.widthFactor + marginOffset;
+ itemOffset = ii.offset;
+ widthFactor = ii.widthFactor;
+ } else {
+ itemOffset = offset;
+ widthFactor = mAdapter.getPageWidth(pos);
+ }
+
+ final float left;
+ final float scaledOffset = itemOffset * width;
+ if (isLayoutRtl()) {
+ left = MAX_SCROLL_X - scaledOffset;
} else {
- float widthFactor = mAdapter.getPageWidth(pos);
- drawAt = (offset + widthFactor) * width;
- offset += widthFactor + marginOffset;
+ left = scaledOffset + widthFactor * width;
}
- if (drawAt + mPageMargin > scrollX) {
- mMarginDrawable.setBounds((int) drawAt, mTopPageBounds,
- (int) (drawAt + mPageMargin + 0.5f), mBottomPageBounds);
+ offset = itemOffset + widthFactor + marginOffset;
+
+ if (left + mPageMargin > scrollX) {
+ mMarginDrawable.setBounds((int) left, mTopPageBounds,
+ (int) (left + mPageMargin + 0.5f), mBottomPageBounds);
mMarginDrawable.draw(canvas);
}
- if (drawAt > scrollX + width) {
+ if (left > scrollX + width) {
break; // No more visible, no sense in continuing
}
}
}
}
- /**
- * Start a fake drag of the pager.
- *
- * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager
- * with the touch scrolling of another view, while still letting the ViewPager
- * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)
- * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
- * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
- *
- * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag
- * is already in progress, this method will return false.
- *
- * @return true if the fake drag began successfully, false if it could not be started.
- *
- * @see #fakeDragBy(float)
- * @see #endFakeDrag()
- */
- public boolean beginFakeDrag() {
- if (mIsBeingDragged) {
- return false;
- }
- mFakeDragging = true;
- setScrollState(SCROLL_STATE_DRAGGING);
- mInitialMotionX = mLastMotionX = 0;
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- mVelocityTracker.clear();
- }
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
- mFakeDragBeginTime = time;
- return true;
- }
-
- /**
- * End a fake drag of the pager.
- *
- * @see #beginFakeDrag()
- * @see #fakeDragBy(float)
- */
- public void endFakeDrag() {
- if (!mFakeDragging) {
- throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
- }
-
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
- mPopulatePending = true;
- final int width = getClientWidth();
- final int scrollX = getScrollX();
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int currentPage = ii.position;
- final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
- final int totalDelta = (int) (mLastMotionX - mInitialMotionX);
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
- setCurrentItemInternal(nextPage, true, true, initialVelocity);
- endDrag();
-
- mFakeDragging = false;
- }
-
- /**
- * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.
- *
- * @param xOffset Offset in pixels to drag by.
- * @see #beginFakeDrag()
- * @see #endFakeDrag()
- */
- public void fakeDragBy(float xOffset) {
- if (!mFakeDragging) {
- throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
- }
-
- mLastMotionX += xOffset;
-
- float oldScrollX = getScrollX();
- float scrollX = oldScrollX - xOffset;
- final int width = getClientWidth();
-
- float leftBound = width * mFirstOffset;
- float rightBound = width * mLastOffset;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- leftBound = firstItem.offset * width;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- rightBound = lastItem.offset * width;
- }
-
- if (scrollX < leftBound) {
- scrollX = leftBound;
- } else if (scrollX > rightBound) {
- scrollX = rightBound;
- }
- // Don't lose the rounded component
- mLastMotionX += scrollX - (int) scrollX;
- scrollTo((int) scrollX, getScrollY());
- pageScrolled((int) scrollX);
-
- // Synthesize an event for the VelocityTracker.
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
- mLastMotionX, 0, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
- }
-
- /**
- * Returns true if a fake drag is in progress.
- *
- * @return true if currently in a fake drag, false otherwise.
- *
- * @see #beginFakeDrag()
- * @see #fakeDragBy(float)
- * @see #endFakeDrag()
- */
- public boolean isFakeDragging() {
- return mFakeDragging;
- }
-
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = ev.getActionIndex();
final int pointerId = ev.getPointerId(pointerIndex);
@@ -2408,7 +2389,7 @@ public class ViewPager extends ViewGroup {
return false;
}
- final int width = getClientWidth();
+ final int width = getPaddedWidth();
final int scrollX = getScrollX();
if (direction < 0) {
return (scrollX > (int) (width * mFirstOffset));
@@ -2438,12 +2419,11 @@ public class ViewPager extends ViewGroup {
final int count = group.getChildCount();
// Count backwards - let topmost views consume scroll distance first.
for (int i = count - 1; i >= 0; i--) {
- // TODO: Add versioned support here for transformed views.
- // This will not work for transformed views in Honeycomb+
+ // TODO: Add support for transformed views.
final View child = group.getChildAt(i);
- if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
- y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
- canScroll(child, true, dx, x + scrollX - child.getLeft(),
+ if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
+ && y + scrollY >= child.getTop() && y + scrollY < child.getBottom()
+ && canScroll(child, true, dx, x + scrollX - child.getLeft(),
y + scrollY - child.getTop())) {
return true;
}
@@ -2582,19 +2562,22 @@ public class ViewPager extends ViewGroup {
}
boolean pageLeft() {
- if (mCurItem > 0) {
- setCurrentItem(mCurItem-1, true);
- return true;
- }
- return false;
+ return setCurrentItemInternal(mCurItem + mLeftIncr, true, false);
}
boolean pageRight() {
- if (mAdapter != null && mCurItem < (mAdapter.getCount()-1)) {
- setCurrentItem(mCurItem+1, true);
- return true;
+ return setCurrentItemInternal(mCurItem - mLeftIncr, true, false);
+ }
+
+ @Override
+ public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ if (layoutDirection == LAYOUT_DIRECTION_LTR) {
+ mLeftIncr = -1;
+ } else {
+ mLeftIncr = 1;
}
- return false;
}
/**
diff --git a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
index 6ac0d89..26f5bf4 100644
--- a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
+++ b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
@@ -17,159 +17,58 @@
package com.android.server.backup;
import android.app.AppGlobals;
-import android.app.backup.BackupDataInputStream;
-import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupHelper;
-import android.content.Context;
+import android.app.backup.BlobBackupHelper;
import android.content.pm.IPackageManager;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Slog;
-import android.util.Xml;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.org.bouncycastle.util.Arrays;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.EOFException;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-public class PreferredActivityBackupHelper implements BackupHelper {
+public class PreferredActivityBackupHelper extends BlobBackupHelper {
private static final String TAG = "PreferredBackup";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
// current schema of the backup state blob
- private static final int STATE_VERSION = 1;
+ private static final int STATE_VERSION = 2;
// key under which the preferred-activity state blob is committed to backup
private static final String KEY_PREFERRED = "preferred-activity";
- final Context mContext;
-
- public PreferredActivityBackupHelper(Context context) {
- mContext = context;
- }
-
- // The fds passed here are shared among all helpers, so we mustn't close them
- private void writeState(ParcelFileDescriptor stateFile, byte[] payload) {
- try {
- FileOutputStream fos = new FileOutputStream(stateFile.getFileDescriptor());
-
- // We explicitly don't close 'out' because we must not close the backing fd.
- // The FileOutputStream will not close it implicitly.
- @SuppressWarnings("resource")
- DataOutputStream out = new DataOutputStream(fos);
-
- out.writeInt(STATE_VERSION);
- if (payload == null) {
- out.writeInt(0);
- } else {
- out.writeInt(payload.length);
- out.write(payload);
- }
- } catch (IOException e) {
- Slog.e(TAG, "Unable to write updated state", e);
- }
- }
-
- private byte[] readState(ParcelFileDescriptor oldStateFd) {
- FileInputStream fis = new FileInputStream(oldStateFd.getFileDescriptor());
- BufferedInputStream bis = new BufferedInputStream(fis);
-
- @SuppressWarnings("resource")
- DataInputStream in = new DataInputStream(bis);
-
- byte[] oldState = null;
- try {
- int version = in.readInt();
- if (version == STATE_VERSION) {
- int size = in.readInt();
- if (size > 0) {
- if (size > 200*1024) {
- Slog.w(TAG, "Suspiciously large state blog; ignoring. N=" + size);
- } else {
- // size looks okay; make the return buffer and fill it
- oldState = new byte[size];
- in.read(oldState);
- }
- }
- } else {
- Slog.w(TAG, "Prior state from unrecognized version " + version);
- }
- } catch (EOFException e) {
- // Empty file is expected on first backup, so carry on. If the state
- // is truncated we just treat it the same way.
- oldState = null;
- } catch (Exception e) {
- Slog.w(TAG, "Error examing prior backup state " + e.getMessage());
- oldState = null;
- }
-
- return oldState;
+ public PreferredActivityBackupHelper() {
+ super(STATE_VERSION, KEY_PREFERRED);
}
@Override
- public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) {
- byte[] payload = null;
- try {
- byte[] oldPayload = readState(oldState);
-
+ protected byte[] getBackupPayload(String key) {
+ if (KEY_PREFERRED.equals(key)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Checking whether to back up");
+ }
IPackageManager pm = AppGlobals.getPackageManager();
- byte[] newPayload = pm.getPreferredActivityBackup(UserHandle.USER_OWNER);
- if (!Arrays.areEqual(oldPayload, newPayload)) {
- if (DEBUG) {
- Slog.i(TAG, "State has changed => writing new preferred app payload");
- }
- data.writeEntityHeader(KEY_PREFERRED, newPayload.length);
- data.writeEntityData(newPayload, newPayload.length);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "No change to state => not writing to wire");
- }
+ try {
+ return pm.getPreferredActivityBackup(UserHandle.USER_OWNER);
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to store backup payload", e);
+ // fall through to report null state
}
-
- // Always need to re-record the state, even if nothing changed
- payload = newPayload;
- } catch (Exception e) {
- // On failures we'll wind up committing a zero-size state payload. This is
- // a forward-safe situation because we know we commit the entire new payload
- // on prior-state mismatch.
- Slog.w(TAG, "Unable to record preferred activities", e);
- } finally {
- writeState(newState, payload);
+ } else {
+ Slog.w(TAG, "Unexpected backup key " + key);
}
+ return null;
}
@Override
- public void restoreEntity(BackupDataInputStream data) {
- IPackageManager pm = AppGlobals.getPackageManager();
- try {
- byte[] payload = new byte[data.size()];
- data.read(payload);
+ protected void applyRestoredPayload(String key, byte[] payload) {
+ if (KEY_PREFERRED.equals(key)) {
if (DEBUG) {
- Slog.i(TAG, "Restoring preferred activities; size=" + payload.length);
+ Slog.v(TAG, "Restoring");
}
- pm.restorePreferredActivities(payload, UserHandle.USER_OWNER);
- } catch (Exception e) {
- Slog.e(TAG, "Exception reading restore data", e);
+ IPackageManager pm = AppGlobals.getPackageManager();
+ try {
+ pm.restorePreferredActivities(payload, UserHandle.USER_OWNER);
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to restore", e);
+ }
+ } else {
+ Slog.w(TAG, "Unexpected restore key " + key);
}
}
-
- @Override
- public void writeNewStateDescription(ParcelFileDescriptor newState) {
- writeState(newState, null);
- }
-
}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 8e97aa9..a80abce 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -94,7 +94,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this, files, keys));
addHelper(RECENTS_HELPER, new RecentsBackupHelper(this));
addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
- addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this));
+ addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
super.onBackup(oldState, data, newState);
@@ -129,7 +129,8 @@ public class SystemBackupAgent extends BackupAgentHelper {
new String[] { WALLPAPER_IMAGE_KEY} ));
addHelper(RECENTS_HELPER, new RecentsBackupHelper(this));
addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
- addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this));
+ addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
+ addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
try {
super.onRestore(data, appVersionCode, newState);