Merge "Rename home to settings home" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 732c500..429674c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5031,13 +5031,13 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
- method public boolean getHintContentIntentLaunchesActivity();
+ method public boolean getHintLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
- method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+ method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -49061,6 +49061,7 @@
ctor public BufferedReader(java.io.Reader, int);
ctor public BufferedReader(java.io.Reader);
method public void close() throws java.io.IOException;
+ method public java.util.stream.Stream<java.lang.String> lines();
method public int read(char[], int, int) throws java.io.IOException;
method public java.lang.String readLine() throws java.io.IOException;
}
@@ -49931,6 +49932,11 @@
ctor public UTFDataFormatException(java.lang.String);
}
+ public class UncheckedIOException extends java.lang.RuntimeException {
+ ctor public UncheckedIOException(java.lang.String, java.io.IOException);
+ ctor public UncheckedIOException(java.io.IOException);
+ }
+
public class UnsupportedEncodingException extends java.io.IOException {
ctor public UnsupportedEncodingException();
ctor public UnsupportedEncodingException(java.lang.String);
@@ -57587,6 +57593,7 @@
method public void set(int, int);
method public void set(int, int, boolean);
method public int size();
+ method public java.util.stream.IntStream stream();
method public byte[] toByteArray();
method public long[] toLongArray();
method public static java.util.BitSet valueOf(long[]);
@@ -58042,6 +58049,7 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public boolean replace(K, V, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -58085,6 +58093,7 @@
method public synchronized boolean remove(java.lang.Object, java.lang.Object);
method public synchronized boolean replace(K, V, V);
method public synchronized V replace(K, V);
+ method public synchronized void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -58625,6 +58634,18 @@
public class Random implements java.io.Serializable {
ctor public Random();
ctor public Random(long);
+ method public java.util.stream.DoubleStream doubles(long);
+ method public java.util.stream.DoubleStream doubles();
+ method public java.util.stream.DoubleStream doubles(long, double, double);
+ method public java.util.stream.DoubleStream doubles(double, double);
+ method public java.util.stream.IntStream ints(long);
+ method public java.util.stream.IntStream ints();
+ method public java.util.stream.IntStream ints(long, int, int);
+ method public java.util.stream.IntStream ints(int, int);
+ method public java.util.stream.LongStream longs(long);
+ method public java.util.stream.LongStream longs();
+ method public java.util.stream.LongStream longs(long, long, long);
+ method public java.util.stream.LongStream longs(long, long);
method protected int next(int);
method public boolean nextBoolean();
method public void nextBytes(byte[]);
@@ -59066,6 +59087,7 @@
method public java.util.NavigableSet<K> navigableKeySet();
method public java.util.Map.Entry<K, V> pollFirstEntry();
method public java.util.Map.Entry<K, V> pollLastEntry();
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.NavigableMap<K, V> subMap(K, boolean, K, boolean);
method public java.util.SortedMap<K, V> subMap(K, K);
method public java.util.NavigableMap<K, V> tailMap(K, boolean);
@@ -59167,6 +59189,7 @@
ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
}
@@ -60140,18 +60163,6 @@
public class ThreadLocalRandom extends java.util.Random {
method public static java.util.concurrent.ThreadLocalRandom current();
- method public java.util.stream.DoubleStream doubles(long);
- method public java.util.stream.DoubleStream doubles();
- method public java.util.stream.DoubleStream doubles(long, double, double);
- method public java.util.stream.DoubleStream doubles(double, double);
- method public java.util.stream.IntStream ints(long);
- method public java.util.stream.IntStream ints();
- method public java.util.stream.IntStream ints(long, int, int);
- method public java.util.stream.IntStream ints(int, int);
- method public java.util.stream.LongStream longs(long);
- method public java.util.stream.LongStream longs();
- method public java.util.stream.LongStream longs(long, long, long);
- method public java.util.stream.LongStream longs(long, long);
method public double nextDouble(double);
method public double nextDouble(double, double);
method public int nextInt(int, int);
@@ -61524,6 +61535,7 @@
}
public final class Pattern implements java.io.Serializable {
+ method public java.util.function.Predicate<java.lang.String> asPredicate();
method public static java.util.regex.Pattern compile(java.lang.String);
method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
method public int flags();
@@ -61533,6 +61545,7 @@
method public static java.lang.String quote(java.lang.String);
method public java.lang.String[] split(java.lang.CharSequence, int);
method public java.lang.String[] split(java.lang.CharSequence);
+ method public java.util.stream.Stream<java.lang.String> splitAsStream(java.lang.CharSequence);
field public static final int CANON_EQ = 128; // 0x80
field public static final int CASE_INSENSITIVE = 2; // 0x2
field public static final int COMMENTS = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index 56b2f58..befff23 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5165,13 +5165,13 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
- method public boolean getHintContentIntentLaunchesActivity();
+ method public boolean getHintLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
- method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+ method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -52174,6 +52174,7 @@
ctor public BufferedReader(java.io.Reader, int);
ctor public BufferedReader(java.io.Reader);
method public void close() throws java.io.IOException;
+ method public java.util.stream.Stream<java.lang.String> lines();
method public int read(char[], int, int) throws java.io.IOException;
method public java.lang.String readLine() throws java.io.IOException;
}
@@ -53044,6 +53045,11 @@
ctor public UTFDataFormatException(java.lang.String);
}
+ public class UncheckedIOException extends java.lang.RuntimeException {
+ ctor public UncheckedIOException(java.lang.String, java.io.IOException);
+ ctor public UncheckedIOException(java.io.IOException);
+ }
+
public class UnsupportedEncodingException extends java.io.IOException {
ctor public UnsupportedEncodingException();
ctor public UnsupportedEncodingException(java.lang.String);
@@ -60700,6 +60706,7 @@
method public void set(int, int);
method public void set(int, int, boolean);
method public int size();
+ method public java.util.stream.IntStream stream();
method public byte[] toByteArray();
method public long[] toLongArray();
method public static java.util.BitSet valueOf(long[]);
@@ -61155,6 +61162,7 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public boolean replace(K, V, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -61198,6 +61206,7 @@
method public synchronized boolean remove(java.lang.Object, java.lang.Object);
method public synchronized boolean replace(K, V, V);
method public synchronized V replace(K, V);
+ method public synchronized void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -61738,6 +61747,18 @@
public class Random implements java.io.Serializable {
ctor public Random();
ctor public Random(long);
+ method public java.util.stream.DoubleStream doubles(long);
+ method public java.util.stream.DoubleStream doubles();
+ method public java.util.stream.DoubleStream doubles(long, double, double);
+ method public java.util.stream.DoubleStream doubles(double, double);
+ method public java.util.stream.IntStream ints(long);
+ method public java.util.stream.IntStream ints();
+ method public java.util.stream.IntStream ints(long, int, int);
+ method public java.util.stream.IntStream ints(int, int);
+ method public java.util.stream.LongStream longs(long);
+ method public java.util.stream.LongStream longs();
+ method public java.util.stream.LongStream longs(long, long, long);
+ method public java.util.stream.LongStream longs(long, long);
method protected int next(int);
method public boolean nextBoolean();
method public void nextBytes(byte[]);
@@ -62179,6 +62200,7 @@
method public java.util.NavigableSet<K> navigableKeySet();
method public java.util.Map.Entry<K, V> pollFirstEntry();
method public java.util.Map.Entry<K, V> pollLastEntry();
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.NavigableMap<K, V> subMap(K, boolean, K, boolean);
method public java.util.SortedMap<K, V> subMap(K, K);
method public java.util.NavigableMap<K, V> tailMap(K, boolean);
@@ -62280,6 +62302,7 @@
ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
}
@@ -63253,18 +63276,6 @@
public class ThreadLocalRandom extends java.util.Random {
method public static java.util.concurrent.ThreadLocalRandom current();
- method public java.util.stream.DoubleStream doubles(long);
- method public java.util.stream.DoubleStream doubles();
- method public java.util.stream.DoubleStream doubles(long, double, double);
- method public java.util.stream.DoubleStream doubles(double, double);
- method public java.util.stream.IntStream ints(long);
- method public java.util.stream.IntStream ints();
- method public java.util.stream.IntStream ints(long, int, int);
- method public java.util.stream.IntStream ints(int, int);
- method public java.util.stream.LongStream longs(long);
- method public java.util.stream.LongStream longs();
- method public java.util.stream.LongStream longs(long, long, long);
- method public java.util.stream.LongStream longs(long, long);
method public double nextDouble(double);
method public double nextDouble(double, double);
method public int nextInt(int, int);
@@ -64637,6 +64648,7 @@
}
public final class Pattern implements java.io.Serializable {
+ method public java.util.function.Predicate<java.lang.String> asPredicate();
method public static java.util.regex.Pattern compile(java.lang.String);
method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
method public int flags();
@@ -64646,6 +64658,7 @@
method public static java.lang.String quote(java.lang.String);
method public java.lang.String[] split(java.lang.CharSequence, int);
method public java.lang.String[] split(java.lang.CharSequence);
+ method public java.util.stream.Stream<java.lang.String> splitAsStream(java.lang.CharSequence);
field public static final int CANON_EQ = 128; // 0x80
field public static final int CASE_INSENSITIVE = 2; // 0x2
field public static final int COMMENTS = 4; // 0x4
diff --git a/api/test-current.txt b/api/test-current.txt
index 679347d..d39e0b7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5031,13 +5031,13 @@
method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
method public java.lang.CharSequence getCancelLabel();
method public java.lang.CharSequence getConfirmLabel();
- method public boolean getHintContentIntentLaunchesActivity();
+ method public boolean getHintLaunchesActivity();
method public java.lang.CharSequence getInProgressLabel();
method public boolean isAvailableOffline();
method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
- method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
+ method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
}
@@ -49139,6 +49139,7 @@
ctor public BufferedReader(java.io.Reader, int);
ctor public BufferedReader(java.io.Reader);
method public void close() throws java.io.IOException;
+ method public java.util.stream.Stream<java.lang.String> lines();
method public int read(char[], int, int) throws java.io.IOException;
method public java.lang.String readLine() throws java.io.IOException;
}
@@ -50009,6 +50010,11 @@
ctor public UTFDataFormatException(java.lang.String);
}
+ public class UncheckedIOException extends java.lang.RuntimeException {
+ ctor public UncheckedIOException(java.lang.String, java.io.IOException);
+ ctor public UncheckedIOException(java.io.IOException);
+ }
+
public class UnsupportedEncodingException extends java.io.IOException {
ctor public UnsupportedEncodingException();
ctor public UnsupportedEncodingException(java.lang.String);
@@ -57665,6 +57671,7 @@
method public void set(int, int);
method public void set(int, int, boolean);
method public int size();
+ method public java.util.stream.IntStream stream();
method public byte[] toByteArray();
method public long[] toLongArray();
method public static java.util.BitSet valueOf(long[]);
@@ -58120,6 +58127,7 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public boolean replace(K, V, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -58163,6 +58171,7 @@
method public synchronized boolean remove(java.lang.Object, java.lang.Object);
method public synchronized boolean replace(K, V, V);
method public synchronized V replace(K, V);
+ method public synchronized void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -58703,6 +58712,18 @@
public class Random implements java.io.Serializable {
ctor public Random();
ctor public Random(long);
+ method public java.util.stream.DoubleStream doubles(long);
+ method public java.util.stream.DoubleStream doubles();
+ method public java.util.stream.DoubleStream doubles(long, double, double);
+ method public java.util.stream.DoubleStream doubles(double, double);
+ method public java.util.stream.IntStream ints(long);
+ method public java.util.stream.IntStream ints();
+ method public java.util.stream.IntStream ints(long, int, int);
+ method public java.util.stream.IntStream ints(int, int);
+ method public java.util.stream.LongStream longs(long);
+ method public java.util.stream.LongStream longs();
+ method public java.util.stream.LongStream longs(long, long, long);
+ method public java.util.stream.LongStream longs(long, long);
method protected int next(int);
method public boolean nextBoolean();
method public void nextBytes(byte[]);
@@ -59144,6 +59165,7 @@
method public java.util.NavigableSet<K> navigableKeySet();
method public java.util.Map.Entry<K, V> pollFirstEntry();
method public java.util.Map.Entry<K, V> pollLastEntry();
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.NavigableMap<K, V> subMap(K, boolean, K, boolean);
method public java.util.SortedMap<K, V> subMap(K, K);
method public java.util.NavigableMap<K, V> tailMap(K, boolean);
@@ -59245,6 +59267,7 @@
ctor public WeakHashMap(java.util.Map<? extends K, ? extends V>);
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
}
}
@@ -60218,18 +60241,6 @@
public class ThreadLocalRandom extends java.util.Random {
method public static java.util.concurrent.ThreadLocalRandom current();
- method public java.util.stream.DoubleStream doubles(long);
- method public java.util.stream.DoubleStream doubles();
- method public java.util.stream.DoubleStream doubles(long, double, double);
- method public java.util.stream.DoubleStream doubles(double, double);
- method public java.util.stream.IntStream ints(long);
- method public java.util.stream.IntStream ints();
- method public java.util.stream.IntStream ints(long, int, int);
- method public java.util.stream.IntStream ints(int, int);
- method public java.util.stream.LongStream longs(long);
- method public java.util.stream.LongStream longs();
- method public java.util.stream.LongStream longs(long, long, long);
- method public java.util.stream.LongStream longs(long, long);
method public double nextDouble(double);
method public double nextDouble(double, double);
method public int nextInt(int, int);
@@ -61602,6 +61613,7 @@
}
public final class Pattern implements java.io.Serializable {
+ method public java.util.function.Predicate<java.lang.String> asPredicate();
method public static java.util.regex.Pattern compile(java.lang.String);
method public static java.util.regex.Pattern compile(java.lang.String, int) throws java.util.regex.PatternSyntaxException;
method public int flags();
@@ -61611,6 +61623,7 @@
method public static java.lang.String quote(java.lang.String);
method public java.lang.String[] split(java.lang.CharSequence, int);
method public java.lang.String[] split(java.lang.CharSequence);
+ method public java.util.stream.Stream<java.lang.String> splitAsStream(java.lang.CharSequence);
field public static final int CANON_EQ = 128; // 0x80
field public static final int CASE_INSENSITIVE = 2; // 0x2
field public static final int COMMENTS = 4; // 0x4
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 784872c..a759719 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1419,7 +1419,7 @@
* an activity and transitions should be generated, false otherwise.
* @return this object for method chaining
*/
- public WearableExtender setHintContentIntentLaunchesActivity(
+ public WearableExtender setHintLaunchesActivity(
boolean hintLaunchesActivity) {
setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
return this;
@@ -1432,7 +1432,7 @@
* should be generated, false otherwise. The default value is {@code false} if this was
* never set.
*/
- public boolean getHintContentIntentLaunchesActivity() {
+ public boolean getHintLaunchesActivity() {
return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
}
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 104feb5..585d2a3 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -366,6 +366,9 @@
*
* <p>NOTE: {@code WebView} does not honor this flag.
*
+ * <p>This flag is ignored on Android N and above if an Android Network Security Config is
+ * present.
+ *
* <p>This flag comes from
* {@link android.R.styleable#AndroidManifestApplication_usesCleartextTraffic
* android:usesCleartextTraffic} of the <application> tag.
diff --git a/core/java/android/inputmethodservice/CompactExtractEditLayout.java b/core/java/android/inputmethodservice/CompactExtractEditLayout.java
new file mode 100644
index 0000000..35c54b2
--- /dev/null
+++ b/core/java/android/inputmethodservice/CompactExtractEditLayout.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 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.inputmethodservice;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.annotation.FractionRes;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * A special purpose layout for the editor extract view for tiny (sub 250dp) screens.
+ * The layout is based on sizes proportional to screen pixel size to provide for the
+ * best layout fidelity on varying pixel sizes and densities.
+ *
+ * @hide
+ */
+public class CompactExtractEditLayout extends LinearLayout {
+ private View mInputExtractEditText;
+ private View mInputExtractAccessories;
+ private View mInputExtractAction;
+ private boolean mPerformLayoutChanges;
+
+ public CompactExtractEditLayout(Context context) {
+ super(context);
+ }
+
+ public CompactExtractEditLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CompactExtractEditLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mInputExtractEditText = findViewById(com.android.internal.R.id.inputExtractEditText);
+ mInputExtractAccessories = findViewById(com.android.internal.R.id.inputExtractAccessories);
+ mInputExtractAction = findViewById(com.android.internal.R.id.inputExtractAction);
+
+ if (mInputExtractEditText != null && mInputExtractAccessories != null
+ && mInputExtractAction != null) {
+ mPerformLayoutChanges = true;
+ }
+ }
+
+ private int applyFractionInt(@FractionRes int fraction, int whole) {
+ return Math.round(getResources().getFraction(fraction, whole, whole));
+ }
+
+ private static void setLayoutHeight(View v, int px) {
+ ViewGroup.LayoutParams lp = v.getLayoutParams();
+ lp.height = px;
+ v.setLayoutParams(lp);
+ }
+
+ private static void setLayoutMarginBottom(View v, int px) {
+ ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
+ lp.bottomMargin = px;
+ v.setLayoutParams(lp);
+ }
+
+ private void applyProportionalLayout(int screenWidthPx, int screenHeightPx) {
+ if (getResources().getConfiguration().isScreenRound()) {
+ setGravity(Gravity.BOTTOM);
+ }
+ setLayoutHeight(this, applyFractionInt(
+ com.android.internal.R.fraction.input_extract_layout_height, screenHeightPx));
+
+ setPadding(
+ applyFractionInt(com.android.internal.R.fraction.input_extract_layout_padding_left,
+ screenWidthPx),
+ 0,
+ applyFractionInt(com.android.internal.R.fraction.input_extract_layout_padding_right,
+ screenWidthPx),
+ 0);
+
+ setLayoutMarginBottom(mInputExtractEditText,
+ applyFractionInt(com.android.internal.R.fraction.input_extract_text_margin_bottom,
+ screenHeightPx));
+
+ setLayoutMarginBottom(mInputExtractAccessories,
+ applyFractionInt(com.android.internal.R.fraction.input_extract_action_margin_bottom,
+ screenHeightPx));
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mPerformLayoutChanges) {
+ Resources res = getResources();
+ DisplayMetrics dm = res.getDisplayMetrics();
+ int heightPixels = dm.heightPixels;
+ int widthPixels = dm.widthPixels;
+ applyProportionalLayout(widthPixels, heightPixels);
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index cc201bc..085b97c 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -68,9 +68,10 @@
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
-import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.ImageButton;
import android.widget.LinearLayout;
+import android.widget.TextView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -302,7 +303,7 @@
boolean mExtractViewHidden;
ExtractEditText mExtractEditText;
ViewGroup mExtractAccessories;
- Button mExtractAction;
+ View mExtractAction;
ExtractedText mExtractedText;
int mExtractedToken;
@@ -1344,7 +1345,7 @@
mExtractEditText = (ExtractEditText)view.findViewById(
com.android.internal.R.id.inputExtractEditText);
mExtractEditText.setIME(this);
- mExtractAction = (Button)view.findViewById(
+ mExtractAction = view.findViewById(
com.android.internal.R.id.inputExtractAction);
if (mExtractAction != null) {
mExtractAccessories = (ViewGroup)view.findViewById(
@@ -2408,7 +2409,35 @@
return getText(com.android.internal.R.string.ime_action_default);
}
}
-
+
+ /**
+ * Return a drawable resource id that can be used as a button icon for the given
+ * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
+ *
+ * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
+ *
+ * @return Returns a drawable resource id to use.
+ */
+ @DrawableRes
+ private int getIconForImeAction(int imeOptions) {
+ switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
+ case EditorInfo.IME_ACTION_GO:
+ return com.android.internal.R.drawable.ic_input_extract_action_go;
+ case EditorInfo.IME_ACTION_SEARCH:
+ return com.android.internal.R.drawable.ic_input_extract_action_search;
+ case EditorInfo.IME_ACTION_SEND:
+ return com.android.internal.R.drawable.ic_input_extract_action_send;
+ case EditorInfo.IME_ACTION_NEXT:
+ return com.android.internal.R.drawable.ic_input_extract_action_next;
+ case EditorInfo.IME_ACTION_DONE:
+ return com.android.internal.R.drawable.ic_input_extract_action_done;
+ case EditorInfo.IME_ACTION_PREVIOUS:
+ return com.android.internal.R.drawable.ic_input_extract_action_previous;
+ default:
+ return com.android.internal.R.drawable.ic_input_extract_action_return;
+ }
+ }
+
/**
* Called when the fullscreen-mode extracting editor info has changed,
* to determine whether the extracting (extract text and candidates) portion
@@ -2459,10 +2488,20 @@
if (hasAction) {
mExtractAccessories.setVisibility(View.VISIBLE);
if (mExtractAction != null) {
- if (ei.actionLabel != null) {
- mExtractAction.setText(ei.actionLabel);
+ if (mExtractAction instanceof ImageButton) {
+ ((ImageButton) mExtractAction)
+ .setImageResource(getIconForImeAction(ei.imeOptions));
+ if (ei.actionLabel != null) {
+ mExtractAction.setContentDescription(ei.actionLabel);
+ } else {
+ mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions));
+ }
} else {
- mExtractAction.setText(getTextForImeAction(ei.imeOptions));
+ if (ei.actionLabel != null) {
+ ((TextView) mExtractAction).setText(ei.actionLabel);
+ } else {
+ ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions));
+ }
}
mExtractAction.setOnClickListener(mActionClickListener);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 638be51..fdb1cef 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1581,6 +1581,8 @@
public static final class System extends NameValueTable {
public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
+ private static final float DEFAULT_FONT_SCALE = 1.0f;
+
/** @hide */
public static interface Validator {
public boolean validate(String value);
@@ -2089,9 +2091,9 @@
public static void getConfigurationForUser(ContentResolver cr, Configuration outConfig,
int userHandle) {
outConfig.fontScale = Settings.System.getFloatForUser(
- cr, FONT_SCALE, outConfig.fontScale, userHandle);
+ cr, FONT_SCALE, DEFAULT_FONT_SCALE, userHandle);
if (outConfig.fontScale < 0) {
- outConfig.fontScale = 1;
+ outConfig.fontScale = DEFAULT_FONT_SCALE;
}
outConfig.setLocales(LocaleList.forLanguageTags(
Settings.System.getStringForUser(cr, SYSTEM_LOCALES, userHandle)));
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index c972476..c4ed94f 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -893,6 +893,10 @@
nSerializeDisplayListTree(mNativeProxy);
}
+ public static boolean copySurfaceInto(Surface surface, Bitmap bitmap) {
+ return nCopySurfaceInto(surface, bitmap);
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -1029,4 +1033,6 @@
private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
+
+ private static native boolean nCopySurfaceInto(Surface surface, Bitmap bitmap);
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f2bf9e1..1f2acc9 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -760,7 +760,7 @@
} else {
try {
AppGlobals.getPackageManager().setLastChosenActivity(intent,
- intent.resolveTypeIfNeeded(getContentResolver()),
+ intent.resolveType(getContentResolver()),
PackageManager.MATCH_DEFAULT_ONLY,
filter, bestMatch, intent.getComponent());
} catch (RemoteException re) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index f9ac563..3aa7719 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -210,7 +210,6 @@
private Drawable mResizingBackgroundDrawable;
private Drawable mCaptionBackgroundDrawable;
private Drawable mUserCaptionBackgroundDrawable;
- private Drawable mOriginalBackgroundDrawable;
private float mAvailableWidth;
@@ -891,11 +890,6 @@
mBackgroundPadding.setEmpty();
}
drawableChanged();
-
- // Make sure we don't reset to the old drawable when finishing resizing.
- if (mResizeMode != RESIZE_MODE_INVALID) {
- mOriginalBackgroundDrawable = null;
- }
}
}
@@ -1960,9 +1954,6 @@
updateElevation();
updateColorViews(null /* insets */, false);
-
- mOriginalBackgroundDrawable = getBackground();
- setBackgroundDrawable(null);
}
mResizeMode = resizeMode;
getViewRootImpl().requestInvalidateRootRenderNode();
@@ -1974,10 +1965,6 @@
updateColorViews(null /* insets */, false);
mResizeMode = RESIZE_MODE_INVALID;
getViewRootImpl().requestInvalidateRootRenderNode();
- if (mOriginalBackgroundDrawable != null) {
- setBackgroundDrawable(mOriginalBackgroundDrawable);
- mOriginalBackgroundDrawable = null;
- }
}
@Override
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 90d69d2..5c961d9 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -370,10 +370,6 @@
retVal = 0;
break;
- case CONTEXT_HUB_LOAD_OS:
- retVal = 0;
- break;
-
default:
retVal = -1;
break;
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 3d65209..faa4192 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -663,6 +663,14 @@
proxy->setContentDrawBounds(left, top, right, bottom);
}
+static jboolean android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env,
+ jobject clazz, jobject jsurface, jobject jbitmap) {
+ SkBitmap bitmap;
+ GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
+ sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
+ return RenderProxy::copySurfaceInto(surface, &bitmap);
+}
+
// ----------------------------------------------------------------------------
// FrameMetricsObserver
// ----------------------------------------------------------------------------
@@ -768,6 +776,8 @@
{ "nRemoveFrameMetricsObserver",
"(JJ)V",
(void*)android_view_ThreadedRenderer_removeFrameMetricsObserver },
+ { "nCopySurfaceInto", "(Landroid/view/Surface;Landroid/graphics/Bitmap;)Z",
+ (void*)android_view_ThreadedRenderer_copySurfaceInto },
};
int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9a2e39c7..778f797 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -420,6 +420,7 @@
<protected-broadcast android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
<protected-broadcast android:name="android.os.storage.action.DISK_SCANNED" />
<protected-broadcast android:name="com.android.server.action.UPDATE_TWILIGHT_STATE" />
+ <protected-broadcast android:name="com.android.server.action.RESET_TWILIGHT_AUTO" />
<protected-broadcast android:name="com.android.server.device_idle.STEP_IDLE_STATE" />
<protected-broadcast android:name="com.android.server.device_idle.STEP_LIGHT_IDLE_STATE" />
<protected-broadcast android:name="com.android.server.Wifi.action.TOGGLE_PNO" />
diff --git a/core/res/res/drawable-hdpi/ic_launcher_android.png b/core/res/res/drawable-hdpi/ic_launcher_android.png
index cce5187..2e9b196 100644
--- a/core/res/res/drawable-hdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-hdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_launcher_android.png b/core/res/res/drawable-ldpi/ic_launcher_android.png
index 628a8de..245e4b7 100644
--- a/core/res/res/drawable-ldpi/ic_launcher_android.png
+++ b/core/res/res/drawable-ldpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_launcher_android.png b/core/res/res/drawable-mdpi/ic_launcher_android.png
index 6a97d5b..baacd4f 100644
--- a/core/res/res/drawable-mdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-mdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_launcher_android.png b/core/res/res/drawable-xhdpi/ic_launcher_android.png
index b1097d6..00b69a5 100644
--- a/core/res/res/drawable-xhdpi/ic_launcher_android.png
+++ b/core/res/res/drawable-xhdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_launcher_android.png b/core/res/res/drawable-xxhdpi/ic_launcher_android.png
new file mode 100644
index 0000000..ad05cd5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable/ic_input_extract_action_done.xml b/core/res/res/drawable/ic_input_extract_action_done.xml
new file mode 100644
index 0000000..f6e872e
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_done.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M18,32.34L9.66,24l-2.83,2.83L18,38l24,-24 -2.83,-2.83z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_go.xml b/core/res/res/drawable/ic_input_extract_action_go.xml
new file mode 100644
index 0000000..edbc826
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_go.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M6,22h28.34l-7.17,-7.17L30,12l12,12 -12,12 -2.83,-2.83L34.34,26H6z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_next.xml b/core/res/res/drawable/ic_input_extract_action_next.xml
new file mode 100644
index 0000000..ffef346
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_next.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M23.17,14.83L30.34,22H2v4h28.34l-7.17,7.17L26,36l12,-12 -12,-12 -2.83,2.83zM40,12v24h4V12h-4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_previous.xml b/core/res/res/drawable/ic_input_extract_action_previous.xml
new file mode 100644
index 0000000..89777b0
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_previous.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M22.83,14.83L15.66,22H44v4H15.66l7.17,7.17L20,36 8,24l12,-12 2.83,2.83zM6,12v24H2V12h4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_return.xml b/core/res/res/drawable/ic_input_extract_action_return.xml
new file mode 100644
index 0000000..cb2de5a
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_return.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M38,14v8H11.66l7.17,-7.17L16,12 4,24l12,12 2.83,-2.83L11.66,26H42V14z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_search.xml b/core/res/res/drawable/ic_input_extract_action_search.xml
new file mode 100644
index 0000000..dcbcdbf
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_search.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector android:height="24dp" android:viewportHeight="48.0"
+ android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FFFFFF" android:pathData="M31,28h-1.59l-0.55,-0.55C30.82,25.18 32,22.23 32,19c0,-7.18 -5.82,-13 -13,-13S6,11.82 6,19s5.82,13 13,13c3.23,0 6.18,-1.18 8.45,-3.13l0.55,0.55L28,31l10,9.98L40.98,38 31,28zM19,28c-4.97,0 -9,-4.03 -9,-9s4.03,-9 9,-9 9,4.03 9,9 -4.03,9 -9,9z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_send.xml b/core/res/res/drawable/ic_input_extract_action_send.xml
new file mode 100644
index 0000000..6494bee5
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_send.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:pathData="M4.02,42L46,24 4.02,6 4,20l30,4 -30,4z"
+ android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/drawable/input_extract_action_bg_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
new file mode 100644
index 0000000..9c6a6c3
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/input_extract_action_bg_pressed_material_dark"
+ android:state_pressed="true"/>
+ <item android:drawable="@drawable/input_extract_action_bg_normal_material_dark"/>
+</selector>
diff --git a/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
new file mode 100644
index 0000000..8449978
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+ <solid android:color="@color/material_deep_teal_200"/>
+</shape>
diff --git a/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
new file mode 100644
index 0000000..adade104
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+ <solid android:color="@color/material_deep_teal_100"/>
+</shape>
diff --git a/core/res/res/layout-watch/input_method_extract_view.xml b/core/res/res/layout-watch/input_method_extract_view.xml
new file mode 100644
index 0000000..e3cd2ce
--- /dev/null
+++ b/core/res/res/layout-watch/input_method_extract_view.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<android.inputmethodservice.CompactExtractEditLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:baselineAligned="false">
+
+ <android.inputmethodservice.ExtractEditText
+ android:id="@id/inputExtractEditText"
+ android:layout_width="0dp"
+ android:layout_height="24dp"
+ android:background="@null"
+ android:singleLine="true"
+ android:inputType="text"
+ android:layout_weight="1"
+ android:fontFamily="sans-serif-condensed-light"
+ android:textColor="@color/primary_text_default_material_dark"
+ android:textColorHighlight="@color/accent_material_dark"
+ android:textSize="18dp"
+ android:cursorVisible="false"
+ android:gravity="bottom|right"
+ />
+
+ <FrameLayout
+ android:id="@id/inputExtractAccessories"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:visibility="visible">
+ <ImageButton
+ android:id="@id/inputExtractAction"
+ android:layout_width="@dimen/input_extract_action_button_width"
+ android:layout_height="@dimen/input_extract_action_button_width"
+ android:background="@drawable/input_extract_action_bg_material_dark"
+ android:padding="4dp"
+ android:scaleType="centerInside" />
+ </FrameLayout>
+</android.inputmethodservice.CompactExtractEditLayout>
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 4b8640c..5850e50 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -72,7 +72,9 @@
<TextView android:id="@+id/empty"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/white"
+ android:elevation="8dp"
android:layout_alwaysShow="true"
android:text="@string/noApplications"
android:padding="32dp"
diff --git a/core/res/res/values-round-watch/dimens.xml b/core/res/res/values-round-watch/dimens.xml
new file mode 100644
index 0000000..a12f499
--- /dev/null
+++ b/core/res/res/values-round-watch/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- each of these are relative to the display size -->
+ <item name="input_extract_layout_height" type="fraction">25.2%</item>
+ <item name="input_extract_layout_padding_left" type="fraction">7.5%</item>
+ <item name="input_extract_layout_padding_left_no_action" type="fraction">@fraction/input_extract_layout_padding_right</item>
+ <item name="input_extract_layout_padding_right" type="fraction">21.4%</item>
+ <item name="input_extract_text_margin_bottom" type="fraction">5.5%</item>
+ <item name="input_extract_action_margin_bottom" type="fraction">2.1%</item>
+ <item name="input_extract_action_button_width" type="dimen">32dp</item>
+ <item name="input_extract_action_button_height" type="dimen">32dp</item>
+</resources>
diff --git a/core/res/res/values-w170dp-notround-watch/dimens.xml b/core/res/res/values-w170dp-notround-watch/dimens.xml
new file mode 100644
index 0000000..c91cbc1
--- /dev/null
+++ b/core/res/res/values-w170dp-notround-watch/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- each of these are relative to the display size -->
+ <item name="input_extract_layout_padding_right" type="fraction">7.5%</item>
+</resources>
diff --git a/core/res/res/values-watch/dimens.xml b/core/res/res/values-watch/dimens.xml
new file mode 100644
index 0000000..f103aa9
--- /dev/null
+++ b/core/res/res/values-watch/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- each of these are relative to the display size -->
+ <item name="input_extract_layout_height" type="fraction">17.5%</item>
+ <item name="input_extract_layout_padding_left" type="fraction">3.6%</item>
+ <item name="input_extract_layout_padding_left_no_action" type="fraction">@fraction/input_extract_layout_padding_right</item>
+ <item name="input_extract_layout_padding_right" type="fraction">2.5%</item>
+ <item name="input_extract_text_margin_bottom" type="fraction">0%</item>
+ <item name="input_extract_action_margin_bottom" type="fraction">0%</item>
+ <item name="input_extract_action_button_width" type="dimen">24dp</item>
+ <item name="input_extract_action_button_height" type="dimen">24dp</item>
+</resources>
diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml
index 756a94b..6d6065f 100644
--- a/core/res/res/values-watch/themes.xml
+++ b/core/res/res/values-watch/themes.xml
@@ -18,6 +18,7 @@
<style name="Theme.Dialog.AppError" parent="Theme.Micro.Dialog.AppError" />
<style name="Theme.Holo.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
<style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+ <style name="Theme.InputMethod" parent="Theme.Micro.InputMethod" />
<style name="Theme.Material.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
<style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
</resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 61753b1..66509fb 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -19,14 +19,16 @@
<style name="Theme.DeviceDefault.Dialog" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+ <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Micro.InputMethod" />
+ <style name="Theme.DeviceDefault.Panel" parent="Theme.Micro.Panel" />
<style name="Theme.DeviceDefault.Light" parent="Theme.Micro.Light" />
<style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Micro.Light" />
<style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Micro.Light" />
<style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Micro.Dialog" />
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+ <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Micro.Light.Panel" />
<style name="Theme.DeviceDefault.Settings" parent="Theme.Micro" />
<style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Micro" />
-
</resources>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 7399fa9..c8ca116 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -75,7 +75,9 @@
<color name="material_grey_100">#fff5f5f5</color>
<color name="material_grey_50">#fffafafa</color>
+ <color name="material_deep_teal_100">#ffb2dfdb</color>
<color name="material_deep_teal_200">#ff80cbc4</color>
+ <color name="material_deep_teal_300">#ff4db6ac</color>
<color name="material_deep_teal_500">#ff009688</color>
<color name="material_blue_grey_800">#ff37474f</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cb551e8..aada05d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -520,12 +520,6 @@
<!-- Wifi driver supports batched scan -->
<bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
- <!-- Wifi HAL supported PNO -->
- <bool translatable="false" name="config_wifi_hal_pno_enable">false</bool>
-
- <!-- Wifi SSID white list (can't be enabled if config_wifi_hal_pno_enable is not) -->
- <bool translatable="false" name="config_wifi_ssid_white_list_enable">true</bool>
-
<!-- Idle Receive current for wifi radio. 0 by default-->
<integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 29c6951..7eaff7b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -390,9 +390,7 @@
<java-symbol type="integer" name="config_wifi_supplicant_scan_interval" />
<java-symbol type="integer" name="config_wifi_no_network_periodic_scan_interval" />
<java-symbol type="integer" name="config_wifi_scan_interval_p2p_connected" />
- <java-symbol type="bool" name="config_wifi_hal_pno_enable" />
<java-symbol type="integer" name="config_windowOutsetBottom" />
- <java-symbol type="bool" name="config_wifi_ssid_white_list_enable" />
<java-symbol type="integer" name="db_connection_pool_size" />
<java-symbol type="integer" name="db_journal_size_limit" />
<java-symbol type="integer" name="db_wal_autocheckpoint" />
@@ -2532,4 +2530,23 @@
<java-symbol type="id" name="titleDividerNoCustom" />
<java-symbol type="bool" name="config_sustainedPerformanceModeSupported" />
+
+ <!-- Wearable input extract edit view -->
+ <java-symbol type="drawable" name="ic_input_extract_action_go" />
+ <java-symbol type="drawable" name="ic_input_extract_action_search" />
+ <java-symbol type="drawable" name="ic_input_extract_action_send" />
+ <java-symbol type="drawable" name="ic_input_extract_action_next" />
+ <java-symbol type="drawable" name="ic_input_extract_action_done" />
+ <java-symbol type="drawable" name="ic_input_extract_action_previous" />
+ <java-symbol type="drawable" name="ic_input_extract_action_return" />
+
+ <java-symbol type="fraction" name="input_extract_layout_height" />
+ <java-symbol type="fraction" name="input_extract_layout_padding_left" />
+ <java-symbol type="fraction" name="input_extract_layout_padding_left_no_action" />
+ <java-symbol type="fraction" name="input_extract_layout_padding_right" />
+ <java-symbol type="fraction" name="input_extract_text_margin_bottom" />
+ <java-symbol type="fraction" name="input_extract_action_margin_bottom" />
+
+ <java-symbol type="dimen" name="input_extract_action_button_width" />
+ <java-symbol type="dimen" name="input_extract_action_button_height" />
</resources>
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index 478d66c..25a6e00 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -83,4 +83,18 @@
<item name="fontFamily">sans-serif-condensed-light</item>
<item name="textColor">@color/micro_text_light</item>
</style>
+
+ <style name="Theme.Micro.Panel" parent="Theme.Material.Panel" />
+ <style name="Theme.Micro.Light.Panel" parent="Theme.Material.Light.Panel" />
+
+ <!-- Default theme for material style input methods, which is used by the
+ {@link android.inputmethodservice.InputMethodService} class.
+ This inherits from Theme.Panel, but sets up IME appropriate animations
+ and a few custom attributes. -->
+ <style name="Theme.Micro.InputMethod" parent="Theme.Micro.Panel">
+ <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
+ <item name="imeFullscreenBackground">#1e282c</item>
+ <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
+ <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
+ </style>
</resources>
diff --git a/docs/html/guide/topics/manifest/application-element.jd b/docs/html/guide/topics/manifest/application-element.jd
index 5600b5c..887b4ea 100644
--- a/docs/html/guide/topics/manifest/application-element.jd
+++ b/docs/html/guide/topics/manifest/application-element.jd
@@ -472,6 +472,8 @@
{@link android.os.StrictMode.VmPolicy.Builder#detectCleartextNetwork() StrictMode.VmPolicy.Builder.detectCleartextNetwork()}.
<p>This attribute was added in API level 23.</p>
+
+<p>This flag is ignored on Android N and above if an Android Network Security Config is present.</p>
</dd>
<dt><a name="vmSafeMode"></a>{@code android:vmSafeMode}</dt>
diff --git a/graphics/java/android/graphics/PixelCopy.java b/graphics/java/android/graphics/PixelCopy.java
new file mode 100644
index 0000000..c599126
--- /dev/null
+++ b/graphics/java/android/graphics/PixelCopy.java
@@ -0,0 +1,104 @@
+package android.graphics;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.ThreadedRenderer;
+
+/**
+ * Provides a mechanisms to issue pixel copy requests to allow for copy
+ * operations from {@link Surface} to {@link Bitmap}
+ *
+ * @hide
+ */
+public final class PixelCopy {
+ /**
+ * Contains the result of a pixel copy request
+ */
+ public static final class Response {
+ /**
+ * Indicates whether or not the copy request completed successfully.
+ * If this is true, then {@link #bitmap} contains the result of the copy.
+ * If this is false, {@link #bitmap} is unmodified from the originally
+ * passed destination.
+ *
+ * For example a request might fail if the source is protected content
+ * so copies are not allowed. Similarly if the source has nothing to
+ * copy from, because either no frames have been produced yet or because
+ * it has already been destroyed, then this will be false.
+ */
+ public boolean success;
+
+ /**
+ * The output bitmap. This is always the same object that was passed
+ * to request() as the 'dest' bitmap. If {@link #success} is true this
+ * contains a copy of the pixels of the source object. If {@link #success}
+ * is false then this is unmodified.
+ */
+ @NonNull
+ public Bitmap bitmap;
+ }
+
+ public interface OnPixelCopyFinished {
+ /**
+ * Callback for when a pixel copy request has completed. This will be called
+ * regardless of whether the copy succeeded or failed.
+ *
+ * @param response Contains the result of the copy request which includes
+ * whether or not the copy was successful.
+ */
+ void onPixelCopyFinished(PixelCopy.Response response);
+ }
+
+ /**
+ * Requests for the display content of a {@link SurfaceView} to be copied
+ * into a provided {@link Bitmap}.
+ *
+ * The contents of the source will be scaled to fit exactly inside the bitmap.
+ * The pixel format of the source buffer will be converted, as part of the copy,
+ * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
+ * in the SurfaceView's Surface will be used as the source of the copy.
+ *
+ * @param source The source from which to copy
+ * @param dest The destination of the copy. The source will be scaled to
+ * match the width, height, and format of this bitmap.
+ * @param listener Callback for when the pixel copy request completes
+ * @param listenerThread The callback will be invoked on this Handler when
+ * the copy is finished.
+ */
+ public static void request(@NonNull SurfaceView source, @NonNull Bitmap dest,
+ @NonNull OnPixelCopyFinished listener, @NonNull Handler listenerThread) {
+ request(source.getHolder().getSurface(), dest, listener, listenerThread);
+ }
+
+ /**
+ * Requests a copy of the pixels from a {@link Surface} to be copied into
+ * a provided {@link Bitmap}.
+ *
+ * The contents of the source will be scaled to fit exactly inside the bitmap.
+ * The pixel format of the source buffer will be converted, as part of the copy,
+ * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
+ * in the Surface will be used as the source of the copy.
+ *
+ * @param source The source from which to copy
+ * @param dest The destination of the copy. The source will be scaled to
+ * match the width, height, and format of this bitmap.
+ * @param listener Callback for when the pixel copy request completes
+ * @param listenerThread The callback will be invoked on this Handler when
+ * the copy is finished.
+ */
+ public static void request(@NonNull Surface source, @NonNull Bitmap dest,
+ @NonNull OnPixelCopyFinished listener, @NonNull Handler listenerThread) {
+ // TODO: Make this actually async and fast and cool and stuff
+ final PixelCopy.Response response = new PixelCopy.Response();
+ response.success = ThreadedRenderer.copySurfaceInto(source, dest);
+ response.bitmap = dest;
+ listenerThread.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onPixelCopyFinished(response);
+ }
+ });
+ }
+}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 0606b0b..717a1e6 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -84,6 +84,7 @@
Properties.cpp \
PropertyValuesHolder.cpp \
PropertyValuesAnimatorSet.cpp \
+ Readback.cpp \
RenderBufferCache.cpp \
RenderNode.cpp \
RenderProperties.cpp \
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
new file mode 100644
index 0000000..d7df77c
--- /dev/null
+++ b/libs/hwui/Readback.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Readback.h"
+
+#include "Caches.h"
+#include "Image.h"
+#include "GlopBuilder.h"
+#include "renderstate/RenderState.h"
+#include "renderthread/EglManager.h"
+#include "utils/GLUtils.h"
+
+#include <GLES2/gl2.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+bool Readback::copySurfaceInto(renderthread::RenderThread& renderThread,
+ Surface& surface, SkBitmap* bitmap) {
+ // TODO: Clean this up and unify it with LayerRenderer::copyLayer,
+ // of which most of this is copied from.
+ renderThread.eglManager().initialize();
+
+ Caches& caches = Caches::getInstance();
+ RenderState& renderState = renderThread.renderState();
+ int destWidth = bitmap->width();
+ int destHeight = bitmap->height();
+ if (destWidth > caches.maxTextureSize
+ || destHeight > caches.maxTextureSize) {
+ ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d",
+ destWidth, destHeight, caches.maxTextureSize);
+ return false;
+ }
+ GLuint fbo = renderState.createFramebuffer();
+ if (!fbo) {
+ ALOGW("Could not obtain an FBO");
+ return false;
+ }
+
+ SkAutoLockPixels alp(*bitmap);
+
+ GLuint texture;
+
+ GLenum format;
+ GLenum type;
+
+ switch (bitmap->colorType()) {
+ case kAlpha_8_SkColorType:
+ format = GL_ALPHA;
+ type = GL_UNSIGNED_BYTE;
+ break;
+ case kRGB_565_SkColorType:
+ format = GL_RGB;
+ type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case kARGB_4444_SkColorType:
+ format = GL_RGBA;
+ type = GL_UNSIGNED_SHORT_4_4_4_4;
+ break;
+ case kN32_SkColorType:
+ default:
+ format = GL_RGBA;
+ type = GL_UNSIGNED_BYTE;
+ break;
+ }
+
+ renderState.bindFramebuffer(fbo);
+
+ // TODO: Use layerPool or something to get this maybe? But since we
+ // need explicit format control we can't currently.
+
+ // Setup the rendertarget
+ glGenTextures(1, &texture);
+ caches.textureState().activateTexture(0);
+ caches.textureState().bindTexture(texture);
+ glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_2D, 0, format, destWidth, destHeight,
+ 0, format, type, nullptr);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, texture, 0);
+
+ // Setup the source
+ sp<GraphicBuffer> sourceBuffer;
+ sp<Fence> sourceFence;
+ // FIXME: Waiting on an API from libgui for this
+ // surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence);
+ if (!sourceBuffer.get()) {
+ ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
+ return false;
+ }
+ int err = sourceFence->wait(500 /* ms */);
+ if (err != NO_ERROR) {
+ ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+ return false;
+ }
+ Image sourceImage(sourceBuffer);
+ if (!sourceImage.getTexture()) {
+ ALOGW("Failed to make an EGLImage from the GraphicBuffer");
+ return false;
+ }
+ Texture sourceTexture(caches);
+ sourceTexture.wrap(sourceImage.getTexture(),
+ sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */);
+
+ {
+ // Draw & readback
+ renderState.setViewport(destWidth, destHeight);
+ renderState.scissor().setEnabled(false);
+ renderState.blend().syncEnabled();
+ renderState.stencil().disable();
+
+ Rect destRect(destWidth, destHeight);
+ Glop glop;
+ GlopBuilder(renderState, caches, &glop)
+ .setRoundRectClipState(nullptr)
+ .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
+ .setFillLayer(sourceTexture, nullptr, 1.0f, SkXfermode::kSrc_Mode,
+ Blend::ModeOrderSwap::NoSwap)
+ .setTransform(Matrix4::identity(), TransformFlags::None)
+ .setModelViewMapUnitToRect(destRect)
+ .build();
+ Matrix4 ortho;
+ ortho.loadOrtho(destWidth, destHeight);
+ renderState.render(glop, ortho);
+
+ glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
+ type, bitmap->getPixels());
+ }
+
+ // Cleanup
+ caches.textureState().deleteTexture(texture);
+ renderState.deleteFramebuffer(fbo);
+
+ GL_CHECKPOINT(MODERATE);
+
+ return true;
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
new file mode 100644
index 0000000..ea03c82
--- /dev/null
+++ b/libs/hwui/Readback.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "renderthread/RenderThread.h"
+
+#include <SkBitmap.h>
+#include <gui/Surface.h>
+
+namespace android {
+namespace uirenderer {
+
+class Readback {
+public:
+ static bool copySurfaceInto(renderthread::RenderThread& renderThread,
+ Surface& surface, SkBitmap* bitmap);
+};
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 1116383..096093c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -19,6 +19,7 @@
#include "DeferredLayerUpdater.h"
#include "DisplayList.h"
#include "LayerRenderer.h"
+#include "Readback.h"
#include "Rect.h"
#include "renderthread/CanvasContext.h"
#include "renderthread/RenderTask.h"
@@ -604,6 +605,20 @@
post(task);
}
+CREATE_BRIDGE3(copySurfaceInto, RenderThread* thread,
+ Surface* surface, SkBitmap* bitmap) {
+ return (void*) Readback::copySurfaceInto(*args->thread,
+ *args->surface, args->bitmap);
+}
+
+bool RenderProxy::copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap) {
+ SETUP_TASK(copySurfaceInto);
+ args->bitmap = bitmap;
+ args->surface = surface.get();
+ args->thread = &RenderThread::getInstance();
+ return (bool) staticPostAndWait(task);
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index ecc296b..98aace0 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -126,6 +126,8 @@
ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
ANDROID_API long getDroppedFrameReportCount();
+ ANDROID_API static bool copySurfaceInto(sp<Surface>& surface, SkBitmap* bitmap);
+
private:
RenderThread& mRenderThread;
CanvasContext* mContext;
diff --git a/packages/DocumentsUI/res/color/item_root_icon.xml b/packages/DocumentsUI/res/color/item_root_icon.xml
index 0aa2c13..e1d7e61 100644
--- a/packages/DocumentsUI/res/color/item_root_icon.xml
+++ b/packages/DocumentsUI/res/color/item_root_icon.xml
@@ -15,5 +15,10 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="@*android:color/secondary_text_material_light" />
+ <item
+ android:state_activated="false"
+ android:color="@*android:color/secondary_text_material_light" />
+ <item
+ android:state_activated="true"
+ android:color="@color/root_activated_color" />
</selector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index d353f31..8881034 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -534,6 +534,7 @@
Comparator<AppEntry> mRebuildComparator;
ArrayList<AppEntry> mRebuildResult;
ArrayList<AppEntry> mLastAppList;
+ boolean mRebuildForeground;
Session(Callbacks callbacks) {
mCallbacks = callbacks;
@@ -572,6 +573,11 @@
// Creates a new list of app entries with the given filter and comparator.
public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
+ return rebuild(filter, comparator, true);
+ }
+
+ public ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator,
+ boolean foreground) {
synchronized (mRebuildSync) {
synchronized (mEntriesMap) {
mRebuildingSessions.add(this);
@@ -579,6 +585,7 @@
mRebuildAsync = false;
mRebuildFilter = filter;
mRebuildComparator = comparator;
+ mRebuildForeground = foreground;
mRebuildResult = null;
if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
Message msg = mBackgroundHandler.obtainMessage(
@@ -620,10 +627,12 @@
mRebuildRequested = false;
mRebuildFilter = null;
mRebuildComparator = null;
+ if (mRebuildForeground) {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+ mRebuildForeground = false;
+ }
}
- Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
-
if (filter != null) {
filter.init();
}
@@ -640,7 +649,10 @@
if (filter == null || filter.filterApp(entry)) {
synchronized (mEntriesMap) {
if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
- entry.ensureLabel(mContext);
+ if (comparator != null) {
+ // Only need the label if we are going to be sorting.
+ entry.ensureLabel(mContext);
+ }
if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
filteredApps.add(entry);
if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
@@ -648,7 +660,9 @@
}
}
- Collections.sort(filteredApps, comparator);
+ if (comparator != null) {
+ Collections.sort(filteredApps, comparator);
+ }
synchronized (mRebuildSync) {
if (!mRebuildRequested) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index ff70190..bcbc6ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -216,6 +216,8 @@
if (sDashboardCategories == null) {
sTileCache = new HashMap<>();
sConfigTracker = new InterestingConfigChanges();
+ // Apply initial current config.
+ sConfigTracker.applyNewConfig(getResources());
sDashboardCategories = TileUtils.getCategories(this, sTileCache);
}
return sDashboardCategories;
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 7ca7614..796dff5 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -220,6 +220,7 @@
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.v(TAG, "onStartCommand(): " + dumpIntent(intent));
if (intent != null) {
// Handle it in a separate thread.
final Message msg = mMainHandler.obtainMessage();
@@ -297,6 +298,7 @@
return;
}
final Parcelable parcel = ((Intent) msg.obj).getParcelableExtra(EXTRA_ORIGINAL_INTENT);
+ Log.v(TAG, "handleMessage(): " + dumpIntent((Intent) parcel));
final Intent intent;
if (parcel instanceof Intent) {
// The real intent was passed to BugreportReceiver, which delegated to the service.
@@ -707,7 +709,8 @@
for (int i = 0; i < mProcesses.size(); i++) {
final BugreportInfo info = mProcesses.valueAt(i);
if (info.finished) {
- Log.d(TAG, "Not updating progress because share notification was already sent");
+ Log.d(TAG, "Not updating progress for " + info.id + " while taking screenshot"
+ + " because share notification was already sent");
continue;
}
updateProgress(info);
@@ -846,7 +849,15 @@
private static Intent buildSendIntent(Context context, BugreportInfo info) {
// Files are kept on private storage, so turn into Uris that we can
// grant temporary permissions for.
- final Uri bugreportUri = getUri(context, info.bugreportFile);
+ final Uri bugreportUri;
+ try {
+ bugreportUri = getUri(context, info.bugreportFile);
+ } catch (IllegalArgumentException e) {
+ // Should not happen on production, but happens when a Shell is sideloaded and
+ // FileProvider cannot find a configured root for it.
+ Log.wtf(TAG, "Could not get URI for " + info.bugreportFile, e);
+ return null;
+ }
final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
final String mimeType = "application/vnd.android.bugreport";
@@ -907,6 +918,12 @@
addDetailsToZipFile(mContext, info);
final Intent sendIntent = buildSendIntent(mContext, info);
+ if (sendIntent == null) {
+ Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built");
+ stopProgress(id);
+ return;
+ }
+
final Intent notifIntent;
// Send through warning dialog by default
@@ -1165,6 +1182,52 @@
}
}
+ /**
+ * Dumps an intent, extracting the relevant extras.
+ */
+ static String dumpIntent(Intent intent) {
+ if (intent == null) {
+ return "NO INTENT";
+ }
+ String action = intent.getAction();
+ if (action == null) {
+ // Happens when BugreportReceiver calls startService...
+ action = "no action";
+ }
+ final StringBuilder buffer = new StringBuilder(action).append(" extras: ");
+ addExtra(buffer, intent, EXTRA_ID);
+ addExtra(buffer, intent, EXTRA_PID);
+ addExtra(buffer, intent, EXTRA_MAX);
+ addExtra(buffer, intent, EXTRA_NAME);
+ addExtra(buffer, intent, EXTRA_DESCRIPTION);
+ addExtra(buffer, intent, EXTRA_BUGREPORT);
+ addExtra(buffer, intent, EXTRA_SCREENSHOT);
+ addExtra(buffer, intent, EXTRA_INFO);
+
+ if (intent.hasExtra(EXTRA_ORIGINAL_INTENT)) {
+ buffer.append(SHORT_EXTRA_ORIGINAL_INTENT).append(": ");
+ final Intent originalIntent = intent.getParcelableExtra(EXTRA_ORIGINAL_INTENT);
+ buffer.append(dumpIntent(originalIntent));
+ } else {
+ buffer.append("no ").append(SHORT_EXTRA_ORIGINAL_INTENT);
+ }
+
+ return buffer.toString();
+ }
+
+ private static final String SHORT_EXTRA_ORIGINAL_INTENT =
+ EXTRA_ORIGINAL_INTENT.substring(EXTRA_ORIGINAL_INTENT.lastIndexOf('.') + 1);
+
+ private static void addExtra(StringBuilder buffer, Intent intent, String name) {
+ final String shortName = name.substring(name.lastIndexOf('.') + 1);
+ if (intent.hasExtra(name)) {
+ buffer.append(shortName).append('=').append(intent.getExtra(name));
+ } else {
+ buffer.append("no ").append(shortName);
+ }
+ buffer.append(", ");
+ }
+
private static boolean setSystemProperty(String key, String value) {
try {
if (DEBUG) Log.v(TAG, "Setting system property " + key + " to " + value);
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index cbd17bf..f6e558f 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -20,6 +20,7 @@
import static com.android.shell.BugreportProgressService.EXTRA_ORIGINAL_INTENT;
import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
import static com.android.shell.BugreportProgressService.getFileExtra;
+import static com.android.shell.BugreportProgressService.dumpIntent;
import java.io.File;
@@ -51,7 +52,7 @@
@Override
public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "onReceive: " + intent);
+ Log.d(TAG, "onReceive(): " + dumpIntent(intent));
// Clean up older bugreports in background
cleanupOldFiles(this, intent, INTENT_BUGREPORT_FINISHED, MIN_KEEP_COUNT, MIN_KEEP_AGE);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f5854f5..c248adf 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -159,6 +159,9 @@
<!-- DND access -->
<uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
+ <!-- It's like, reality, but, you know, virtual -->
+ <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
@@ -235,6 +238,19 @@
android:value="com.android.settings.category.system" />
</activity>
+ <activity-alias android:name=".DemoMode"
+ android:targetActivity=".tuner.TunerActivity"
+ android:icon="@drawable/tuner"
+ android:theme="@style/TunerSettings"
+ android:label="@string/demo_mode"
+ android:process=":tuner"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.settings.action.DEMO_MODE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity-alias>
+
<!-- Service used by secondary users to register themselves with the system user. -->
<service android:name=".recents.RecentsSystemUserService"
android:exported="false"
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 087f61e..076b5bc 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -37,7 +37,7 @@
import com.android.systemui.statusbar.policy.BatteryController;
-public class BatteryMeterDrawable extends Drawable implements DemoMode,
+public class BatteryMeterDrawable extends Drawable implements
BatteryController.BatteryStateChangeCallback {
private static final float ASPECT_RATIO = 9.5f / 14.5f;
@@ -184,14 +184,12 @@
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_PERCENT_SETTING), false, mSettingObserver);
updateShowPercent();
- if (mDemoMode) return;
mBatteryController.addStateChangedCallback(this);
}
public void stopListening() {
mListening = false;
mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
- if (mDemoMode) return;
mBatteryController.removeStateChangedCallback(this);
}
@@ -507,35 +505,6 @@
return 0;
}
- private boolean mDemoMode;
-
- @Override
- public void dispatchDemoCommand(String command, Bundle args) {
- if (!mDemoMode && command.equals(COMMAND_ENTER)) {
- mBatteryController.removeStateChangedCallback(this);
- mDemoMode = true;
- if (mListening) {
- mBatteryController.removeStateChangedCallback(this);
- }
- } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
- mDemoMode = false;
- postInvalidate();
- if (mListening) {
- mBatteryController.addStateChangedCallback(this);
- }
- } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
- String level = args.getString("level");
- String plugged = args.getString("plugged");
- if (level != null) {
- mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
- }
- if (plugged != null) {
- mPluggedIn = Boolean.parseBoolean(plugged);
- }
- postInvalidate();
- }
- }
-
private final class SettingObserver extends ContentObserver {
public SettingObserver() {
super(new Handler());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 44f220b..4ecda54 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -25,6 +25,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.os.Trace;
import android.util.ArraySet;
import android.util.IntProperty;
import android.util.Property;
@@ -261,6 +262,14 @@
}
/**
+ * Adds a trace event for debugging.
+ */
+ public static void addTraceEvent(String event) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, event);
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+
+ /**
* Returns a lightweight dump of a rect.
*/
public static String dumpRect(Rect r) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index b75a91e..e4da8b3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -23,13 +23,13 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.util.ArraySet;
+import android.util.MutableFloat;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.ViewDebug;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
@@ -628,22 +628,24 @@
/**
* Updates this stack when a scroll happens.
+ *
*/
- public void updateFocusStateOnScroll(float stackScroll, float deltaScroll) {
- if (deltaScroll == 0f) {
- return;
+ public float updateFocusStateOnScroll(float lastTargetStackScroll, float targetStackScroll,
+ float lastStackScroll) {
+ if (targetStackScroll == lastStackScroll) {
+ return targetStackScroll;
}
+ float deltaScroll = targetStackScroll - lastStackScroll;
+ float deltaTargetScroll = targetStackScroll - lastTargetStackScroll;
+ float newScroll = targetStackScroll;
+ mUnfocusedRange.offset(targetStackScroll);
for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
int taskId = mTaskIndexOverrideMap.keyAt(i);
float x = mTaskIndexMap.get(taskId);
float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
float newOverrideX = overrideX + deltaScroll;
- mUnfocusedRange.offset(stackScroll);
- boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f ||
- mUnfocusedRange.getNormalizedX(newOverrideX) > 1f;
- if (outOfBounds || (overrideX >= x && x >= newOverrideX) ||
- (overrideX <= x && x <= newOverrideX)) {
+ if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
// Remove the override once we reach the original task index
mTaskIndexOverrideMap.removeAt(i);
} else if ((overrideX >= x && deltaScroll <= 0f) ||
@@ -652,11 +654,23 @@
mTaskIndexOverrideMap.put(taskId, newOverrideX);
} else {
// Scrolling override x away from x, we should still move the scroll towards x
- float deltaX = overrideX - x;
- newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - Math.abs(deltaScroll));
- mTaskIndexOverrideMap.put(taskId, x + newOverrideX);
+ newScroll = lastStackScroll;
+ newOverrideX = overrideX - deltaTargetScroll;
+ if (isInvalidOverrideX(x, overrideX, newOverrideX)) {
+ mTaskIndexOverrideMap.removeAt(i);
+ } else{
+ mTaskIndexOverrideMap.put(taskId, newOverrideX);
+ }
}
}
+ return newScroll;
+ }
+
+ private boolean isInvalidOverrideX(float x, float overrideX, float newOverrideX) {
+ boolean outOfBounds = mUnfocusedRange.getNormalizedX(newOverrideX) < 0f ||
+ mUnfocusedRange.getNormalizedX(newOverrideX) > 1f;
+ return outOfBounds || (overrideX >= x && x >= newOverrideX) ||
+ (overrideX <= x && x <= newOverrideX);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 13c8403..0fc45ed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1618,7 +1618,6 @@
if (animation != null) {
relayoutTaskViewsOnNextFrame(animation);
}
- mLayoutAlgorithm.updateFocusStateOnScroll(curScroll, curScroll - prevScroll);
if (mEnterAnimationComplete) {
if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 19b3c94..1fa73c6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.util.FloatProperty;
import android.util.Log;
+import android.util.MutableFloat;
import android.util.Property;
import android.view.ViewDebug;
import android.widget.OverScroller;
@@ -66,6 +67,8 @@
@ViewDebug.ExportedProperty(category="recents")
float mStackScrollP;
+ @ViewDebug.ExportedProperty(category="recents")
+ float mLastDeltaP = 0f;
float mFlingDownScrollP;
int mFlingDownY;
@@ -84,6 +87,11 @@
/** Resets the task scroller. */
void reset() {
mStackScrollP = 0f;
+ mLastDeltaP = 0f;
+ }
+
+ void resetDeltaScroll() {
+ mLastDeltaP = 0f;
}
/** Gets the current stack scroll */
@@ -99,14 +107,27 @@
}
/**
+ * Sets the current stack scroll immediately, and returns the difference between the target
+ * scroll and the actual scroll after accounting for the effect on the focus state.
+ */
+ public float setDeltaStackScroll(float downP, float deltaP) {
+ float targetScroll = downP + deltaP;
+ float newScroll = mLayoutAlgorithm.updateFocusStateOnScroll(downP + mLastDeltaP, targetScroll,
+ mStackScrollP);
+ setStackScroll(newScroll, AnimationProps.IMMEDIATE);
+ mLastDeltaP = deltaP;
+ return newScroll - targetScroll;
+ }
+
+ /**
* Sets the current stack scroll, but indicates to the callback the preferred animation to
* update to this new scroll.
*/
- public void setStackScroll(float s, AnimationProps animation) {
- float prevStackScroll = mStackScrollP;
- mStackScrollP = s;
+ public void setStackScroll(float newScroll, AnimationProps animation) {
+ float prevScroll = mStackScrollP;
+ mStackScrollP = newScroll;
if (mCb != null) {
- mCb.onStackScrollChanged(prevStackScroll, mStackScrollP, animation);
+ mCb.onStackScrollChanged(prevScroll, mStackScrollP, animation);
}
}
@@ -115,9 +136,9 @@
* @return whether the stack progress changed.
*/
public boolean setStackScrollToInitialState() {
- float prevStackScrollP = mStackScrollP;
+ float prevScroll = mStackScrollP;
setStackScroll(mLayoutAlgorithm.mInitialScrollP);
- return Float.compare(prevStackScrollP, mStackScrollP) != 0;
+ return Float.compare(prevScroll, mStackScrollP) != 0;
}
/**
@@ -227,10 +248,9 @@
boolean computeScroll() {
if (mScroller.computeScrollOffset()) {
float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY());
- float scroll = mFlingDownScrollP + deltaP;
- setStackScroll(scroll);
+ mFlingDownScrollP += setDeltaStackScroll(mFlingDownScrollP, deltaP);
if (DEBUG) {
- Log.d(TAG, "computeScroll: " + scroll);
+ Log.d(TAG, "computeScroll: " + (mFlingDownScrollP + deltaP));
}
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index ee0de1a..3cdb1fb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -200,6 +200,7 @@
// Stop the current scroll if it is still flinging
mScroller.stopScroller();
mScroller.stopBoundScrollAnimation();
+ mScroller.resetDeltaScroll();
Utilities.cancelAnimationWithoutCallbacks(mScrollFlingAnimator);
// Finish any existing task animations from the delete
@@ -223,6 +224,7 @@
mDownY = (int) ev.getY(index);
mLastY = mDownY;
mDownScrollP = mScroller.getStackScroll();
+ mScroller.resetDeltaScroll();
mVelocityTracker.addMovement(ev);
break;
}
@@ -256,20 +258,21 @@
// If we just move linearly on the screen, then that would map to 1/arclength
// of the curve, so just move the scroll proportional to that
float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
- float curScrollP = mDownScrollP + deltaP;
// Modulate the overscroll to prevent users from pulling the stack too far
float minScrollP = layoutAlgorithm.mMinScrollP;
float maxScrollP = layoutAlgorithm.mMaxScrollP;
+ float curScrollP = mDownScrollP + deltaP;
if (curScrollP < minScrollP || curScrollP > maxScrollP) {
float clampedScrollP = Utilities.clamp(curScrollP, minScrollP, maxScrollP);
float overscrollP = (curScrollP - clampedScrollP);
float overscrollX = Math.abs(overscrollP) / MAX_OVERSCROLL;
- curScrollP = clampedScrollP + (Math.signum(overscrollP) *
- (OVERSCROLL_INTERP.getInterpolation(overscrollX) * MAX_OVERSCROLL));
+ float interpX = OVERSCROLL_INTERP.getInterpolation(overscrollX);
+ curScrollP = clampedScrollP + Math.signum(overscrollP) *
+ (interpX * MAX_OVERSCROLL);
}
-
- mScroller.setStackScroll(curScrollP);
+ mDownScrollP += mScroller.setDeltaStackScroll(mDownScrollP,
+ curScrollP - mDownScrollP);
mStackViewScrolledEvent.updateY(y - mLastY);
EventBus.getDefault().send(mStackViewScrolledEvent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 98e0dd9..8461d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -133,6 +133,8 @@
private boolean mGrowRecents;
private ValueAnimator mCurrentAnimator;
private boolean mEntranceAnimationRunning;
+ private boolean mExitAnimationRunning;
+ private int mExitStartPosition;
private GestureDetector mGestureDetector;
private boolean mDockedStackMinimized;
@@ -445,6 +447,7 @@
mDockSide = WindowManager.DOCKED_INVALID;
mCurrentAnimator = null;
mEntranceAnimationRunning = false;
+ mExitAnimationRunning = false;
EventBus.getDefault().send(new StoppedDragingEvent());
}
});
@@ -654,6 +657,13 @@
mOtherTaskRect);
mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
mOtherTaskRect, null);
+ } else if (mExitAnimationRunning && taskPosition != TASK_POSITION_SAME) {
+ calculateBoundsForPosition(taskPosition,
+ mDockSide, mDockedTaskRect);
+ calculateBoundsForPosition(mExitStartPosition,
+ DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
+ mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
+ mOtherTaskRect, null);
} else if (taskPosition != TASK_POSITION_SAME) {
calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
mOtherRect);
@@ -895,7 +905,9 @@
: mSnapAlgorithm.getDismissStartTarget();
// Don't start immediately - give a little bit time to settle the drag resize change.
- stopDragging(getCurrentPosition(), target, 336 /* duration */, 100 /* startDelay */,
+ mExitAnimationRunning = true;
+ mExitStartPosition = getCurrentPosition();
+ stopDragging(mExitStartPosition, target, 336 /* duration */, 100 /* startDelay */,
Interpolators.TOUCH_RESPONSE);
// Vibrate after undocking
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 1b2393a..3ac7b26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -63,6 +63,8 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -262,11 +264,24 @@
protected AssistManager mAssistManager;
+ protected boolean mVrMode;
+
@Override // NotificationData.Environment
public boolean isDeviceProvisioned() {
return mDeviceProvisioned;
}
+ private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+ @Override
+ public void onVrStateChanged(boolean enabled) {
+ mVrMode = enabled;
+ }
+ };
+
+ public boolean isDeviceInVrMode() {
+ return mVrMode;
+ }
+
protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -776,6 +791,14 @@
mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
null, null);
updateCurrentProfilesCache();
+
+ IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
+ try {
+ vrManager.registerListener(mVrStateCallbacks);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+ }
+
}
protected void notifyUserAboutHiddenNotifications() {
@@ -2353,6 +2376,10 @@
}
protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
+ if (isDeviceInVrMode()) {
+ return false;
+ }
+
if (mNotificationData.shouldFilterOut(sbn)) {
if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 5b00523..491ffde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -348,6 +348,12 @@
setVisible(isShown());
}
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
+ }
+
private void setVisible(final boolean isVisible) {
if (isVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
index a5ebbba..9ed5022 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
@@ -232,8 +232,9 @@
return;
}
final boolean isRtl = mParent.isLayoutRtl();
- final float left = isRtl ? -(mParent.getWidth() - mHorizSpaceForGear) : 0;
- final float right = isRtl ? 0 : (mParent.getWidth() - mHorizSpaceForGear);
+ // TODO No need to cast to float here once b/28050538 is fixed.
+ final float left = (float) (isRtl ? -(mParent.getWidth() - mHorizSpaceForGear) : 0);
+ final float right = (float) (isRtl ? 0 : (mParent.getWidth() - mHorizSpaceForGear));
setTranslationX(onLeft ? left : right);
mOnLeft = onLeft;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index 03b51c6..244536d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -240,6 +240,11 @@
}
@Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ // TODO: Car demo mode.
+ }
+
+ @Override
public boolean isPowerSave() {
// Power save is not valid for the car, so always return false.
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 75430ff..f68b24b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1374,6 +1374,10 @@
}
private boolean shouldSuppressFullScreenIntent(String key) {
+ if (isDeviceInVrMode()) {
+ return true;
+ }
+
if (mPowerManager.isInteractive()) {
return mNotificationData.shouldSuppressScreenOn(key);
} else {
@@ -3591,11 +3595,10 @@
dispatchDemoCommandToView(command, args, R.id.clock);
}
if (modeChange || command.equals(COMMAND_BATTERY)) {
- dispatchDemoCommandToView(command, args, R.id.battery);
+ mBatteryController.dispatchDemoCommand(command, args);
}
if (modeChange || command.equals(COMMAND_STATUS)) {
mIconController.dispatchDemoCommand(command, args);
-
}
if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
mNetworkController.dispatchDemoCommand(command, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index ea64fd8..559436b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -16,10 +16,12 @@
package com.android.systemui.statusbar.policy;
+import com.android.systemui.DemoMode;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
-public interface BatteryController {
+public interface BatteryController extends DemoMode {
/**
* Prints the current state of the {@link BatteryController} to the given {@link PrintWriter}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 24207f3..5d734c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -21,9 +21,11 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
+import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.util.Log;
+import com.android.systemui.DemoMode;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -43,6 +45,7 @@
private final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
private final PowerManager mPowerManager;
private final Handler mHandler;
+ private final Context mContext;
protected int mLevel;
protected boolean mPluggedIn;
@@ -52,17 +55,21 @@
private boolean mTestmode = false;
public BatteryControllerImpl(Context context) {
+ mContext = context;
mHandler = new Handler();
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ registerReceiver();
+ updatePowerSave();
+ }
+
+ private void registerReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
filter.addAction(ACTION_LEVEL_TEST);
- context.registerReceiver(this, filter);
-
- updatePowerSave();
+ mContext.registerReceiver(this, filter);
}
@Override
@@ -176,4 +183,28 @@
mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
}
}
+
+ private boolean mDemoMode;
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ if (!mDemoMode && command.equals(COMMAND_ENTER)) {
+ mDemoMode = true;
+ mContext.unregisterReceiver(this);
+ } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
+ mDemoMode = false;
+ registerReceiver();
+ updatePowerSave();
+ } else if (mDemoMode && command.equals(COMMAND_BATTERY)) {
+ String level = args.getString("level");
+ String plugged = args.getString("plugged");
+ if (level != null) {
+ mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
+ }
+ if (plugged != null) {
+ mPluggedIn = Boolean.parseBoolean(plugged);
+ }
+ fireBatteryLevelChanged();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
index 6dd196b..c4c64e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java
@@ -33,24 +33,29 @@
}
private void handleRestrictBackgroundChanged(boolean isDataSaving) {
- final int N = mListeners.size();
- for (int i = 0; i < N; i++) {
- mListeners.get(i).onDataSaverChanged(isDataSaving);
+ synchronized (mListeners) {
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onDataSaverChanged(isDataSaving);
+ }
}
}
public void addListener(Listener listener) {
- mListeners.add(listener);
- if (mListeners.size() == 1) {
- mPolicyManager.registerListener(mPolicyListener);
+ synchronized (mListeners) {
+ mListeners.add(listener);
+ if (mListeners.size() == 1) {
+ mPolicyManager.registerListener(mPolicyListener);
+ }
}
listener.onDataSaverChanged(isDataSaverEnabled());
}
public void remListener(Listener listener) {
- mListeners.remove(listener);
- if (mListeners.size() == 0) {
- mPolicyManager.unregisterListener(mPolicyListener);
+ synchronized (mListeners) {
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ mPolicyManager.unregisterListener(mPolicyListener);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 7c5cdfb..8b52bf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3799,6 +3799,7 @@
} else {
getViewTreeObserver().removeOnPreDrawListener(mShadowUpdater);
}
+ mContinuousShadowUpdate = continuousShadowUpdate;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 748ee97..ae104cd 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -36,11 +36,15 @@
super.onCreate(savedInstanceState);
if (getFragmentManager().findFragmentByTag(TAG_TUNER) == null) {
+ boolean showDemoMode = getIntent().getAction().equals(
+ "com.android.settings.action.DEMO_MODE");
boolean showNightMode = getIntent().getBooleanExtra(
NightModeFragment.EXTRA_SHOW_NIGHT_MODE, false);
+ final PreferenceFragment fragment = showNightMode ? new NightModeFragment()
+ : showDemoMode ? new DemoModeFragment()
+ : new TunerFragment();
getFragmentManager().beginTransaction().replace(R.id.content_frame,
- showNightMode ? new NightModeFragment() : new TunerFragment(),
- TAG_TUNER).commit();
+ fragment, TAG_TUNER).commit();
}
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 22cc066..544e645 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -2966,16 +2966,6 @@
return;
}
setInputMethodLocked(nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
- if (mSubtypeSwitchedByShortCutToast != null) {
- mSubtypeSwitchedByShortCutToast.cancel();
- mSubtypeSwitchedByShortCutToast = null;
- }
- if ((mImeWindowVis & InputMethodService.IME_VISIBLE) != 0) {
- // IME window is shown. The user should be able to visually understand that the
- // subtype is changed in most of cases. To avoid UI overlap, we do not show a toast
- // in this case.
- return;
- }
final InputMethodInfo newInputMethodInfo = mMethodMap.get(mCurMethodId);
if (newInputMethodInfo == null) {
return;
@@ -2983,8 +2973,12 @@
final CharSequence toastText = InputMethodUtils.getImeAndSubtypeDisplayName(mContext,
newInputMethodInfo, mCurrentSubtype);
if (!TextUtils.isEmpty(toastText)) {
- mSubtypeSwitchedByShortCutToast = Toast.makeText(mContext, toastText.toString(),
- Toast.LENGTH_SHORT);
+ if (mSubtypeSwitchedByShortCutToast == null) {
+ mSubtypeSwitchedByShortCutToast = Toast.makeText(mContext, toastText,
+ Toast.LENGTH_SHORT);
+ } else {
+ mSubtypeSwitchedByShortCutToast.setText(toastText);
+ }
mSubtypeSwitchedByShortCutToast.show();
}
}
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index d136f1a..9ab6300 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -401,7 +401,8 @@
return getLockCredentialFilePathForUser(userId, BASE_ZERO_LOCK_PATTERN_FILE);
}
- private String getChildProfileLockFile(int userId) {
+ @VisibleForTesting
+ String getChildProfileLockFile(int userId) {
return getLockCredentialFilePathForUser(userId, CHILD_PROFILE_LOCK_FILE);
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d34e8fc..53c6024 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -4248,6 +4248,7 @@
// Work Challenge is present) let startActivityInPackage handle the intercepting.
if (!mService.mUserController.shouldConfirmCredentials(task.userId)
&& task.getRootActivity() != null) {
+ mActivityMetricsLogger.notifyActivityLaunching();
mService.moveTaskToFrontLocked(task.taskId, 0, bOptions);
// If we are launching the task in the docked stack, put it into resizing mode so
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2765578..a0f0bee 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1496,7 +1496,7 @@
final StatusBarNotification sbn = mNotificationList.get(i).sbn;
if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
&& (sbn.getNotification().flags
- & Notification.FLAG_AUTOGROUP_SUMMARY) != 0) {
+ & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
// We could pass back a cloneLight() but clients might get confused and
// try to send this thing back to notify() again, which would not work
// very well.
@@ -2543,18 +2543,13 @@
// Handle grouped notifications and bail out early if we
// can to avoid extracting signals.
handleGroupedNotificationLocked(r, old, callingUid, callingPid);
- boolean ignoreNotification =
- removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
- if (DBG) Slog.d(TAG, "ignoreNotification is " + ignoreNotification);
// This conditional is a dirty hack to limit the logging done on
// behalf of the download manager without affecting other apps.
if (!pkg.equals("com.android.providers.downloads")
|| Log.isLoggable("DownloadManager", Log.VERBOSE)) {
int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
- if (ignoreNotification) {
- enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
- } else if (old != null) {
+ if (old != null) {
enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
}
EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
@@ -2562,10 +2557,6 @@
enqueueStatus);
}
- if (ignoreNotification) {
- return;
- }
-
mRankingHelper.extractSignals(r);
final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
@@ -2681,58 +2672,6 @@
}
}
- /**
- * Performs group notification optimizations if SysUI is the only active
- * notification listener and returns whether the given notification should
- * be ignored.
- *
- * <p>Returns true if the given notification is a child of a group with a
- * summary, which means that SysUI will never show it, and hence the new
- * notification can be safely ignored. Also cancels any previous instance
- * of the ignored notification.</p>
- *
- * <p>For summaries, cancels all children of that group, as SysUI will
- * never show them anymore.</p>
- *
- * @return true if the given notification can be ignored as an optimization
- */
- private boolean removeUnusedGroupedNotificationLocked(NotificationRecord r,
- NotificationRecord old, int callingUid, int callingPid) {
- if (!ENABLE_CHILD_NOTIFICATIONS) {
- // No optimizations are possible if listeners want groups.
- if (mListeners.notificationGroupsDesired()) {
- return false;
- }
-
- StatusBarNotification sbn = r.sbn;
- String group = sbn.getGroupKey();
- boolean isSummary = sbn.getNotification().isGroupSummary();
- boolean isChild = !isSummary && sbn.isGroup();
-
- NotificationRecord summary = mSummaryByGroupKey.get(group);
- if (isChild && summary != null) {
- // Child with an active summary -> ignore
- if (DBG) {
- Slog.d(TAG, "Ignoring group child " + sbn.getKey() + " due to existing summary "
- + summary.getKey());
- }
- // Make sure we don't leave an old version of the notification around.
- if (old != null) {
- if (DBG) {
- Slog.d(TAG, "Canceling old version of ignored group child " + sbn.getKey());
- }
- cancelNotificationLocked(old, false, REASON_GROUP_OPTIMIZATION);
- }
- return true;
- } else if (isSummary) {
- // Summary -> cancel children
- cancelGroupChildrenLocked(r, callingUid, callingPid, null,
- REASON_GROUP_OPTIMIZATION);
- }
- }
- return false;
- }
-
@VisibleForTesting
void buzzBeepBlinkLocked(NotificationRecord record) {
boolean buzz = false;
@@ -3869,7 +3808,6 @@
public class NotificationListeners extends ManagedServices {
private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
- private boolean mNotificationGroupsDesired;
public NotificationListeners() {
super(getContext(), mHandler, mNotificationList, mUserProfiles);
@@ -3902,7 +3840,6 @@
final INotificationListener listener = (INotificationListener) info.service;
final NotificationRankingUpdate update;
synchronized (mNotificationList) {
- updateNotificationGroupsDesiredLocked();
update = makeRankingUpdateLocked(info);
}
try {
@@ -3919,7 +3856,6 @@
updateEffectsSuppressorLocked();
}
mLightTrimListeners.remove(removed);
- updateNotificationGroupsDesiredLocked();
}
public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
@@ -4112,31 +4048,6 @@
}
return false;
}
-
- /**
- * Returns whether any of the currently registered listeners wants to receive notification
- * groups.
- *
- * <p>Currently we assume groups are desired by non-SystemUI listeners.</p>
- */
- public boolean notificationGroupsDesired() {
- return mNotificationGroupsDesired;
- }
-
- private void updateNotificationGroupsDesiredLocked() {
- mNotificationGroupsDesired = true;
- // No listeners, no groups.
- if (mServices.isEmpty()) {
- mNotificationGroupsDesired = false;
- return;
- }
- // One listener: Check whether it's SysUI.
- if (mServices.size() == 1 &&
- mServices.get(0).component.getPackageName().equals("com.android.systemui")) {
- mNotificationGroupsDesired = false;
- return;
- }
- }
}
public static final class DumpFilter {
diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
index 160d44c..27077f2 100644
--- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
@@ -27,8 +27,11 @@
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.vr.IVrManager;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -45,6 +48,7 @@
import android.widget.FrameLayout;
import com.android.internal.R;
+import com.android.server.vr.VrManagerService;
/**
* Helper to manage showing/hiding a confirmation prompt when the navigation bar is hidden
@@ -66,6 +70,7 @@
private long mPanicTime;
private WindowManager mWindowManager;
private int mCurrentUserId;
+ private IVrManager mVrManager;
public ImmersiveModeConfirmation(Context context) {
mContext = context;
@@ -75,6 +80,8 @@
.getInteger(R.integer.config_immersive_mode_confirmation_panic);
mWindowManager = (WindowManager)
mContext.getSystemService(Context.WINDOW_SERVICE);
+ mVrManager = (IVrManager) IVrManager.Stub.asInterface(
+ ServiceManager.getService(VrManagerService.VR_MANAGER_BINDER_SERVICE));
}
private long getNavBarExitDuration() {
@@ -112,6 +119,14 @@
}
}
+ private boolean getVrMode() {
+ boolean vrMode = false;
+ try {
+ vrMode = mVrManager.getVrModeState();
+ } catch (RemoteException ex) { }
+ return vrMode;
+ }
+
public void immersiveModeChanged(String pkg, boolean isImmersiveMode,
boolean userSetupComplete) {
mHandler.removeMessages(H.SHOW);
@@ -119,7 +134,10 @@
final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg);
if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s mConfirmed=%s",
disabled, mConfirmed));
- if (!disabled && (DEBUG_SHOW_EVERY_TIME || !mConfirmed) && userSetupComplete) {
+ if (!disabled
+ && (DEBUG_SHOW_EVERY_TIME || !mConfirmed)
+ && userSetupComplete
+ && !getVrMode()) {
mHandler.sendEmptyMessageDelayed(H.SHOW, mShowDelayMs);
}
} else {
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index f004b45..49ff385 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -50,6 +50,8 @@
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.StringBuilder;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -103,6 +105,7 @@
new RemoteCallbackList<>();
private final ArraySet<String> mPreviousToggledListenerSettings = new ArraySet<>();
private String mPreviousNotificationPolicyAccessPackage;
+ private String mPreviousCoarseLocationPackage;
private String mPreviousManageOverlayPackage;
private static final int MSG_VR_STATE_CHANGE = 0;
@@ -186,6 +189,32 @@
return VrManagerService.this.getVrMode();
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("permission denied: can't dump VrManagerService from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+ pw.print("mVrModeEnabled=");
+ pw.println(mVrModeEnabled);
+ pw.print("mCurrentVrModeUser=");
+ pw.println(mCurrentVrModeUser);
+ pw.print("mRemoteCallbacks=");
+ int i=mRemoteCallbacks.beginBroadcast(); // create the broadcast item array
+ while(i-->0) {
+ pw.print(mRemoteCallbacks.getBroadcastItem(i));
+ if (i>0) pw.print(", ");
+ }
+ mRemoteCallbacks.finishBroadcast();
+ pw.println();
+ pw.print("mCurrentVrService=");
+ pw.println(mCurrentVrService != null ? mCurrentVrService.getComponent() : "(none)");
+ pw.print("mCurrentVrModeComponent=");
+ pw.println(mCurrentVrModeComponent);
+ }
+
};
private void enforceCallerPermission(String permission) {
@@ -418,6 +447,7 @@
mWasDefaultGranted = true;
+ grantCoarseLocationAccess(pName, userId);
grantOverlayAccess(pName, userId);
grantNotificationPolicyAccess(pName);
grantNotificationListenerAccess(pName, userId);
@@ -447,6 +477,7 @@
String pName = component.getPackageName();
if (mWasDefaultGranted) {
+ revokeCoarseLocationAccess(userId);
revokeOverlayAccess(userId);
revokeNotificationPolicyAccess(pName);
revokeNotificiationListenerAccess();
@@ -455,6 +486,27 @@
}
+ private void grantCoarseLocationAccess(String pkg, UserHandle userId) {
+ PackageManager pm = mContext.getPackageManager();
+ boolean prev = (PackageManager.PERMISSION_GRANTED ==
+ pm.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, pkg));
+ mPreviousCoarseLocationPackage = null;
+ if (!prev) {
+ pm.grantRuntimePermission(pkg, android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ userId);
+ mPreviousCoarseLocationPackage = pkg;
+ }
+ }
+
+ private void revokeCoarseLocationAccess(UserHandle userId) {
+ PackageManager pm = mContext.getPackageManager();
+ if (mPreviousCoarseLocationPackage != null) {
+ pm.revokeRuntimePermission(mPreviousCoarseLocationPackage,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION, userId);
+ mPreviousCoarseLocationPackage = null;
+ }
+ }
+
private void grantOverlayAccess(String pkg, UserHandle userId) {
PackageManager pm = mContext.getPackageManager();
boolean prev = (PackageManager.PERMISSION_GRANTED ==
@@ -476,7 +528,6 @@
}
}
-
private void grantNotificationPolicyAccess(String pkg) {
NotificationManager nm = mContext.getSystemService(NotificationManager.class);
boolean prev = nm.isNotificationPolicyAccessGrantedForPackage(pkg);
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index cd976e7..df5d027 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -119,6 +119,8 @@
}
private void updateFallbackStateOnBoot() {
+ if (!mSystemInterface.isFallbackLogicEnabled()) return;
+
WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
updateFallbackState(webviewProviders, true);
}
@@ -497,8 +499,15 @@
mWebViewPackageDirty = false;
// If we have changed provider since we started the relro creation we need to
// redo the whole process using the new package instead.
- PackageInfo newPackage = findPreferredWebViewPackage();
- onWebViewProviderChanged(newPackage);
+ try {
+ PackageInfo newPackage = findPreferredWebViewPackage();
+ onWebViewProviderChanged(newPackage);
+ } catch (WebViewFactory.MissingWebViewPackageException e) {
+ // If we can't find any valid WebView package we are now in a state where
+ // mAnyWebViewInstalled is false, so loading WebView will be blocked and we
+ // should simply wait until we receive an intent declaring a new package was
+ // installed.
+ }
} else {
mLock.notifyAll();
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 446b74b..1475686 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -910,7 +910,7 @@
// Calculate the content bounds excluding the area occupied by IME
getDisplayContent().getContentRect(displayContentRect);
contentBounds.set(displayContentRect);
- int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top);
+ int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
// if IME window is animating, get its actual vertical shown position (but no smaller than
// the final target vertical position)
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9c25f63..140f381 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1288,7 +1288,7 @@
}
final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
- if (w == winShowWhenLocked) {
+ if (w == winShowWhenLocked && mPolicy.isKeyguardShowingOrOccluded()) {
return;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e7ae2b0..6f45508 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -823,7 +823,7 @@
mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
mSystemServiceManager.startService(
- "com.android.server.wifi.WifiScanningService");
+ "com.android.server.wifi.scanner.WifiScanningService");
if (!disableRtt) {
mSystemServiceManager.startService("com.android.server.wifi.RttService");
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
index dae8447..7d28e39 100644
--- a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
@@ -87,6 +87,12 @@
return new File(mStorageDir,
super.getLockPasswordFilename(userId).replace('/', '-')).getAbsolutePath();
}
+
+ @Override
+ String getChildProfileLockFile(int userId) {
+ return new File(mStorageDir,
+ super.getChildProfileLockFile(userId).replace('/', '-')).getAbsolutePath();
+ }
};
}
@@ -235,6 +241,27 @@
assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash);
}
+ public void testLockType_WriteProfileWritesParent() {
+ mStorage.writePasswordHash("parentpassword".getBytes(), 10);
+ mStorage.writePatternHash("12345678".getBytes(), 20);
+
+ assertEquals(2, mStorage.getStoredCredentialType(10));
+ assertEquals(1, mStorage.getStoredCredentialType(20));
+ mStorage.clearCache();
+ assertEquals(2, mStorage.getStoredCredentialType(10));
+ assertEquals(1, mStorage.getStoredCredentialType(20));
+ }
+
+ public void testProfileLock_ReadWriteChildProfileLock() {
+ assertFalse(mStorage.hasChildProfileLock(20));
+ mStorage.writeChildProfileLock(20, "profilepassword".getBytes());
+ assertArrayEquals("profilepassword".getBytes(), mStorage.readChildProfileLock(20));
+ assertTrue(mStorage.hasChildProfileLock(20));
+ mStorage.clearCache();
+ assertArrayEquals("profilepassword".getBytes(), mStorage.readChildProfileLock(20));
+ assertTrue(mStorage.hasChildProfileLock(20));
+ }
+
public void testPassword_WriteParentWritesProfile() {
mStorage.writePasswordHash("profilepassword".getBytes(), 2);
mStorage.writePasswordHash("parentpasswordd".getBytes(), 1);
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
new file mode 100644
index 0000000..26b87c5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.webkit;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.webkit.WebViewProviderInfo;
+
+import java.util.HashMap;
+
+public class TestSystemImpl implements SystemInterface {
+ private String mUserProvider = "";
+ private final WebViewProviderInfo[] mPackageConfigs;
+ HashMap<String, PackageInfo> mPackages = new HashMap();
+ private boolean mFallbackLogicEnabled;
+ private final int mNumRelros;
+ private final boolean mIsDebuggable;
+
+ public TestSystemImpl(WebViewProviderInfo[] packageConfigs, boolean fallbackLogicEnabled,
+ int numRelros, boolean isDebuggable) {
+ mPackageConfigs = packageConfigs;
+ mFallbackLogicEnabled = fallbackLogicEnabled;
+ mNumRelros = numRelros;
+ mIsDebuggable = isDebuggable;
+ }
+
+ @Override
+ public WebViewProviderInfo[] getWebViewPackages() {
+ return mPackageConfigs;
+ }
+
+ @Override
+ public int onWebViewProviderChanged(PackageInfo packageInfo) {
+ return mNumRelros;
+ }
+
+ @Override
+ public String getUserChosenWebViewProvider(Context context) { return mUserProvider; }
+
+ @Override
+ public void updateUserSetting(Context context, String newProviderName) {
+ mUserProvider = newProviderName;
+ }
+
+ @Override
+ public void killPackageDependents(String packageName) {}
+
+ @Override
+ public boolean isFallbackLogicEnabled() {
+ return mFallbackLogicEnabled;
+ }
+
+ @Override
+ public void enableFallbackLogic(boolean enable) {
+ mFallbackLogicEnabled = enable;
+ }
+
+ @Override
+ public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) {
+ enablePackageForAllUsers(context, packageName, false);
+ }
+
+ @Override
+ public void enablePackageForAllUsers(Context context, String packageName, boolean enable) {
+ enablePackageForUser(packageName, enable, 0);
+ }
+
+ @Override
+ public void enablePackageForUser(String packageName, boolean enable, int userId) {
+ PackageInfo packageInfo = mPackages.get(packageName);
+ if (packageInfo == null) {
+ throw new IllegalArgumentException("There is no package called " + packageName);
+ }
+ packageInfo.applicationInfo.enabled = enable;
+ setPackageInfo(packageInfo);
+ }
+
+ @Override
+ public boolean systemIsDebuggable() { return mIsDebuggable; }
+
+ @Override
+ public PackageInfo getPackageInfoForProvider(WebViewProviderInfo info) throws
+ NameNotFoundException {
+ PackageInfo ret = mPackages.get(info.packageName);
+ if (ret == null) throw new NameNotFoundException(info.packageName);
+ return ret;
+ }
+
+ public void setPackageInfo(PackageInfo pi) {
+ mPackages.put(pi.packageName, pi);
+ }
+
+ @Override
+ public int getFactoryPackageVersion(String packageName) {
+ return 0;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
new file mode 100644
index 0000000..c00520d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.webkit;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.Signature;
+import android.os.Bundle;
+import android.util.Base64;
+import android.test.AndroidTestCase;
+
+import android.webkit.WebViewFactory;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewProviderResponse;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.hamcrest.Description;
+
+import org.mockito.Mockito;
+import org.mockito.Matchers;
+import org.mockito.ArgumentMatcher;
+
+
+/**
+ * Tests for WebViewUpdateService
+ */
+public class WebViewUpdateServiceTest extends AndroidTestCase {
+ private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
+
+ private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl;
+ private TestSystemImpl mTestSystemImpl;
+
+ private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Creates a new instance.
+ */
+ public WebViewUpdateServiceTest() {
+ }
+
+ private void setupWithPackages(WebViewProviderInfo[] packages) {
+ setupWithPackages(packages, true);
+ }
+
+ private void setupWithPackages(WebViewProviderInfo[] packages,
+ boolean fallbackLogicEnabled) {
+ setupWithPackages(packages, fallbackLogicEnabled, 1);
+ }
+
+ private void setupWithPackages(WebViewProviderInfo[] packages,
+ boolean fallbackLogicEnabled, int numRelros) {
+ setupWithPackages(packages, fallbackLogicEnabled, numRelros,
+ true /* isDebuggable == true -> don't check package signatures */);
+ }
+
+ private void setupWithPackages(WebViewProviderInfo[] packages,
+ boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable) {
+ TestSystemImpl testing = new TestSystemImpl(packages, fallbackLogicEnabled, numRelros,
+ isDebuggable);
+ mTestSystemImpl = Mockito.spy(testing);
+ mWebViewUpdateServiceImpl =
+ new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl);
+ }
+
+ private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) {
+ for(WebViewProviderInfo wpi : providers) {
+ mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true /* enabled */,
+ true /* valid */));
+ }
+ }
+
+ private void checkCertainPackageUsedAfterWebViewPreparation(String expectedProviderName,
+ WebViewProviderInfo[] webviewPackages) {
+ checkCertainPackageUsedAfterWebViewPreparation(expectedProviderName, webviewPackages, 1);
+ }
+
+ private void checkCertainPackageUsedAfterWebViewPreparation(String expectedProviderName,
+ WebViewProviderInfo[] webviewPackages, int numRelros) {
+ setupWithPackages(webviewPackages, true, numRelros);
+ // Add (enabled and valid) package infos for each provider
+ setEnabledAndValidPackageInfos(webviewPackages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(expectedProviderName)));
+
+ for (int n = 0; n < numRelros; n++) {
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+ }
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(expectedProviderName, response.packageInfo.packageName);
+ }
+
+ // For matching the package name of a PackageInfo
+ private class IsPackageInfoWithName extends ArgumentMatcher<PackageInfo> {
+ private final String mPackageName;
+
+ IsPackageInfoWithName(String name) {
+ mPackageName = name;
+ }
+
+ @Override
+ public boolean matches(Object p) {
+ return ((PackageInfo) p).packageName.equals(mPackageName);
+ }
+
+ // Provide a more useful description in case of mismatch
+ @Override
+ public void describeTo (Description description) {
+ description.appendText(String.format("PackageInfo with name '%s'", mPackageName));
+ }
+ }
+
+ private static PackageInfo createPackageInfo(
+ String packageName, boolean enabled, boolean valid) {
+ PackageInfo p = new PackageInfo();
+ p.packageName = packageName;
+ p.applicationInfo = new ApplicationInfo();
+ p.applicationInfo.enabled = enabled;
+ p.applicationInfo.metaData = new Bundle();
+ if (valid) {
+ // no flag means invalid
+ p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah");
+ }
+ return p;
+ }
+
+ private static PackageInfo createPackageInfo(
+ String packageName, boolean enabled, boolean valid, Signature[] signatures) {
+ PackageInfo p = createPackageInfo(packageName, enabled, valid);
+ p.signatures = signatures;
+ return p;
+ }
+
+
+ // ****************
+ // Tests
+ // ****************
+
+
+ public void testWithSinglePackage() {
+ String testPackageName = "test.package.name";
+ checkCertainPackageUsedAfterWebViewPreparation(testPackageName,
+ new WebViewProviderInfo[] {
+ new WebViewProviderInfo(testPackageName, "",
+ true /*default available*/, false /* fallback */, null)});
+ }
+
+ public void testDefaultPackageUsedOverNonDefault() {
+ String defaultPackage = "defaultPackage";
+ String nonDefaultPackage = "nonDefaultPackage";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(nonDefaultPackage, "", false, false, null),
+ new WebViewProviderInfo(defaultPackage, "", true, false, null)};
+ checkCertainPackageUsedAfterWebViewPreparation(defaultPackage, packages);
+ }
+
+ public void testSeveralRelros() {
+ String singlePackage = "singlePackage";
+ checkCertainPackageUsedAfterWebViewPreparation(
+ singlePackage,
+ new WebViewProviderInfo[] {
+ new WebViewProviderInfo(singlePackage, "", true /*def av*/, false, null)},
+ 2);
+ }
+
+ // Ensure that package with valid signatures is chosen rather than package with invalid
+ // signatures.
+ public void testWithSignatures() {
+ String validPackage = "valid package";
+ String invalidPackage = "invalid package";
+
+ Signature validSignature = new Signature("11");
+ Signature invalidExpectedSignature = new Signature("22");
+ Signature invalidPackageSignature = new Signature("33");
+
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(invalidPackage, "", true, false, new String[]{
+ Base64.encodeToString(
+ invalidExpectedSignature.toByteArray(), Base64.DEFAULT)}),
+ new WebViewProviderInfo(validPackage, "", true, false, new String[]{
+ Base64.encodeToString(
+ validSignature.toByteArray(), Base64.DEFAULT)})
+ };
+ setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */,
+ false /* isDebuggable */);
+ mTestSystemImpl.setPackageInfo(createPackageInfo(invalidPackage, true /* enabled */,
+ true /* valid */, new Signature[]{invalidPackageSignature}));
+ mTestSystemImpl.setPackageInfo(createPackageInfo(validPackage, true /* enabled */,
+ true /* valid */, new Signature[]{validSignature}));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(validPackage)));
+
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(validPackage, response.packageInfo.packageName);
+
+ WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages();
+ assertEquals(1, validPackages.length);
+ assertEquals(validPackage, validPackages[0].packageName);
+ }
+
+ public void testFailWaitingForRelro() {
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo("packagename", "", true, true, null)};
+ setupWithPackages(packages);
+ setEnabledAndValidPackageInfos(packages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(packages[0].packageName)));
+
+ // Never call notifyRelroCreation()
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO, response.status);
+ }
+
+ public void testFailListingEmptyWebviewPackages() {
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[0];
+ setupWithPackages(packages);
+ setEnabledAndValidPackageInfos(packages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
+ Matchers.anyObject());
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+ }
+
+ public void testFailListingInvalidWebviewPackage() {
+ WebViewProviderInfo wpi = new WebViewProviderInfo("", "", true, true, null);
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
+ setupWithPackages(packages);
+ mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName, true, false));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+ }
+
+ // Test that switching provider using changeProviderAndSetting works.
+ public void testSwitchingProvider() {
+ String firstPackage = "first";
+ String secondPackage = "second";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(firstPackage, "", true, false, null),
+ new WebViewProviderInfo(secondPackage, "", true, false, null)};
+ checkSwitchingProvider(packages, firstPackage, secondPackage);
+ }
+
+ public void testSwitchingProviderToNonDefault() {
+ String defaultPackage = "defaultPackage";
+ String nonDefaultPackage = "nonDefaultPackage";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(defaultPackage, "", true, false, null),
+ new WebViewProviderInfo(nonDefaultPackage, "", false, false, null)};
+ checkSwitchingProvider(packages, defaultPackage, nonDefaultPackage);
+ }
+
+ private void checkSwitchingProvider(WebViewProviderInfo[] packages, String initialPackage,
+ String finalPackage) {
+ checkCertainPackageUsedAfterWebViewPreparation(initialPackage, packages);
+
+ mWebViewUpdateServiceImpl.changeProviderAndSetting(finalPackage);
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(finalPackage)));
+
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+ WebViewProviderResponse secondResponse = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, secondResponse.status);
+ assertEquals(finalPackage, secondResponse.packageInfo.packageName);
+
+ Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(initialPackage));
+ }
+
+ // Change provider during relro creation by using changeProviderAndSetting
+ public void testSwitchingProviderDuringRelroCreation() {
+ checkChangingProviderDuringRelroCreation(true);
+ }
+
+ // Change provider during relro creation by enabling a provider
+ public void testChangingProviderThroughEnablingDuringRelroCreation() {
+ checkChangingProviderDuringRelroCreation(false);
+ }
+
+ private void checkChangingProviderDuringRelroCreation(boolean settingsChange) {
+ String firstPackage = "first";
+ String secondPackage = "second";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(firstPackage, "", true, false, null),
+ new WebViewProviderInfo(secondPackage, "", true, false, null)};
+ setupWithPackages(packages);
+ if (settingsChange) {
+ // Have all packages be enabled, so that we can change provider however we want to
+ setEnabledAndValidPackageInfos(packages);
+ } else {
+ // Have all packages be disabled so that we can change one to enabled later
+ for(WebViewProviderInfo wpi : packages) {
+ mTestSystemImpl.setPackageInfo(createPackageInfo(wpi.packageName,
+ false /* enabled */, true /* valid */));
+ }
+ }
+
+ CountDownLatch countdown = new CountDownLatch(1);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
+
+ assertEquals(firstPackage, mWebViewUpdateServiceImpl.getCurrentWebViewPackageName());
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ WebViewProviderResponse threadResponse =
+ mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, threadResponse.status);
+ assertEquals(secondPackage, threadResponse.packageInfo.packageName);
+ // Verify that we killed the first package
+ Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage));
+ countdown.countDown();
+ }
+ }).start();
+ try {
+ Thread.sleep(1000); // Let the new thread run / be blocked
+ } catch (InterruptedException e) {
+ }
+
+ if (settingsChange) {
+ mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
+ } else {
+ // Switch provider by enabling the second one
+ mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
+ true /* valid */));
+ mWebViewUpdateServiceImpl.packageStateChanged(
+ secondPackage, WebViewUpdateService.PACKAGE_CHANGED);
+ }
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+ // first package done, should start on second
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(secondPackage)));
+
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+ // second package done, the other thread should now be unblocked
+ try {
+ countdown.await();
+ } catch (InterruptedException e) {
+ }
+ }
+
+ public void testRunFallbackLogicIfEnabled() {
+ checkFallbackLogicBeingRun(true);
+ }
+
+ public void testDontRunFallbackLogicIfDisabled() {
+ checkFallbackLogicBeingRun(false);
+ }
+
+ private void checkFallbackLogicBeingRun(boolean fallbackLogicEnabled) {
+ String primaryPackage = "primary";
+ String fallbackPackage = "fallback";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ primaryPackage, "", true /* default available */, false /* fallback */, null),
+ new WebViewProviderInfo(
+ fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+ setupWithPackages(packages, fallbackLogicEnabled);
+ setEnabledAndValidPackageInfos(packages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ // Verify that we disable the fallback package if fallback logic enabled, and don't disable
+ // the fallback package if that logic is disabled
+ if (fallbackLogicEnabled) {
+ Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Mockito.eq(fallbackPackage));
+ } else {
+ Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Matchers.anyObject());
+ }
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
+
+ // Enable fallback package
+ mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
+ true /* valid */));
+ mWebViewUpdateServiceImpl.packageStateChanged(
+ fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED);
+
+ if (fallbackLogicEnabled) {
+ // Check that we have now disabled the fallback package twice
+ Mockito.verify(mTestSystemImpl, Mockito.times(2)).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Mockito.eq(fallbackPackage));
+ } else {
+ // Check that we still haven't disabled any package
+ Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Matchers.anyObject());
+ }
+ }
+
+ /**
+ * Scenario for installing primary package when fallback enabled.
+ * 1. Start with only fallback installed
+ * 2. Install non-fallback
+ * 3. Fallback should be disabled
+ */
+ public void testInstallingNonFallbackPackage() {
+ String primaryPackage = "primary";
+ String fallbackPackage = "fallback";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ primaryPackage, "", true /* default available */, false /* fallback */, null),
+ new WebViewProviderInfo(
+ fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+ setupWithPackages(packages, true /* isFallbackLogicEnabled */);
+ mTestSystemImpl.setPackageInfo(
+ createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */));
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Matchers.anyObject());
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(fallbackPackage)));
+
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(fallbackPackage, response.packageInfo.packageName);
+
+ // Install primary package
+ mTestSystemImpl.setPackageInfo(
+ createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */));
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_ADDED);
+
+ // Verify fallback disabled and primary package used as provider
+ Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers(
+ Matchers.anyObject(), Mockito.eq(fallbackPackage));
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(primaryPackage)));
+
+ // Finish the webview preparation and ensure primary package used and fallback killed
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+ response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(primaryPackage, response.packageInfo.packageName);
+ Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(fallbackPackage));
+ }
+
+ public void testFallbackChangesEnabledState() {
+ String primaryPackage = "primary";
+ String fallbackPackage = "fallback";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ primaryPackage, "", true /* default available */, false /* fallback */, null),
+ new WebViewProviderInfo(
+ fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+ setupWithPackages(packages, true /* fallbackLogicEnabled */);
+ setEnabledAndValidPackageInfos(packages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ // Verify fallback disabled at boot when primary package enabled
+ Mockito.verify(mTestSystemImpl).enablePackageForUser(
+ Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+ Matchers.anyInt());
+
+ mTestSystemImpl.setPackageInfo(
+ createPackageInfo(primaryPackage, false /* enabled */, true /* valid */));
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_CHANGED);
+
+ // Verify fallback becomes enabled when primary package becomes disabled
+ Mockito.verify(mTestSystemImpl).enablePackageForUser(
+ Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */,
+ Matchers.anyInt());
+
+ mTestSystemImpl.setPackageInfo(
+ createPackageInfo(primaryPackage, true /* enabled */, true /* valid */));
+ mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage,
+ WebViewUpdateService.PACKAGE_CHANGED);
+
+ // Verify fallback is disabled a second time when primary package becomes enabled
+ Mockito.verify(mTestSystemImpl, Mockito.times(2)).enablePackageForUser(
+ Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+ Matchers.anyInt());
+ }
+
+ public void testAddUserWhenFallbackLogicEnabled() {
+ checkAddingNewUser(true);
+ }
+
+ public void testAddUserWhenFallbackLogicDisabled() {
+ checkAddingNewUser(false);
+ }
+
+ public void checkAddingNewUser(boolean fallbackLogicEnabled) {
+ String primaryPackage = "primary";
+ String fallbackPackage = "fallback";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ primaryPackage, "", true /* default available */, false /* fallback */, null),
+ new WebViewProviderInfo(
+ fallbackPackage, "", true /* default available */, true /* fallback */, null)};
+ setupWithPackages(packages, fallbackLogicEnabled);
+ setEnabledAndValidPackageInfos(packages);
+ int newUser = 100;
+ mWebViewUpdateServiceImpl.handleNewUser(newUser);
+ if (fallbackLogicEnabled) {
+ // Verify fallback package becomes disabled for new user
+ Mockito.verify(mTestSystemImpl).enablePackageForUser(
+ Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */,
+ Mockito.eq(newUser));
+ } else {
+ // Verify that we don't disable fallback for new user
+ Mockito.verify(mTestSystemImpl, Mockito.never()).enablePackageForUser(
+ Mockito.anyObject(), Matchers.anyBoolean() /* enable */,
+ Matchers.anyInt() /* user */);
+ }
+ }
+
+ /**
+ * Timing dependent test where we verify that the list of valid webview packages becoming empty
+ * at a certain point doesn't crash us or break our state.
+ */
+ public void testNotifyRelroDoesntCrashIfNoPackages() {
+ String firstPackage = "first";
+ String secondPackage = "second";
+ WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
+ new WebViewProviderInfo(firstPackage, "", true /* default available */,
+ false /* fallback */, null),
+ new WebViewProviderInfo(secondPackage, "", true /* default available */,
+ false /* fallback */, null)};
+ setupWithPackages(packages);
+ // Add (enabled and valid) package infos for each provider
+ setEnabledAndValidPackageInfos(packages);
+
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+
+ Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
+
+ mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage);
+
+ // Make packages invalid to cause exception to be thrown
+ mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
+ false /* valid */));
+ mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */,
+ false /* valid */));
+
+ // This shouldn't throw an exception!
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+ WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
+
+ // Now make a package valid again and verify that we can switch back to that
+ mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */,
+ true /* valid */));
+
+ mWebViewUpdateServiceImpl.packageStateChanged(firstPackage,
+ WebViewUpdateService.PACKAGE_ADDED);
+
+ // Second time we call onWebViewProviderChanged for firstPackage
+ Mockito.verify(mTestSystemImpl, Mockito.times(2)).onWebViewProviderChanged(
+ Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
+
+ mWebViewUpdateServiceImpl.notifyRelroCreationCompleted();
+
+ response = mWebViewUpdateServiceImpl.waitForAndGetProvider();
+ assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status);
+ assertEquals(firstPackage, response.packageInfo.packageName);
+ }
+
+ // TODO (gsennton) add more tests for ensuring killPackageDependents is called / not called
+}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 18fd985..b9e9ac8 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -374,6 +374,15 @@
</activity>
<activity
+ android:name="GetBitmapSurfaceViewActivity"
+ android:label="SurfaceView/GetBitmap with Camera source">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="GLTextureViewActivity"
android:label="TextureView/OpenGL">
<intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
new file mode 100644
index 0000000..d3cd7db
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.PixelCopy;
+import android.graphics.PixelCopy.OnPixelCopyFinished;
+import android.graphics.PixelCopy.Response;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.Gravity;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.Toast;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class GetBitmapSurfaceViewActivity extends Activity implements SurfaceHolder.Callback {
+ private Camera mCamera;
+ private SurfaceView mSurfaceView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout content = new FrameLayout(this);
+
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.getHolder().addCallback(this);
+
+ Button button = new Button(this);
+ button.setText("Copy bitmap to /sdcard/surfaceview.png");
+ button.setOnClickListener((View v) -> {
+ Bitmap b = Bitmap.createBitmap(
+ mSurfaceView.getWidth(),
+ mSurfaceView.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ PixelCopy.request(mSurfaceView, b,
+ mOnCopyFinished, mSurfaceView.getHandler());
+ });
+
+ content.addView(mSurfaceView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
+ content.addView(button, new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM));
+ setContentView(content);
+ }
+
+ private final OnPixelCopyFinished mOnCopyFinished = new OnPixelCopyFinished() {
+ @Override
+ public void onPixelCopyFinished(Response response) {
+ if (!response.success) {
+ Toast.makeText(GetBitmapSurfaceViewActivity.this,
+ "Failed to copy", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ try {
+ try (FileOutputStream out = new FileOutputStream(
+ Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
+ response.bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ };
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mCamera = Camera.open();
+
+ try {
+ mCamera.setPreviewSurface(holder.getSurface());
+ } catch (IOException t) {
+ android.util.Log.e("TextureView", "Cannot set preview texture target!", t);
+ }
+
+ mCamera.startPreview();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mCamera.stopPreview();
+ mCamera.release();
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
index b1431c5..5c30fab 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
@@ -17,18 +17,29 @@
package com.android.test.hwui;
import android.app.Activity;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.PixelCopy;
+import android.graphics.PixelCopy.OnPixelCopyFinished;
+import android.graphics.PixelCopy.Response;
import android.graphics.PorterDuff;
import android.os.Bundle;
-import android.view.Gravity;
+import android.os.Environment;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.Toast;
-@SuppressWarnings({"UnusedDeclaration"})
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
public class HardwareCanvasSurfaceViewActivity extends Activity implements Callback {
private SurfaceView mSurfaceView;
private HardwareCanvasSurfaceViewActivity.RenderingThread mThread;
@@ -42,13 +53,49 @@
mSurfaceView = new SurfaceView(this);
mSurfaceView.getHolder().addCallback(this);
- content.addView(mSurfaceView, new FrameLayout.LayoutParams(
+ Button button = new Button(this);
+ button.setText("Copy bitmap to /sdcard/surfaceview.png");
+ button.setOnClickListener((View v) -> {
+ Bitmap b = Bitmap.createBitmap(
+ mSurfaceView.getWidth(),
+ mSurfaceView.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ PixelCopy.request(mSurfaceView, b,
+ mOnCopyFinished, mSurfaceView.getHandler());
+ });
+
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ layout.addView(mSurfaceView, LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+
+ content.addView(layout, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT,
- Gravity.CENTER));
+ FrameLayout.LayoutParams.MATCH_PARENT));
setContentView(content);
}
+ private final OnPixelCopyFinished mOnCopyFinished = new OnPixelCopyFinished() {
+ @Override
+ public void onPixelCopyFinished(Response response) {
+ if (!response.success) {
+ Toast.makeText(HardwareCanvasSurfaceViewActivity.this,
+ "Failed to copy", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ try {
+ try (FileOutputStream out = new FileOutputStream(
+ Environment.getExternalStorageDirectory() + "/surfaceview.png");) {
+ response.bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ }
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ };
+
@Override
public void surfaceCreated(SurfaceHolder holder) {
mThread = new RenderingThread(holder.getSurface());
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index 7412bc2..50efc7f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -245,6 +245,13 @@
return sFontLocation;
}
+ // ---- delegate methods ----
+ @LayoutlibDelegate
+ /*package*/ static boolean addFont(FontFamily thisFontFamily, String path, int ttcIndex) {
+ final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mNativePtr);
+ return delegate != null && delegate.addFont(path, ttcIndex);
+ }
+
// ---- native methods ----
@LayoutlibDelegate
@@ -270,16 +277,8 @@
}
@LayoutlibDelegate
- /*package*/ static boolean nAddFont(long nativeFamily, final String path, int ttcIndex) {
- // FIXME: support ttc fonts. Hack JRE??
- final FontFamily_Delegate delegate = getDelegate(nativeFamily);
- if (delegate != null) {
- if (sFontLocation == null) {
- delegate.mPostInitRunnables.add(() -> delegate.addFont(path));
- return true;
- }
- return delegate.addFont(path);
- }
+ /*package*/ static boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex) {
+ assert false : "The only client of this method has been overriden.";
return false;
}
@@ -390,6 +389,15 @@
mPostInitRunnables = null;
}
+ private boolean addFont(final String path, int ttcIndex) {
+ // FIXME: support ttc fonts. Hack JRE??
+ if (sFontLocation == null) {
+ mPostInitRunnables.add(() -> addFont(path));
+ return true;
+ }
+ return addFont(path);
+ }
+
private boolean addFont(@NonNull String path) {
return addFont(path, DEFAULT_FONT_WEIGHT, path.endsWith(FONT_SUFFIX_ITALIC));
}
diff --git a/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java b/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java
index 6c34c70..6d3bb4c 100644
--- a/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/util/PathParser_Delegate.java
@@ -64,15 +64,14 @@
}
@LayoutlibDelegate
- /*package*/ static boolean nParseStringForPath(long pathPtr, @NonNull String pathString, int
+ /*package*/ static void nParseStringForPath(long pathPtr, @NonNull String pathString, int
stringLength) {
Path_Delegate path_delegate = Path_Delegate.getDelegate(pathPtr);
if (path_delegate == null) {
- return false;
+ return;
}
assert pathString.length() == stringLength;
PathDataNode.nodesToPath(createNodesFromPathData(pathString), path_delegate);
- return true;
}
@LayoutlibDelegate
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 483bddc..061bed7 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -192,6 +192,7 @@
"android.graphics.BitmapFactory#setDensityFromOptions",
"android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
"android.graphics.drawable.GradientDrawable#buildRing",
+ "android.graphics.FontFamily#addFont",
"android.graphics.Typeface#getSystemFontConfigLocation",
"android.graphics.Typeface#makeFamilyFromParsed",
"android.os.Handler#sendMessageAtTime",