Merge "Make public getFragments() and onGetLayoutInflater() methods" into oc-dev
diff --git a/api/current.txt b/api/current.txt
index 28e8918..5287984 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -696,6 +696,7 @@
field public static final int hyphenationFrequency = 16843998; // 0x10104de
field public static final int icon = 16842754; // 0x1010002
field public static final int iconPreview = 16843337; // 0x1010249
+ field public static final int iconSpaceReserved = 16844132; // 0x1010564
field public static final int iconTint = 16844129; // 0x1010561
field public static final int iconTintMode = 16844130; // 0x1010562
field public static final int iconifiedByDefault = 16843514; // 0x10102fa
@@ -20892,7 +20893,8 @@
method public int getContentType();
method public int getFlags();
method public int getUsage();
- method public static int getVolumeControlStream(android.media.AudioAttributes);
+ method public static deprecated int getVolumeControlStream(android.media.AudioAttributes);
+ method public int getVolumeControlStream();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
@@ -26675,7 +26677,6 @@
ctor public PublishConfig.Builder();
method public android.net.wifi.aware.PublishConfig build();
method public android.net.wifi.aware.PublishConfig.Builder setMatchFilter(java.util.List<byte[]>);
- method public android.net.wifi.aware.PublishConfig.Builder setPublishCount(int);
method public android.net.wifi.aware.PublishConfig.Builder setPublishType(int);
method public android.net.wifi.aware.PublishConfig.Builder setServiceName(java.lang.String);
method public android.net.wifi.aware.PublishConfig.Builder setServiceSpecificInfo(byte[]);
@@ -26704,7 +26705,6 @@
method public android.net.wifi.aware.SubscribeConfig.Builder setMatchStyle(int);
method public android.net.wifi.aware.SubscribeConfig.Builder setServiceName(java.lang.String);
method public android.net.wifi.aware.SubscribeConfig.Builder setServiceSpecificInfo(byte[]);
- method public android.net.wifi.aware.SubscribeConfig.Builder setSubscribeCount(int);
method public android.net.wifi.aware.SubscribeConfig.Builder setSubscribeType(int);
method public android.net.wifi.aware.SubscribeConfig.Builder setTerminateNotificationEnabled(boolean);
method public android.net.wifi.aware.SubscribeConfig.Builder setTtlSec(int);
@@ -32049,6 +32049,7 @@
method public int getWidgetLayoutResource();
method public boolean hasKey();
method public boolean isEnabled();
+ method public boolean isIconSpaceReserved();
method public boolean isPersistent();
method public boolean isRecycleEnabled();
method public boolean isSelectable();
@@ -32083,6 +32084,7 @@
method public void setFragment(java.lang.String);
method public void setIcon(android.graphics.drawable.Drawable);
method public void setIcon(int);
+ method public void setIconSpaceReserved(boolean);
method public void setIntent(android.content.Intent);
method public void setKey(java.lang.String);
method public void setLayoutResource(int);
@@ -41080,9 +41082,9 @@
public static final class FontConfig.Font implements android.os.Parcelable {
method public int describeContents();
method public android.text.FontConfig.Axis[] getAxes();
- method public android.os.ParcelFileDescriptor getFd();
method public java.lang.String getFontName();
method public int getTtcIndex();
+ method public android.net.Uri getUri();
method public int getWeight();
method public boolean isItalic();
method public void writeToParcel(android.os.Parcel, int);
@@ -48829,6 +48831,7 @@
method public int getRendererRequestedPriority();
method public deprecated float getScale();
method public android.webkit.WebSettings getSettings();
+ method public android.view.textclassifier.TextClassifier getTextClassifier();
method public java.lang.String getTitle();
method public java.lang.String getUrl();
method public android.webkit.WebChromeClient getWebChromeClient();
@@ -48875,6 +48878,7 @@
method public void setNetworkAvailable(boolean);
method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
method public void setRendererPriorityPolicy(int, boolean);
+ method public void setTextClassifier(android.view.textclassifier.TextClassifier);
method public deprecated void setVerticalScrollbarOverlay(boolean);
method public void setWebChromeClient(android.webkit.WebChromeClient);
method public static void setWebContentsDebuggingEnabled(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 59370bd..bec8d25 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -810,6 +810,7 @@
field public static final int hyphenationFrequency = 16843998; // 0x10104de
field public static final int icon = 16842754; // 0x1010002
field public static final int iconPreview = 16843337; // 0x1010249
+ field public static final int iconSpaceReserved = 16844132; // 0x1010564
field public static final int iconTint = 16844129; // 0x1010561
field public static final int iconTintMode = 16844130; // 0x1010562
field public static final int iconifiedByDefault = 16843514; // 0x10102fa
@@ -22628,7 +22629,8 @@
method public int getContentType();
method public int getFlags();
method public int getUsage();
- method public static int getVolumeControlStream(android.media.AudioAttributes);
+ method public static deprecated int getVolumeControlStream(android.media.AudioAttributes);
+ method public int getVolumeControlStream();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
@@ -29367,7 +29369,6 @@
ctor public PublishConfig.Builder();
method public android.net.wifi.aware.PublishConfig build();
method public android.net.wifi.aware.PublishConfig.Builder setMatchFilter(java.util.List<byte[]>);
- method public android.net.wifi.aware.PublishConfig.Builder setPublishCount(int);
method public android.net.wifi.aware.PublishConfig.Builder setPublishType(int);
method public android.net.wifi.aware.PublishConfig.Builder setServiceName(java.lang.String);
method public android.net.wifi.aware.PublishConfig.Builder setServiceSpecificInfo(byte[]);
@@ -29396,7 +29397,6 @@
method public android.net.wifi.aware.SubscribeConfig.Builder setMatchStyle(int);
method public android.net.wifi.aware.SubscribeConfig.Builder setServiceName(java.lang.String);
method public android.net.wifi.aware.SubscribeConfig.Builder setServiceSpecificInfo(byte[]);
- method public android.net.wifi.aware.SubscribeConfig.Builder setSubscribeCount(int);
method public android.net.wifi.aware.SubscribeConfig.Builder setSubscribeType(int);
method public android.net.wifi.aware.SubscribeConfig.Builder setTerminateNotificationEnabled(boolean);
method public android.net.wifi.aware.SubscribeConfig.Builder setTtlSec(int);
@@ -34894,6 +34894,7 @@
method public int getWidgetLayoutResource();
method public boolean hasKey();
method public boolean isEnabled();
+ method public boolean isIconSpaceReserved();
method public boolean isPersistent();
method public boolean isRecycleEnabled();
method public boolean isSelectable();
@@ -34928,6 +34929,7 @@
method public void setFragment(java.lang.String);
method public void setIcon(android.graphics.drawable.Drawable);
method public void setIcon(int);
+ method public void setIconSpaceReserved(boolean);
method public void setIntent(android.content.Intent);
method public void setKey(java.lang.String);
method public void setLayoutResource(int);
@@ -44534,9 +44536,9 @@
public static final class FontConfig.Font implements android.os.Parcelable {
method public int describeContents();
method public android.text.FontConfig.Axis[] getAxes();
- method public android.os.ParcelFileDescriptor getFd();
method public java.lang.String getFontName();
method public int getTtcIndex();
+ method public android.net.Uri getUri();
method public int getWeight();
method public boolean isItalic();
method public void writeToParcel(android.os.Parcel, int);
@@ -52380,6 +52382,7 @@
method public int getRendererRequestedPriority();
method public deprecated float getScale();
method public android.webkit.WebSettings getSettings();
+ method public android.view.textclassifier.TextClassifier getTextClassifier();
method public java.lang.String getTitle();
method public java.lang.String getUrl();
method public android.webkit.WebChromeClient getWebChromeClient();
@@ -52427,6 +52430,7 @@
method public void setNetworkAvailable(boolean);
method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
method public void setRendererPriorityPolicy(int, boolean);
+ method public void setTextClassifier(android.view.textclassifier.TextClassifier);
method public deprecated void setVerticalScrollbarOverlay(boolean);
method public void setWebChromeClient(android.webkit.WebChromeClient);
method public static void setWebContentsDebuggingEnabled(boolean);
@@ -52668,6 +52672,7 @@
method public abstract float getScale();
method public abstract android.webkit.WebViewProvider.ScrollDelegate getScrollDelegate();
method public abstract android.webkit.WebSettings getSettings();
+ method public default android.view.textclassifier.TextClassifier getTextClassifier();
method public abstract java.lang.String getTitle();
method public abstract java.lang.String getTouchIconUrl();
method public abstract java.lang.String getUrl();
@@ -52720,6 +52725,7 @@
method public abstract void setNetworkAvailable(boolean);
method public abstract void setPictureListener(android.webkit.WebView.PictureListener);
method public abstract void setRendererPriorityPolicy(int, boolean);
+ method public default void setTextClassifier(android.view.textclassifier.TextClassifier);
method public abstract void setVerticalScrollbarOverlay(boolean);
method public abstract void setWebChromeClient(android.webkit.WebChromeClient);
method public abstract void setWebViewClient(android.webkit.WebViewClient);
diff --git a/api/test-current.txt b/api/test-current.txt
index f9a6aaf..e9f94d9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -696,6 +696,7 @@
field public static final int hyphenationFrequency = 16843998; // 0x10104de
field public static final int icon = 16842754; // 0x1010002
field public static final int iconPreview = 16843337; // 0x1010249
+ field public static final int iconSpaceReserved = 16844132; // 0x1010564
field public static final int iconTint = 16844129; // 0x1010561
field public static final int iconTintMode = 16844130; // 0x1010562
field public static final int iconifiedByDefault = 16843514; // 0x10102fa
@@ -21005,7 +21006,8 @@
method public int getContentType();
method public int getFlags();
method public int getUsage();
- method public static int getVolumeControlStream(android.media.AudioAttributes);
+ method public static deprecated int getVolumeControlStream(android.media.AudioAttributes);
+ method public int getVolumeControlStream();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
@@ -26788,7 +26790,6 @@
ctor public PublishConfig.Builder();
method public android.net.wifi.aware.PublishConfig build();
method public android.net.wifi.aware.PublishConfig.Builder setMatchFilter(java.util.List<byte[]>);
- method public android.net.wifi.aware.PublishConfig.Builder setPublishCount(int);
method public android.net.wifi.aware.PublishConfig.Builder setPublishType(int);
method public android.net.wifi.aware.PublishConfig.Builder setServiceName(java.lang.String);
method public android.net.wifi.aware.PublishConfig.Builder setServiceSpecificInfo(byte[]);
@@ -26817,7 +26818,6 @@
method public android.net.wifi.aware.SubscribeConfig.Builder setMatchStyle(int);
method public android.net.wifi.aware.SubscribeConfig.Builder setServiceName(java.lang.String);
method public android.net.wifi.aware.SubscribeConfig.Builder setServiceSpecificInfo(byte[]);
- method public android.net.wifi.aware.SubscribeConfig.Builder setSubscribeCount(int);
method public android.net.wifi.aware.SubscribeConfig.Builder setSubscribeType(int);
method public android.net.wifi.aware.SubscribeConfig.Builder setTerminateNotificationEnabled(boolean);
method public android.net.wifi.aware.SubscribeConfig.Builder setTtlSec(int);
@@ -32186,6 +32186,7 @@
method public int getWidgetLayoutResource();
method public boolean hasKey();
method public boolean isEnabled();
+ method public boolean isIconSpaceReserved();
method public boolean isPersistent();
method public boolean isRecycleEnabled();
method public boolean isSelectable();
@@ -32220,6 +32221,7 @@
method public void setFragment(java.lang.String);
method public void setIcon(android.graphics.drawable.Drawable);
method public void setIcon(int);
+ method public void setIconSpaceReserved(boolean);
method public void setIntent(android.content.Intent);
method public void setKey(java.lang.String);
method public void setLayoutResource(int);
@@ -41287,9 +41289,9 @@
public static final class FontConfig.Font implements android.os.Parcelable {
method public int describeContents();
method public android.text.FontConfig.Axis[] getAxes();
- method public android.os.ParcelFileDescriptor getFd();
method public java.lang.String getFontName();
method public int getTtcIndex();
+ method public android.net.Uri getUri();
method public int getWeight();
method public boolean isItalic();
method public void writeToParcel(android.os.Parcel, int);
@@ -49212,6 +49214,7 @@
method public int getRendererRequestedPriority();
method public deprecated float getScale();
method public android.webkit.WebSettings getSettings();
+ method public android.view.textclassifier.TextClassifier getTextClassifier();
method public java.lang.String getTitle();
method public java.lang.String getUrl();
method public android.webkit.WebChromeClient getWebChromeClient();
@@ -49258,6 +49261,7 @@
method public void setNetworkAvailable(boolean);
method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
method public void setRendererPriorityPolicy(int, boolean);
+ method public void setTextClassifier(android.view.textclassifier.TextClassifier);
method public deprecated void setVerticalScrollbarOverlay(boolean);
method public void setWebChromeClient(android.webkit.WebChromeClient);
method public static void setWebContentsDebuggingEnabled(boolean);
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 6e31d80..1ca2be5 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -612,6 +612,51 @@
}
/**
+ * Enables the optional codecs.
+ *
+ * @hide
+ */
+ public void enableOptionalCodecs() {
+ if (DBG) Log.d(TAG, "enableOptionalCodecs");
+ enableDisableOptionalCodecs(true);
+ }
+
+ /**
+ * Disables the optional codecs.
+ *
+ * @hide
+ */
+ public void disableOptionalCodecs() {
+ if (DBG) Log.d(TAG, "disableOptionalCodecs");
+ enableDisableOptionalCodecs(false);
+ }
+
+ /**
+ * Enables or disables the optional codecs.
+ *
+ * @param enable if true, enable the optional codecs, other disable them
+ */
+ private void enableDisableOptionalCodecs(boolean enable) {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ if (enable) {
+ mService.enableOptionalCodecs();
+ } else {
+ mService.disableOptionalCodecs();
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
+ return;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
* Helper for converting a state to a string.
*
* For debug use only - strings are not internationalized.
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index 176e48f..d5e1429 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -63,7 +63,7 @@
public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
private final int mCodecType;
- private final int mCodecPriority;
+ private int mCodecPriority;
private final int mSampleRate;
private final int mBitsPerSample;
private final int mChannelMode;
@@ -280,6 +280,15 @@
}
/**
+ * Checks whether the codec is mandatory.
+ *
+ * @return true if the codec is mandatory, otherwise false.
+ */
+ public boolean isMandatoryCodec() {
+ return mCodecType == SOURCE_CODEC_TYPE_SBC;
+ }
+
+ /**
* Gets the codec selection priority.
* The codec selection priority is relative to other codecs: larger value
* means higher priority. If 0, reset to default.
@@ -291,6 +300,17 @@
}
/**
+ * Sets the codec selection priority.
+ * The codec selection priority is relative to other codecs: larger value
+ * means higher priority. If 0, reset to default.
+ *
+ * @param codecPriority the codec priority
+ */
+ public void setCodecPriority(int codecPriority) {
+ mCodecPriority = codecPriority;
+ }
+
+ /**
* Gets the codec sample rate. The value can be a bitmask with all
* supported sample rates:
* {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index dbb5b7d..a775a1f 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -40,4 +40,6 @@
boolean isA2dpPlaying(in BluetoothDevice device);
BluetoothCodecStatus getCodecStatus();
oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig);
+ oneway void enableOptionalCodecs();
+ oneway void disableOptionalCodecs();
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 7a0158a..b992d29 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -242,6 +242,7 @@
* You can provide multiple intents for a single shortcut so that the last defined activity is launched
* with the other activities in the <a href="/guide/components/tasks-and-back-stack.html">back stack</a>.
* See {@link android.app.TaskStackBuilder} for details.
+ * <p><b>Note:</b> String resources may not be used within an {@code <intent>} element.
* </dd>
* <dt>{@code categories}</dt>
* <dd>Specify shortcut categories. Currently only
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
new file mode 100644
index 0000000..7dec4d7
--- /dev/null
+++ b/core/java/android/os/HidlSupport.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 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.os;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.IntStream;
+
+/** @hide */
+public class HidlSupport {
+ /**
+ * Similar to Objects.deepEquals, but also take care of lists.
+ * Two objects of HIDL types are considered equal if:
+ * 1. Both null
+ * 2. Both non-null, and of the same class, and:
+ * 2.1 Both are primitive arrays / enum arrays, elements are equal using == check
+ * 2.2 Both are object arrays, elements are checked recursively
+ * 2.3 Both are Lists, elements are checked recursively
+ * 2.4 (If both are collections other than lists or maps, throw an error)
+ * 2.5 lft.equals(rgt) returns true
+ */
+ public static boolean deepEquals(Object lft, Object rgt) {
+ if (lft == rgt) {
+ return true;
+ }
+ if (lft == null || rgt == null) {
+ return false;
+ }
+
+ Class<?> lftClazz = lft.getClass();
+ Class<?> rgtClazz = rgt.getClass();
+ if (lftClazz != rgtClazz) {
+ return false;
+ }
+
+ if (lftClazz.isArray()) {
+ Class<?> lftElementType = lftClazz.getComponentType();
+ if (lftElementType != rgtClazz.getComponentType()) {
+ return false;
+ }
+
+ if (lftElementType.isPrimitive()) {
+ return Objects.deepEquals(lft, rgt);
+ }
+
+ Object[] lftArray = (Object[])lft;
+ Object[] rgtArray = (Object[])rgt;
+ return (lftArray.length == rgtArray.length) &&
+ IntStream.range(0, lftArray.length).allMatch(
+ i -> deepEquals(lftArray[i], rgtArray[i]));
+ }
+
+ if (lft instanceof List<?>) {
+ List<Object> lftList = (List<Object>)lft;
+ List<Object> rgtList = (List<Object>)rgt;
+ if (lftList.size() != rgtList.size()) {
+ return false;
+ }
+
+ Iterator<Object> lftIter = lftList.iterator();
+ return rgtList.stream()
+ .allMatch(rgtElement -> deepEquals(lftIter.next(), rgtElement));
+ }
+
+ throwErrorIfUnsupportedType(lft);
+
+ return lft.equals(rgt);
+ }
+
+ /**
+ * Similar to Arrays.deepHashCode, but also take care of lists.
+ */
+ public static int deepHashCode(Object o) {
+ if (o == null) {
+ return 0;
+ }
+ Class<?> clazz = o.getClass();
+ if (clazz.isArray()) {
+ Class<?> elementType = clazz.getComponentType();
+ if (elementType.isPrimitive()) {
+ return primitiveArrayHashCode(o);
+ }
+ return Arrays.hashCode(Arrays.stream((Object[])o)
+ .mapToInt(element -> deepHashCode(element))
+ .toArray());
+ }
+
+ if (o instanceof List<?>) {
+ return Arrays.hashCode(((List<Object>)o).stream()
+ .mapToInt(element -> deepHashCode(element))
+ .toArray());
+ }
+
+ throwErrorIfUnsupportedType(o);
+
+ return o.hashCode();
+ }
+
+ private static void throwErrorIfUnsupportedType(Object o) {
+ if (o instanceof Collection<?> && !(o instanceof List<?>)) {
+ throw new UnsupportedOperationException(
+ "Cannot check equality on collections other than lists: " +
+ o.getClass().getName());
+ }
+
+ if (o instanceof Map<?, ?>) {
+ throw new UnsupportedOperationException(
+ "Cannot check equality on maps");
+ }
+ }
+
+ private static int primitiveArrayHashCode(Object o) {
+ Class<?> elementType = o.getClass().getComponentType();
+ if (elementType == boolean.class) {
+ return Arrays.hashCode(((boolean[])o));
+ }
+ if (elementType == byte.class) {
+ return Arrays.hashCode(((byte[])o));
+ }
+ if (elementType == char.class) {
+ return Arrays.hashCode(((char[])o));
+ }
+ if (elementType == double.class) {
+ return Arrays.hashCode(((double[])o));
+ }
+ if (elementType == float.class) {
+ return Arrays.hashCode(((float[])o));
+ }
+ if (elementType == int.class) {
+ return Arrays.hashCode(((int[])o));
+ }
+ if (elementType == long.class) {
+ return Arrays.hashCode(((long[])o));
+ }
+ if (elementType == short.class) {
+ return Arrays.hashCode(((short[])o));
+ }
+ // Should not reach here.
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 4d14277..d3adce7 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -83,6 +83,7 @@
* @attr ref android.R.styleable#Preference_shouldDisableView
* @attr ref android.R.styleable#Preference_recycleEnabled
* @attr ref android.R.styleable#Preference_singleLineTitle
+ * @attr ref android.R.styleable#Preference_iconSpaceReserved
*/
public class Preference implements Comparable<Preference> {
/**
@@ -135,6 +136,7 @@
private boolean mParentDependencyMet = true;
private boolean mRecycleEnabled = true;
private boolean mSingleLineTitle = true;
+ private boolean mIconSpaceReserved;
/**
* @see #setShouldDisableView(boolean)
@@ -302,7 +304,11 @@
case com.android.internal.R.styleable.Preference_singleLineTitle:
mSingleLineTitle = a.getBoolean(attr, mSingleLineTitle);
break;
- }
+
+ case com.android.internal.R.styleable.Preference_iconSpaceReserved:
+ mIconSpaceReserved = a.getBoolean(attr, mIconSpaceReserved);
+ break;
+ }
}
a.recycle();
}
@@ -631,7 +637,11 @@
imageView.setImageDrawable(mIcon);
}
}
- imageView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE);
+ if (mIcon != null) {
+ imageView.setVisibility(View.VISIBLE);
+ } else {
+ imageView.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE);
+ }
}
final View imageFrame = view.findViewById(com.android.internal.R.id.icon_frame);
@@ -931,6 +941,25 @@
}
/**
+ * Sets whether to reserve the space of this Preference icon view when no icon is provided.
+ *
+ * @param iconSpaceReserved set {@code true} if the space for the icon view should be reserved
+ */
+ public void setIconSpaceReserved(boolean iconSpaceReserved) {
+ mIconSpaceReserved = iconSpaceReserved;
+ notifyChanged();
+ }
+
+ /**
+ * Gets whether the space this preference icon view is reserved.
+ *
+ * @see #setIconSpaceReserved(boolean)
+ * @return {@code true} if the space of this preference icon view is reserved
+ */
+ public boolean isIconSpaceReserved() {
+ return mIconSpaceReserved;
+ }
+ /**
* Returns a unique ID for this Preference. This ID should be unique across all
* Preference objects in a hierarchy.
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5820211..e2100bd 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7077,6 +7077,15 @@
INSTANT_APP_SETTINGS.add(ENABLED_ACCESSIBILITY_SERVICES);
INSTANT_APP_SETTINGS.add(ACCESSIBILITY_SPEAK_PASSWORD);
INSTANT_APP_SETTINGS.add(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+ INSTANT_APP_SETTINGS.add(ACCESSIBILITY_CAPTIONING_ENABLED);
+ INSTANT_APP_SETTINGS.add(ACCESSIBILITY_CAPTIONING_PRESET);
+ INSTANT_APP_SETTINGS.add(ACCESSIBILITY_CAPTIONING_EDGE_TYPE);
+ INSTANT_APP_SETTINGS.add(ACCESSIBILITY_CAPTIONING_EDGE_COLOR);
+ INSTANT_APP_SETTINGS.add(ACCESSIBILITY_CAPTIONING_LOCALE);
+ INSTANT_APP_SETTINGS.add(ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR);
+ INSTANT_APP_SETTINGS.add(ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR);
+ INSTANT_APP_SETTINGS.add(ACCESSIBILITY_CAPTIONING_TYPEFACE);
+ INSTANT_APP_SETTINGS.add(ACCESSIBILITY_CAPTIONING_FONT_SCALE);
INSTANT_APP_SETTINGS.add(DEFAULT_INPUT_METHOD);
INSTANT_APP_SETTINGS.add(ENABLED_INPUT_METHODS);
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 70f9bdd..14d3ad7 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -22,13 +22,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
+import android.net.Uri;
import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import java.io.IOException;
import java.lang.annotation.Retention;
-import java.util.Arrays;
/**
@@ -44,20 +42,6 @@
}
/**
- * For duplicating file descriptors.
- *
- * Note that this copy constructor can not be usable for deep copy.
- * @hide
- */
- public FontConfig(@NonNull FontConfig config) {
- mFamilies = new Family[config.mFamilies.length];
- for (int i = 0; i < config.mFamilies.length; ++i) {
- mFamilies[i] = new Family(config.mFamilies[i]);
- }
- mAliases = Arrays.copyOf(config.mAliases, config.mAliases.length);
- }
-
- /**
* Returns the ordered list of families included in the system fonts.
*/
public @NonNull Family[] getFamilies() {
@@ -174,7 +158,7 @@
private final @NonNull Axis[] mAxes;
private final int mWeight;
private final boolean mIsItalic;
- private @Nullable ParcelFileDescriptor mFd;
+ private Uri mUri;
/**
* @hide
@@ -186,29 +170,6 @@
mAxes = axes;
mWeight = weight;
mIsItalic = isItalic;
- mFd = null;
- }
-
- /**
- * This is for duplicating FileDescriptors.
- *
- * Note that this copy ctor doesn't deep copy the members.
- *
- * @hide
- */
- public Font(Font origin) {
- mFontName = origin.mFontName;
- mTtcIndex = origin.mTtcIndex;
- mAxes = origin.mAxes;
- mWeight = origin.mWeight;
- mIsItalic = origin.mIsItalic;
- if (origin.mFd != null) {
- try {
- mFd = origin.mFd.dup();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
}
/**
@@ -247,17 +208,20 @@
}
/**
- * Returns a file descriptor to access the specified font. This should be closed after use.
+ * Returns the content uri associated to this font.
+ *
+ * You can reach to the font contents by calling {@link
+ * android.content.ContentResolver#openInputStream}.
*/
- public @Nullable ParcelFileDescriptor getFd() {
- return mFd;
+ public @Nullable Uri getUri() {
+ return mUri;
}
/**
* @hide
*/
- public void setFd(@NonNull ParcelFileDescriptor fd) {
- mFd = fd;
+ public void setUri(@NonNull Uri uri) {
+ mUri = uri;
}
/**
@@ -269,11 +233,7 @@
mAxes = in.createTypedArray(Axis.CREATOR);
mWeight = in.readInt();
mIsItalic = in.readInt() == 1;
- if (in.readInt() == 1) { /* has FD */
- mFd = ParcelFileDescriptor.CREATOR.createFromParcel(in);
- } else {
- mFd = null;
- }
+ mUri = in.readTypedObject(Uri.CREATOR);
}
@Override
@@ -283,10 +243,7 @@
out.writeTypedArray(mAxes, flag);
out.writeInt(mWeight);
out.writeInt(mIsItalic ? 1 : 0);
- out.writeInt(mFd == null ? 0 : 1);
- if (mFd != null) {
- mFd.writeToParcel(out, flag);
- }
+ out.writeTypedObject(mUri, flag);
}
@Override
@@ -425,22 +382,6 @@
}
/**
- * For duplicating file descriptor underlying Font object.
- *
- * This copy constructor is not for deep copying.
- * @hide
- */
- public Family(Family origin) {
- mName = origin.mName;
- mLanguage = origin.mLanguage;
- mVariant = origin.mVariant;
- mFonts = new Font[origin.mFonts.length];
- for (int i = 0; i < origin.mFonts.length; ++i) {
- mFonts[i] = new Font(origin.mFonts[i]);
- }
- }
-
- /**
* Returns the name given by the system to this font family.
*/
public @Nullable String getName() {
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 61c9201..ae1ee42 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.SparseArray;
@@ -88,8 +89,9 @@
private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
View next = null;
+ ViewGroup effectiveRoot = getEffectiveRoot(root, focused);
if (focused != null) {
- next = findNextUserSpecifiedFocus(root, focused, direction);
+ next = findNextUserSpecifiedFocus(effectiveRoot, focused, direction);
}
if (next != null) {
return next;
@@ -97,9 +99,9 @@
ArrayList<View> focusables = mTempList;
try {
focusables.clear();
- root.addFocusables(focusables, direction);
+ effectiveRoot.addFocusables(focusables, direction);
if (!focusables.isEmpty()) {
- next = findNextFocus(root, focused, focusedRect, direction, focusables);
+ next = findNextFocus(effectiveRoot, focused, focusedRect, direction, focusables);
}
} finally {
focusables.clear();
@@ -108,6 +110,35 @@
}
/**
+ * Returns the "effective" root of a view. The "effective" root is the closest ancestor
+ * within-which focus should cycle.
+ * <p>
+ * For example: normal focus navigation would stay within a ViewGroup marked as
+ * touchscreenBlocksFocus and keyboardNavigationCluster until a cluster-jump out.
+ * @return the "effective" root of {@param focused}
+ */
+ private ViewGroup getEffectiveRoot(ViewGroup root, View focused) {
+ if (focused == null) {
+ return root;
+ }
+ ViewParent effective = focused.getParent();
+ do {
+ if (effective == root) {
+ return root;
+ }
+ ViewGroup vg = (ViewGroup) effective;
+ if (vg.getTouchscreenBlocksFocus()
+ && focused.getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TOUCHSCREEN)
+ && vg.isKeyboardNavigationCluster()) {
+ return vg;
+ }
+ effective = effective.getParent();
+ } while (effective != null);
+ return root;
+ }
+
+ /**
* Find the root of the next keyboard navigation cluster after the current one.
* @param root The view tree to look inside. Cannot be null
* @param currentCluster The starting point of the search. Null means the default cluster
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index e590739..076b33c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -137,7 +137,10 @@
} break;
case DRAW_FINISHED_MSG: {
mDrawFinished = true;
- invalidate();
+ if (mAttachedToWindow) {
+ notifyDrawFinished();
+ invalidate();
+ }
} break;
}
}
@@ -188,9 +191,12 @@
private Translator mTranslator;
private boolean mGlobalListenersAdded;
+ private boolean mAttachedToWindow;
private int mSurfaceFlags = SurfaceControl.HIDDEN;
+ private int mPendingReportDraws;
+
public SurfaceView(Context context) {
this(context, null);
}
@@ -227,6 +233,7 @@
mViewVisibility = getVisibility() == VISIBLE;
mRequestedVisible = mViewVisibility && mWindowVisibility;
+ mAttachedToWindow = true;
if (!mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
observer.addOnScrollChangedListener(mScrollChangedListener);
@@ -261,8 +268,17 @@
updateSurface();
}
+ void notifyDrawFinished() {
+ ViewRootImpl viewRoot = getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.pendingDrawFinished();
+ }
+ mPendingReportDraws--;
+ }
+
@Override
protected void onDetachedFromWindow() {
+ mAttachedToWindow = false;
if (mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
observer.removeOnScrollChangedListener(mScrollChangedListener);
@@ -270,6 +286,10 @@
mGlobalListenersAdded = false;
}
+ while (mPendingReportDraws > 0) {
+ notifyDrawFinished();
+ }
+
mRequestedVisible = false;
updateSurface();
@@ -618,6 +638,9 @@
if (callbacks == null) {
callbacks = getSurfaceCallbacks();
}
+
+ mPendingReportDraws++;
+ viewRoot.drawPending();
SurfaceCallbackHelper sch =
new SurfaceCallbackHelper(this::onDrawFinished);
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3f52a9d..e924f77 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -61,6 +61,7 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManagerGlobal;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -4170,14 +4171,14 @@
/**
* When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to
* request read access to the content URI(s) contained in the {@link ClipData} object.
- * @see android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
+ * @see android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION
*/
public static final int DRAG_FLAG_GLOBAL_URI_READ = Intent.FLAG_GRANT_READ_URI_PERMISSION;
/**
* When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to
* request write access to the content URI(s) contained in the {@link ClipData} object.
- * @see android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ * @see android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION
*/
public static final int DRAG_FLAG_GLOBAL_URI_WRITE = Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
@@ -4185,8 +4186,8 @@
* When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link
* #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant can be persisted across device
* reboots until explicitly revoked with
- * {@link android.content.Context#revokeUriPermission(Uri,int) Context.revokeUriPermission}.
- * @see android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ * {@link android.content.Context#revokeUriPermission(Uri, int)} Context.revokeUriPermission}.
+ * @see android.content.Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION
*/
public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION =
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
@@ -4195,7 +4196,7 @@
* When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link
* #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant applies to any URI that is a prefix
* match against the original granted URI.
- * @see android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
+ * @see android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION
*/
public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION =
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
@@ -7895,7 +7896,7 @@
* @param arguments A {@link Bundle} holding any arguments relevant for this request. May be
* {@code null} if the service provided no arguments.
*
- * @see AccessibilityNodeInfo#setExtraAvailableData
+ * @see AccessibilityNodeInfo#setAvailableExtraData(List)
*/
public void addExtraDataToAccessibilityNodeInfo(
@NonNull AccessibilityNodeInfo info, @NonNull String extraDataKey,
@@ -9839,7 +9840,7 @@
* Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a
* window or serves as a target of cluster navigation.
*
- * @see #restoreDefaultFocus(int)
+ * @see #restoreDefaultFocus()
*
* @return {@code true} if this view is the default-focus view, {@code false} otherwise
* @attr ref android.R.styleable#View_focusedByDefault
@@ -9859,7 +9860,7 @@
* @param isFocusedByDefault {@code true} to set this view as the default-focus view,
* {@code false} otherwise.
*
- * @see #restoreDefaultFocus(int)
+ * @see #restoreDefaultFocus()
*
* @attr ref android.R.styleable#View_focusedByDefault
*/
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7921938..c250226 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1190,7 +1190,8 @@
final int focusableCount = views.size();
final int descendantFocusability = getDescendantFocusability();
- final boolean focusSelf = (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen());
+ final boolean blockFocusForTouchscreen = shouldBlockFocusForTouchscreen();
+ final boolean focusSelf = (isFocusableInTouchMode() || !blockFocusForTouchscreen);
if (descendantFocusability == FOCUS_BLOCK_DESCENDANTS) {
if (focusSelf) {
@@ -1199,7 +1200,7 @@
return;
}
- if (shouldBlockFocusForTouchscreen()) {
+ if (blockFocusForTouchscreen) {
focusableMode |= FOCUSABLES_TOUCH_MODE;
}
@@ -1234,7 +1235,19 @@
public void addKeyboardNavigationClusters(Collection<View> views, int direction) {
final int focusableCount = views.size();
- super.addKeyboardNavigationClusters(views, direction);
+ if (isKeyboardNavigationCluster()) {
+ // Cluster-navigation can enter a touchscreenBlocksFocus cluster, so temporarily
+ // disable touchscreenBlocksFocus to evaluate whether it contains focusables.
+ final boolean blockedFocus = getTouchscreenBlocksFocus();
+ try {
+ setTouchscreenBlocksFocusNoRefocus(false);
+ super.addKeyboardNavigationClusters(views, direction);
+ } finally {
+ setTouchscreenBlocksFocusNoRefocus(blockedFocus);
+ }
+ } else {
+ super.addKeyboardNavigationClusters(views, direction);
+ }
if (focusableCount != views.size()) {
// No need to look for groups inside a group.
@@ -1280,6 +1293,14 @@
}
}
+ private void setTouchscreenBlocksFocusNoRefocus(boolean touchscreenBlocksFocus) {
+ if (touchscreenBlocksFocus) {
+ mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
+ } else {
+ mGroupFlags &= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
+ }
+ }
+
/**
* Check whether this ViewGroup should ignore focus requests for itself and its children.
*/
@@ -1288,8 +1309,12 @@
}
boolean shouldBlockFocusForTouchscreen() {
+ // There is a special case for keyboard-navigation clusters. We allow cluster navigation
+ // to jump into blockFocusForTouchscreen ViewGroups which are clusters. Once in the
+ // cluster, focus is free to move around within it.
return getTouchscreenBlocksFocus() &&
- mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
+ && (!hasFocus() || !isKeyboardNavigationCluster());
}
@Override
@@ -3175,6 +3200,21 @@
@TestApi
@Override
public boolean restoreFocusInCluster(@FocusRealDirection int direction) {
+ // Allow cluster-navigation to enter touchscreenBlocksFocus ViewGroups.
+ if (isKeyboardNavigationCluster()) {
+ final boolean blockedFocus = getTouchscreenBlocksFocus();
+ try {
+ setTouchscreenBlocksFocusNoRefocus(false);
+ return restoreFocusInClusterInternal(direction);
+ } finally {
+ setTouchscreenBlocksFocusNoRefocus(blockedFocus);
+ }
+ } else {
+ return restoreFocusInClusterInternal(direction);
+ }
+ }
+
+ private boolean restoreFocusInClusterInternal(@FocusRealDirection int direction) {
if (mFocusedInCluster != null && !mFocusedInCluster.isKeyboardNavigationCluster()
&& getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
&& (mFocusedInCluster.mViewFlags & VISIBILITY_MASK) == VISIBLE
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cf52c60..a43b13e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -28,6 +28,7 @@
import android.Manifest;
import android.animation.LayoutTransition;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ResourcesManager;
@@ -210,8 +211,11 @@
/**
* Always assign focus if a focusable View is available.
+ *
+ * @hide
*/
- private static boolean sAlwaysAssignFocus;
+ @TestApi
+ public static boolean sAlwaysAssignFocus;
/**
* This list must only be modified by the main thread, so a lock is only needed when changing
@@ -2704,8 +2708,40 @@
}
}
- private void onDrawFinished() {
+ /**
+ * A count of the number of calls to pendingDrawFinished we
+ * require to notify the WM drawing is complete.
+ *
+ * This starts at 1, for the ViewRootImpl surface itself.
+ * Subsurfaces may debt the value with drawPending.
+ */
+ int mDrawsNeededToReport = 1;
+
+ /**
+ * Delay notifying WM of draw finished until
+ * a balanced call to pendingDrawFinished.
+ */
+ void drawPending() {
+ mDrawsNeededToReport++;
+ }
+
+ void pendingDrawFinished() {
+ if (mDrawsNeededToReport == 0) {
+ throw new RuntimeException("Unbalanced drawPending/pendingDrawFinished calls");
+ }
+ mDrawsNeededToReport--;
+ if (mDrawsNeededToReport == 0) {
+ reportDrawFinished();
+ }
+ }
+
+ private void postDrawFinished() {
+ mHandler.sendEmptyMessage(MSG_DRAW_FINISHED);
+ }
+
+ private void reportDrawFinished() {
try {
+ mDrawsNeededToReport = 1;
mWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
// Have fun!
@@ -2762,15 +2798,12 @@
}
if (mSurfaceHolder != null && mSurface.isValid()) {
- SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::onDrawFinished);
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
} else {
- try {
- mWindowSession.finishDrawing(mWindow);
- } catch (RemoteException e) {
- }
+ pendingDrawFinished();
}
}
}
@@ -3576,6 +3609,7 @@
private final static int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26;
private final static int MSG_UPDATE_POINTER_ICON = 27;
private final static int MSG_POINTER_CAPTURE_CHANGED = 28;
+ private final static int MSG_DRAW_FINISHED = 29;
final class ViewRootHandler extends Handler {
@Override
@@ -3627,6 +3661,8 @@
return "MSG_UPDATE_POINTER_ICON";
case MSG_POINTER_CAPTURE_CHANGED:
return "MSG_POINTER_CAPTURE_CHANGED";
+ case MSG_DRAW_FINISHED:
+ return "MSG_DRAW_FINISHED";
}
return super.getMessageName(message);
}
@@ -3902,6 +3938,9 @@
final boolean hasCapture = msg.arg1 != 0;
handlePointerCaptureChanged(hasCapture);
} break;
+ case MSG_DRAW_FINISHED: {
+ pendingDrawFinished();
+ } break;
}
}
}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index 35c9a29..5487965 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -44,8 +44,6 @@
private final Object mLangIdLock = new Object();
private final Context mContext;
- // TODO: Implement a way to close the file descriptors.
- private ParcelFileDescriptor mSmartSelectionFd;
private ParcelFileDescriptor mLangIdFd;
private TextClassifier mDefault;
private LangId mLangId;
@@ -61,15 +59,7 @@
public TextClassifier getDefaultTextClassifier() {
synchronized (mTextClassifierLock) {
if (mDefault == null) {
- try {
- mSmartSelectionFd = ParcelFileDescriptor.open(
- new File("/etc/textclassifier/textclassifier.smartselection.en.model"),
- ParcelFileDescriptor.MODE_READ_ONLY);
- mDefault = new TextClassifierImpl(mContext, mSmartSelectionFd);
- } catch (FileNotFoundException e) {
- Log.e(LOG_TAG, "Error accessing 'text classifier selection' model file.", e);
- mDefault = TextClassifier.NO_OP;
- }
+ mDefault = new TextClassifierImpl(mContext);
}
return mDefault;
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 66a62c3..f634a1b 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -38,17 +38,24 @@
import android.util.Patterns;
import android.view.View;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
+import java.util.StringJoiner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Default implementation of the {@link TextClassifier} interface.
@@ -62,16 +69,21 @@
final class TextClassifierImpl implements TextClassifier {
private static final String LOG_TAG = "TextClassifierImpl";
-
- private final Object mSmartSelectionLock = new Object();
+ private static final String MODEL_DIR = "/etc/textclassifier/";
+ private static final String MODEL_FILE_REGEX = "textclassifier\\.smartselection\\.(.*)\\.model";
private final Context mContext;
- private final ParcelFileDescriptor mFd;
+
+ private final Object mSmartSelectionLock = new Object();
+ @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
+ private Map<Locale, String> mModelFilePaths;
+ @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
+ private Locale mLocale;
+ @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
private SmartSelection mSmartSelection;
- TextClassifierImpl(Context context, ParcelFileDescriptor fd) {
+ TextClassifierImpl(Context context) {
mContext = Preconditions.checkNotNull(context);
- mFd = Preconditions.checkNotNull(fd);
}
@Override
@@ -81,15 +93,16 @@
validateInput(text, selectionStartIndex, selectionEndIndex);
try {
if (text.length() > 0) {
+ final SmartSelection smartSelection = getSmartSelection(defaultLocales);
final String string = text.toString();
- final int[] startEnd = getSmartSelection()
- .suggest(string, selectionStartIndex, selectionEndIndex);
+ final int[] startEnd = smartSelection.suggest(
+ string, selectionStartIndex, selectionEndIndex);
final int start = startEnd[0];
final int end = startEnd[1];
if (start >= 0 && end <= string.length() && start <= end) {
final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
final SmartSelection.ClassificationResult[] results =
- getSmartSelection().classifyText(
+ smartSelection.classifyText(
string, start, end,
getHintFlags(string, start, end));
final int size = results.length;
@@ -120,7 +133,7 @@
try {
if (text.length() > 0) {
final String string = text.toString();
- SmartSelection.ClassificationResult[] results = getSmartSelection()
+ SmartSelection.ClassificationResult[] results = getSmartSelection(defaultLocales)
.classifyText(string, startIndex, endIndex,
getHintFlags(string, startIndex, endIndex));
if (results.length > 0) {
@@ -147,7 +160,7 @@
Preconditions.checkArgument(text != null);
try {
return LinksInfoFactory.create(
- mContext, getSmartSelection(), text.toString(), linkMask);
+ mContext, getSmartSelection(defaultLocales), text.toString(), linkMask);
} catch (Throwable t) {
// Avoid throwing from this method. Log the error.
Log.e(LOG_TAG, "Error getting links info.", t);
@@ -156,15 +169,69 @@
return TextClassifier.NO_OP.getLinks(text, linkMask, defaultLocales);
}
- private SmartSelection getSmartSelection() throws FileNotFoundException {
+ private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
synchronized (mSmartSelectionLock) {
- if (mSmartSelection == null) {
- mSmartSelection = new SmartSelection(mFd.getFd());
+ localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
+ final Locale locale = findBestSupportedLocaleLocked(localeList);
+ if (mSmartSelection == null || !Objects.equals(mLocale, locale)) {
+ destroySmartSelectionIfExistsLocked();
+ mSmartSelection = new SmartSelection(
+ ParcelFileDescriptor.open(
+ // findBestSupportedLocaleLocked should have initialized
+ // mModelFilePaths
+ new File(mModelFilePaths.get(locale)),
+ ParcelFileDescriptor.MODE_READ_ONLY)
+ .getFd());
+ mLocale = locale;
}
return mSmartSelection;
}
}
+ @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
+ private void destroySmartSelectionIfExistsLocked() {
+ if (mSmartSelection != null) {
+ mSmartSelection.close();
+ mSmartSelection = null;
+ }
+ }
+
+ @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
+ @Nullable
+ private Locale findBestSupportedLocaleLocked(LocaleList localeList) {
+ final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(
+ new StringJoiner(",")
+ // Specified localeList takes priority over the system default
+ .add(localeList.toLanguageTags())
+ .add(LocaleList.getDefault().toLanguageTags())
+ .toString());
+ return Locale.lookup(languageRangeList, loadModelFilePathsLocked().keySet());
+ }
+
+ @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
+ private Map<Locale, String> loadModelFilePathsLocked() {
+ if (mModelFilePaths == null) {
+ final Map<Locale, String> modelFilePaths = new HashMap<>();
+ final File modelsDir = new File(MODEL_DIR);
+ if (modelsDir.exists() && modelsDir.isDirectory()) {
+ final File[] models = modelsDir.listFiles();
+ final Pattern modelFilenamePattern = Pattern.compile(MODEL_FILE_REGEX);
+ final int size = models.length;
+ for (int i = 0; i < size; i++) {
+ final File modelFile = models[i];
+ final Matcher matcher = modelFilenamePattern.matcher(modelFile.getName());
+ if (matcher.matches() && modelFile.isFile()) {
+ final String language = matcher.group(1);
+ final Locale locale = Locale.forLanguageTag(language);
+ modelFilePaths.put(locale, modelFile.getAbsolutePath());
+ }
+ }
+ }
+ mModelFilePaths = modelFilePaths;
+ }
+ return mModelFilePaths;
+ }
+
private TextClassificationResult createClassificationResult(
SmartSelection.ClassificationResult[] classifications, CharSequence text) {
final TextClassificationResult.Builder builder = new TextClassificationResult.Builder()
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index c2b4138..bc49123 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.Widget;
import android.content.Context;
@@ -58,6 +59,7 @@
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
+import android.view.textclassifier.TextClassifier;
import android.widget.AbsoluteLayout;
import java.io.BufferedWriter;
@@ -2249,6 +2251,23 @@
public boolean getRendererPriorityWaivedWhenNotVisible() {
return mProvider.getRendererPriorityWaivedWhenNotVisible();
}
+
+ /**
+ * Sets the {@link TextClassifier} for this WebView.
+ */
+ public void setTextClassifier(@Nullable TextClassifier textClassifier) {
+ mProvider.setTextClassifier(textClassifier);
+ }
+
+ /**
+ * Returns the {@link TextClassifier} used by this WebView.
+ * If no TextClassifier has been set, this WebView uses the default set by the system.
+ */
+ @NonNull
+ public TextClassifier getTextClassifier() {
+ return mProvider.getTextClassifier();
+ }
+
//-------------------------------------------------------------------------
// Interface for WebView providers
//-------------------------------------------------------------------------
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 5724a9b..aa1ffa2 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.res.Configuration;
import android.content.Intent;
@@ -41,6 +43,7 @@
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
+import android.view.textclassifier.TextClassifier;
import android.webkit.WebView.HitTestResult;
import android.webkit.WebView.PictureListener;
import android.webkit.WebView.VisualStateCallback;
@@ -275,6 +278,12 @@
public boolean getRendererPriorityWaivedWhenNotVisible();
+ @SuppressWarnings("unused")
+ public default void setTextClassifier(@Nullable TextClassifier textClassifier) {}
+
+ @NonNull
+ public default TextClassifier getTextClassifier() { return TextClassifier.NO_OP; }
+
//-------------------------------------------------------------------------
// Provider internal methods
//-------------------------------------------------------------------------
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1937187..99b91bd 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6873,9 +6873,11 @@
mTransientStateViews.put(position, scrap);
} else {
// Otherwise, we'll have to remove the view and start over.
+ clearScrapForRebind(scrap);
getSkippedScrap().add(scrap);
}
} else {
+ clearScrapForRebind(scrap);
if (mViewTypeCount == 1) {
mCurrentScrap.add(scrap);
} else {
@@ -7098,12 +7100,12 @@
}
} else if (params.scrappedFromPosition == position) {
final View scrap = scrapViews.remove(i);
- clearAccessibilityFromScrap(scrap);
+ clearScrapForRebind(scrap);
return scrap;
}
}
final View scrap = scrapViews.remove(size - 1);
- clearAccessibilityFromScrap(scrap);
+ clearScrapForRebind(scrap);
return scrap;
} else {
return null;
@@ -7117,7 +7119,7 @@
}
}
- private void clearAccessibilityFromScrap(View view) {
+ private void clearScrapForRebind(View view) {
view.clearAccessibilityFocus();
view.setAccessibilityDelegate(null);
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 1c0c4ef..12e35a1 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1639,7 +1639,7 @@
final View focusChild = getAccessibilityFocusedChild(focusHost);
if (focusChild != null) {
if (!dataChanged || isDirectChildHeaderOrFooter(focusChild)
- || focusChild.hasTransientState() || mAdapterHasStableIds) {
+ || (focusChild.hasTransientState() && mAdapterHasStableIds)) {
// The views won't be changing, so try to maintain
// focus on the current host and virtual view.
accessibilityFocusLayoutRestoreView = focusHost;
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 2eb50e0..bf3085d 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -343,6 +343,9 @@
final ViewGroup vgParent = (ViewGroup) parent;
if (vgParent.isKeyboardNavigationCluster()) {
setKeyboardNavigationCluster(false);
+ if (vgParent.getTouchscreenBlocksFocus()) {
+ setTouchscreenBlocksFocus(false);
+ }
break;
}
parent = vgParent.getParent();
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index edcbb2b..4c1d6cb 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -35,6 +35,7 @@
android:layout_alignParentTop="true"
style="?attr/actionBarStyle"
android:transitionName="android:action_bar"
+ android:touchscreenBlocksFocus="true"
android:keyboardNavigationCluster="true"
android:gravity="top">
<com.android.internal.widget.ActionBarView
@@ -54,6 +55,7 @@
android:layout_height="wrap_content"
style="?attr/actionBarSplitStyle"
android:visibility="gone"
+ android:touchscreenBlocksFocus="true"
android:keyboardNavigationCluster="true"
android:gravity="center"/>
</com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout/screen_toolbar.xml b/core/res/res/layout/screen_toolbar.xml
index 0bec8c4..ded2527 100644
--- a/core/res/res/layout/screen_toolbar.xml
+++ b/core/res/res/layout/screen_toolbar.xml
@@ -35,6 +35,7 @@
android:layout_alignParentTop="true"
style="?attr/actionBarStyle"
android:transitionName="android:action_bar"
+ android:touchscreenBlocksFocus="true"
android:keyboardNavigationCluster="true"
android:gravity="top">
<Toolbar
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 995f2c3..a3b2705 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7226,6 +7226,10 @@
<!-- Whether to use single line for the preference title text. By default, preference title
will be constrained to one line, so the default value of this attribute is true. -->
<attr name="singleLineTitle" format="boolean" />
+ <!-- Whether the space for the preference icon view will be reserved. By default, preference
+ icon view visibility will be set to GONE when there is no icon provided, so the default
+ value of this attribute is false. -->
+ <attr name="iconSpaceReserved" format="boolean" />
</declare-styleable>
<!-- Base attributes available to CheckBoxPreference. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 876d44d..6e20208 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2813,6 +2813,7 @@
<public name="iconTint" />
<public name="iconTintMode" />
<public name="maxAspectRatio"/>
+ <public name="iconSpaceReserved"/>
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 25873d2..7dac18d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1249,6 +1249,7 @@
<item name="collapseContentDescription">@string/toolbar_collapse_description</item>
<item name="contentInsetStart">16dp</item>
<item name="contentInsetStartWithNavigation">@dimen/action_bar_content_inset_with_nav</item>
+ <item name="touchscreenBlocksFocus">true</item>
<item name="keyboardNavigationCluster">true</item>
</style>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 344f3c8..5e3488c 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -181,6 +181,10 @@
<allow-in-power-save package="com.android.cellbroadcastreceiver" />
<allow-in-power-save package="com.android.shell" />
+ <!-- Package in charge of provisioning that needs to freely run in the background -->
+ <!-- STOPSHIP: Revert this once it is fixed properly -->
+ <allow-in-power-save package="com.android.managedprovisioning" />
+
<!-- These are the packages that are white-listed to be able to run as system user -->
<system-user-whitelisted-app package="com.android.settings" />
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index b78df34..ff9f11d 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -189,9 +189,8 @@
skip(parser);
}
}
- String fullFilename = "/system/fonts/" +
- FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
- return new FontConfig.Font(fullFilename, index,
+ String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
+ return new FontConfig.Font(sanitizedName, index,
axes.toArray(new FontConfig.Axis[axes.size()]), weight, isItalic);
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 228d950..8c3a2e8 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1003,21 +1003,22 @@
Map<String, ByteBuffer> bufferForPath) {
FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
for (FontConfig.Font font : family.getFonts()) {
- ByteBuffer fontBuffer = bufferForPath.get(font.getFontName());
+ String fullPathName = "/system/fonts/" + font.getFontName();
+ ByteBuffer fontBuffer = bufferForPath.get(fullPathName);
if (fontBuffer == null) {
- try (FileInputStream file = new FileInputStream(font.getFontName())) {
+ try (FileInputStream file = new FileInputStream(fullPathName)) {
FileChannel fileChannel = file.getChannel();
long fontSize = fileChannel.size();
fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
- bufferForPath.put(font.getFontName(), fontBuffer);
+ bufferForPath.put(fullPathName, fontBuffer);
} catch (IOException e) {
- Log.e(TAG, "Error mapping font file " + font.getFontName());
+ Log.e(TAG, "Error mapping font file " + fullPathName);
continue;
}
}
if (!fontFamily.addFontFromBuffer(fontBuffer, font.getTtcIndex(), font.getAxes(),
font.getWeight(), font.isItalic() ? Builder.ITALIC : Builder.NORMAL)) {
- Log.e(TAG, "Error creating font " + font.getFontName() + "#" + font.getTtcIndex());
+ Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex());
}
}
fontFamily.freeze();
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 621d8c0..bd7b804 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -416,7 +416,9 @@
ATRACE_CALL();
std::unique_ptr<LoadedPackage> loaded_package{new LoadedPackage()};
- const ResTable_package* header = chunk.header<ResTable_package>();
+ constexpr size_t kMinPackageSize =
+ sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset);
+ const ResTable_package* header = chunk.header<ResTable_package, kMinPackageSize>();
if (header == nullptr) {
LOG(ERROR) << "Chunk RES_TABLE_PACKAGE_TYPE is too small.";
return {};
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index ce58a9c..77a82ec 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -912,6 +912,8 @@
return USAGE_UNKNOWN;
}
}
+
+ // TODO remove, replaced by non-static API getVolumeControlStream()
/**
* Returns the stream type matching the given attributes for volume control.
* Use this method to derive the stream type needed to configure the volume
@@ -925,6 +927,7 @@
* the attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct
* match. Note that <code>USE_DEFAULT_STREAM_TYPE</code> is not a valid value
* for {@link AudioManager#setStreamVolume(int, int, int)}.
+ * @deprecated use {@link #getVolumeControlStream()}
*/
public static int getVolumeControlStream(@NonNull AudioAttributes aa) {
if (aa == null) {
@@ -934,6 +937,24 @@
}
/**
+ * Returns the stream type matching this {@code AudioAttributes} instance for volume control.
+ * Use this method to derive the stream type needed to configure the volume
+ * control slider in an {@link android.app.Activity} with
+ * {@link android.app.Activity#setVolumeControlStream(int)} for playback conducted with these
+ * attributes.
+ * <BR>Do not use this method to set the stream type on an audio player object
+ * (e.g. {@link AudioTrack}, {@link MediaPlayer}) as this is deprecated,
+ * use {@code AudioAttributes} instead.
+ * @return a valid stream type for {@code Activity} or stream volume control that matches
+ * the attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct
+ * match. Note that {@code USE_DEFAULT_STREAM_TYPE} is not a valid value
+ * for {@link AudioManager#setStreamVolume(int, int, int)}.
+ */
+ public int getVolumeControlStream() {
+ return toVolumeStreamType(true /*fromGetVolumeControlStream*/, this);
+ }
+
+ /**
* @hide
* Only use to get which stream type should be used for volume control, NOT for audio playback
* (all audio playback APIs are supposed to take AudioAttributes as input parameters)
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 187e35a..f11a9cd 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -113,12 +113,14 @@
}
// Notify we are done.
mState = STATE_UPDATED;
+ mDocumentInfo.updated = true;
notifyUpdateCompleted();
}
}
} else {
// We always notify after a write.
mState = STATE_UPDATED;
+ mDocumentInfo.updated = true;
notifyUpdateCompleted();
}
runPendingCommand();
@@ -229,6 +231,7 @@
mDocumentInfo, oldAttributes, attributes, preview, mCommandResultCallback);
scheduleCommand(command);
+ mDocumentInfo.updated = false;
mState = STATE_UPDATING;
// If no layout in progress and we don't have all pages - schedule a write.
} else if ((!(mCurrentCommand instanceof LayoutCommand)
@@ -249,6 +252,7 @@
mDocumentInfo.fileProvider, mCommandResultCallback);
scheduleCommand(command);
+ mDocumentInfo.updated = false;
mState = STATE_UPDATING;
} else {
willUpdate = false;
@@ -396,7 +400,7 @@
private void notifyUpdateFailed(CharSequence error) {
if (DEBUG) {
- Log.i(LOG_TAG, "[CALLING] onUpdateCompleted()");
+ Log.i(LOG_TAG, "[CALLING] notifyUpdateFailed()");
}
mUpdateCallbacks.onUpdateFailed(error);
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index f6df995..4cce166 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -491,8 +491,6 @@
setState(STATE_UPDATE_FAILED);
- updateOptionsUi();
-
mPrintedDocument.kill(message);
}
@@ -502,7 +500,6 @@
&& canUpdateDocument() && updateDocument(true)) {
ensurePreviewUiShown();
setState(STATE_CONFIGURING);
- updateOptionsUi();
}
}
@@ -579,7 +576,6 @@
updatePrintPreviewController(document.changed);
setState(STATE_CONFIGURING);
- updateOptionsUi();
} break;
}
}
@@ -600,8 +596,6 @@
}
setState(STATE_UPDATE_FAILED);
-
- updateOptionsUi();
}
@Override
@@ -734,7 +728,6 @@
updateOptionsUi();
} else {
setState(STATE_CREATE_FILE_FAILED);
- updateOptionsUi();
// Calling finish here does not invoke lifecycle callbacks but we
// update the print job in onPause if finishing, hence post a message.
mDestinationSpinner.post(new Runnable() {
@@ -958,12 +951,14 @@
Log.i(LOG_TAG, "[state]" + state);
}
mState = state;
+ updateOptionsUi();
}
} else {
if (DEBUG) {
Log.i(LOG_TAG, "[state]" + state);
}
mState = state;
+ updateOptionsUi();
}
}
@@ -1230,6 +1225,7 @@
final boolean willUpdate = mPrintedDocument.update(mPrintJob.getAttributes(),
pages, preview);
+ updateOptionsUi();
if (willUpdate && !mPrintedDocument.hasLaidOutPages()) {
// When the update is done we update the print preview.
@@ -1254,7 +1250,6 @@
private void cancelPrint() {
setState(STATE_PRINT_CANCELED);
- updateOptionsUi();
mPrintedDocument.cancel(true);
doFinish();
}
@@ -1274,7 +1269,6 @@
private void confirmPrint() {
setState(STATE_PRINT_CONFIRMED);
- updateOptionsUi();
addCurrentPrinterToHistory();
setUserPrinted();
@@ -1629,6 +1623,8 @@
// Always update the summary.
updateSummary();
+ mDestinationSpinner.setEnabled(!isFinalState(mState));
+
if (mState == STATE_PRINT_CONFIRMED
|| mState == STATE_PRINT_COMPLETED
|| mState == STATE_PRINT_CANCELED
@@ -1636,9 +1632,6 @@
|| mState == STATE_CREATE_FILE_FAILED
|| mState == STATE_PRINTER_UNAVAILABLE
|| mState == STATE_UPDATE_SLOW) {
- if (mState != STATE_PRINTER_UNAVAILABLE) {
- mDestinationSpinner.setEnabled(false);
- }
disableOptionsUi(isFinalState(mState));
return;
}
@@ -1927,7 +1920,7 @@
mPrintButton.setImageResource(R.drawable.ic_menu_savetopdf);
mPrintButton.setContentDescription(getString(R.string.savetopdf_button));
}
- if (!mPrintedDocument.getDocumentInfo().laidout
+ if (!mPrintedDocument.getDocumentInfo().updated
||(mRangeOptionsSpinner.getSelectedItemPosition() == 1
&& (TextUtils.isEmpty(mPageRangeEditText.getText()) || hasErrors()))
|| (mRangeOptionsSpinner.getSelectedItemPosition() == 0
@@ -2048,7 +2041,6 @@
updateDocument(false);
}
ensurePreviewUiShown();
- updateOptionsUi();
}
}
@@ -2058,7 +2050,6 @@
mPrintedDocument.cancel(false);
ensureErrorUiShown(getString(R.string.print_error_printer_unavailable),
PrintErrorFragment.ACTION_NONE);
- updateOptionsUi();
}
}
@@ -3038,7 +3029,6 @@
if (mState == STATE_UPDATE_SLOW) {
setState(STATE_UPDATE_SLOW);
ensureProgressUiShown();
- updateOptionsUi();
return;
} else if (mPosted) {
@@ -3080,7 +3070,6 @@
mPreviousState = mState;
setState(STATE_UPDATE_SLOW);
ensureProgressUiShown();
- updateOptionsUi();
}
}
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 28f6877..db3274a 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -124,6 +124,8 @@
<item>aptX</item>
<item>aptX HD</item>
<item>LDAC</item>
+ <item>Enable Optional Codecs</item>
+ <item>Disable Optional Codecs</item>
</string-array>
<!-- Values for Bluetooth Audio Codec selection preference. -->
@@ -134,6 +136,8 @@
<item>2</item>
<item>3</item>
<item>4</item>
+ <item>5</item>
+ <item>6</item>
</string-array>
<!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50]-->
@@ -144,6 +148,8 @@
<item>aptX</item>
<item>aptX HD</item>
<item>LDAC</item>
+ <item>Enable Optional Codecs</item>
+ <item>Disable Optional Codecs</item>
</string-array>
<!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
index 6764a6b..0f443d6 100755
--- a/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/BatteryMeterDrawableBase.java
@@ -101,7 +101,7 @@
for (int i=0; i < N; i++) {
mColors[2 * i] = levels.getInt(i, 0);
if (colors.getType(i) == TypedValue.TYPE_ATTRIBUTE) {
- mColors[2 * i + 1] = Utils.getColorAttr(context, colors.getResourceId(i, 0));
+ mColors[2 * i + 1] = Utils.getColorAttr(context, colors.getThemeAttributeId(i, 0));
} else {
mColors[2 * i + 1] = colors.getColor(i, 0);
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 46726f2..02deb44 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -20,9 +20,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -580,4 +582,40 @@
mWorkerThread.quit();
updateScores();
}
+
+ /**
+ * Verify that tracking a Passpoint AP on a device with Passpoint disabled doesn't cause
+ * any crash.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void trackPasspointApWithPasspointDisabled() throws Exception {
+ WifiTracker tracker = createMockedWifiTracker();
+
+ // Add a Passpoint AP to the scan results.
+ List<ScanResult> results = new ArrayList<>();
+ ScanResult passpointAp = new ScanResult(
+ WifiSsid.createFromAsciiEncoded(SSID_1),
+ BSSID_1,
+ 0, // hessid
+ 0, //anqpDomainId
+ null, // osuProviders
+ "", // capabilities
+ RSSI_1,
+ 0, // frequency
+ SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */);
+ passpointAp.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
+ results.add(passpointAp);
+
+ // Update access point and verify UnsupportedOperationException is being caught for
+ // call to WifiManager#getMatchingWifiConfig.
+ when(mockWifiManager.getConfiguredNetworks())
+ .thenReturn(new ArrayList<WifiConfiguration>());
+ when(mockWifiManager.getScanResults()).thenReturn(results);
+ doThrow(new UnsupportedOperationException())
+ .when(mockWifiManager).getMatchingWifiConfig(any(ScanResult.class));
+ tracker.forceUpdate();
+ verify(mockWifiManager).getMatchingWifiConfig(any(ScanResult.class));
+ }
}
diff --git a/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml b/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml
index d2daa89..a1c1e5b 100644
--- a/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml
+++ b/packages/SystemUI/res/layout/recents_incompatible_app_overlay.xml
@@ -26,6 +26,6 @@
android:layout_gravity="center"
android:drawableTop="@drawable/recents_info_light"
android:drawablePadding="8dp"
- android:text="@string/recents_incompatible_app_message"
+ android:text="@string/dock_non_resizeble_failed_to_dock_text"
android:textColor="@android:color/white" />
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bf17e38..a2ec804 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -789,8 +789,6 @@
<string name="recents_launch_disabled_message"><xliff:g id="app" example="Calendar">%s</xliff:g> is disabled in safe-mode.</string>
<!-- Recents: Stack action button string. [CHAR LIMIT=NONE] -->
<string name="recents_stack_action_button_label">Clear all</string>
- <!-- Recents: Incompatible task message. [CHAR LIMIT=NONE] -->
- <string name="recents_incompatible_app_message">App doesn\'t support split screen</string>
<!-- Recents: Hint text that shows on the drop targets to start multiwindow. [CHAR LIMIT=NONE] -->
<string name="recents_drag_hint_message">Drag here to use split screen</string>
@@ -1869,9 +1867,6 @@
<!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
<string name="pip_menu_title">Picture in picture menu</string>
- <!-- User visible notification channel name for the PiP BTW notification. [CHAR LIMIT=NONE] -->
- <string name="pip_notification_channel_name">Picture-in-picture</string>
-
<!-- PiP BTW notification title. [CHAR LIMIT=50] -->
<string name="pip_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is in picture-in-picture</string>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 6cda076..87f8ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -172,7 +172,8 @@
mInputConsumerController);
mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController,
mInputConsumerController);
- mNotificationController = new PipNotificationController(context, mActivityManager);
+ mNotificationController = new PipNotificationController(context, mActivityManager,
+ mTouchHandler.getMotionHelper());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index ebda2e8..ec80745 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -45,14 +45,12 @@
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
-import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager.LayoutParams;
-import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -61,6 +59,7 @@
import com.android.systemui.R;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -128,8 +127,9 @@
break;
case MESSAGE_UPDATE_ACTIONS: {
final Bundle data = (Bundle) msg.obj;
- setActions(data.getParcelable(EXTRA_STACK_BOUNDS),
- ((ParceledListSlice) data.getParcelable(EXTRA_ACTIONS)).getList());
+ final ParceledListSlice actions = data.getParcelable(EXTRA_ACTIONS);
+ setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null
+ ? actions.getList() : Collections.EMPTY_LIST);
break;
}
case MESSAGE_UPDATE_DISMISS_FRACTION: {
@@ -260,6 +260,7 @@
}
notifyMenuVisibility(true);
updateExpandButtonFromBounds(stackBounds, movementBounds);
+ setDecorViewVisibility(true);
mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
mMenuContainer.getAlpha(), 1f);
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
@@ -300,9 +301,7 @@
if (animationFinishedRunnable != null) {
animationFinishedRunnable.run();
}
- if (getSystemService(AccessibilityManager.class).isEnabled()) {
- finish();
- }
+ setDecorViewVisibility(false);
}
});
mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener);
@@ -411,6 +410,7 @@
}
private void updateDismissFraction(float fraction) {
+ setDecorViewVisibility(true);
int alpha;
if (mMenuVisible) {
mMenuContainer.setAlpha(1-fraction);
@@ -497,4 +497,16 @@
v.removeCallbacks(mFinishRunnable);
v.postDelayed(mFinishRunnable, delay);
}
+
+ /**
+ * Sets the visibility of the root view of the window to disable drawing and touches for the
+ * activity. This differs from {@link Activity#setVisible(boolean)} in that it does not set
+ * the internal mVisibleFromClient state.
+ */
+ private void setDecorViewVisibility(boolean visible) {
+ final View decorView = getWindow().getDecorView();
+ if (decorView != null) {
+ decorView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
index bdd6b65..db83b8b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
@@ -16,15 +16,17 @@
package com.android.systemui.pip.phone;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE;
import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.OnOpChangedListener;
import android.app.IActivityManager;
import android.app.Notification;
-import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -40,6 +42,7 @@
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.util.NotificationChannels;
/**
* Manages the BTW notification that shows whenever an activity enters or leaves picture-in-picture.
@@ -47,64 +50,79 @@
public class PipNotificationController {
private static final String TAG = PipNotificationController.class.getSimpleName();
- private static final String CHANNEL_ID = PipNotificationController.class.getName();
- private static final int BTW_NOTIFICATION_ID = 0;
+ private static final String NOTIFICATION_TAG = PipNotificationController.class.getName();
+ private static final int NOTIFICATION_ID = 0;
private Context mContext;
private IActivityManager mActivityManager;
+ private AppOpsManager mAppOpsManager;
private NotificationManager mNotificationManager;
- public PipNotificationController(Context context, IActivityManager activityManager) {
+ private PipMotionHelper mMotionHelper;
+
+ private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
+ @Override
+ public void onOpChanged(String op, String packageName) {
+ try {
+ // Dismiss the PiP once the user disables the app ops setting for that package
+ final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
+ packageName, 0);
+ if (mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, packageName)
+ != MODE_ALLOWED) {
+ mMotionHelper.dismissPip();
+ }
+ } catch (NameNotFoundException e) {
+ // Unregister the listener if the package can't be found
+ unregisterAppOpsListener();
+ }
+ }
+ };
+
+ public PipNotificationController(Context context, IActivityManager activityManager,
+ PipMotionHelper motionHelper) {
mContext = context;
mActivityManager = activityManager;
+ mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mNotificationManager = NotificationManager.from(context);
- createNotificationChannel();
+ mMotionHelper = motionHelper;
}
public void onActivityPinned(String packageName) {
// Clear any existing notification
- mNotificationManager.cancel(CHANNEL_ID, BTW_NOTIFICATION_ID);
+ mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
// Build a new notification
- final Notification.Builder builder = new Notification.Builder(mContext, CHANNEL_ID)
- .setLocalOnly(true)
- .setOngoing(true)
- .setSmallIcon(R.drawable.pip_notification_icon)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
+ final Notification.Builder builder =
+ new Notification.Builder(mContext, NotificationChannels.GENERAL)
+ .setLocalOnly(true)
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.pip_notification_icon)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color));
if (updateNotificationForApp(builder, packageName)) {
SystemUI.overrideNotificationAppName(mContext, builder);
// Show the new notification
- mNotificationManager.notify(CHANNEL_ID, BTW_NOTIFICATION_ID, builder.build());
+ mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
}
+
+ // Register for changes to the app ops setting for this package while it is in PiP
+ registerAppOpsListener(packageName);
}
public void onActivityUnpinned() {
+ // Unregister for changes to the previously PiP'ed package
+ unregisterAppOpsListener();
+
ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext, mActivityManager);
if (topPipActivity != null) {
onActivityPinned(topPipActivity.getPackageName());
} else {
- mNotificationManager.cancel(CHANNEL_ID, BTW_NOTIFICATION_ID);
+ mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
}
}
/**
- * Create the notification channel for the PiP BTW notifications if necessary.
- */
- private NotificationChannel createNotificationChannel() {
- NotificationChannel channel = mNotificationManager.getNotificationChannel(CHANNEL_ID);
- if (channel == null) {
- channel = new NotificationChannel(CHANNEL_ID,
- mContext.getString(R.string.pip_notification_channel_name), IMPORTANCE_MIN);
- channel.enableLights(false);
- channel.enableVibration(false);
- mNotificationManager.createNotificationChannel(channel);
- }
- return channel;
- }
-
- /**
* Updates the notification builder with app-specific information, returning whether it was
* successful.
*/
@@ -139,4 +157,13 @@
}
return false;
}
+
+ private void registerAppOpsListener(String packageName) {
+ mAppOpsManager.startWatchingMode(OP_PICTURE_IN_PICTURE, packageName,
+ mAppOpsChangedListener);
+ }
+
+ private void unregisterAppOpsListener() {
+ mAppOpsManager.stopWatchingMode(mAppOpsChangedListener);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index d8e5542..596d3bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -209,7 +209,7 @@
for (String tile : defTiles.split(",")) {
tiles.add(tile);
}
- mTileAdapter.setTileSpecs(tiles);
+ mTileAdapter.resetTileSpecs(mHost, tiles);
}
private void setTileSpecs() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index c33d7da..0a0d2ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -114,6 +114,12 @@
mCurrentSpecs = newSpecs;
}
+ public void resetTileSpecs(QSTileHost host, List<String> specs) {
+ // Notify the host so the tiles get removed callbacks.
+ host.changeTiles(mCurrentSpecs, specs);
+ setTileSpecs(specs);
+ }
+
public void setTileSpecs(List<String> currentSpecs) {
if (currentSpecs.equals(mCurrentSpecs)) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 6da8272..d3bd89f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -474,7 +474,7 @@
return true;
} else {
EventBus.getDefault().send(new ShowUserToastEvent(
- R.string.recents_incompatible_app_message, Toast.LENGTH_SHORT));
+ R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
return false;
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index a5f7832..b8be580 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -610,8 +610,8 @@
if (mIncompatibleAppToastView == null) {
mIncompatibleAppToastView = Utilities.findViewStubById(this,
R.id.incompatible_app_toast_stub).inflate();
- TextView msg = (TextView) findViewById(com.android.internal.R.id.message);
- msg.setText(R.string.recents_incompatible_app_message);
+ TextView msg = findViewById(com.android.internal.R.id.message);
+ msg.setText(R.string.dock_non_resizeble_failed_to_dock_text);
}
mIncompatibleAppToastView.setVisibility(View.VISIBLE);
} else if (mIncompatibleAppToastView != null) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
new file mode 100644
index 0000000..6e7d99e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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.systemui.qs.customize;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.qs.QSTileHost;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class TileAdapterTest extends SysuiTestCase {
+
+ private TileAdapter mTileAdapter;
+
+ @Before
+ public void setup() throws Exception {
+ TestableLooper.get(this).runWithLooper(() -> mTileAdapter = new TileAdapter(mContext));
+ }
+
+ @Test
+ public void testResetNotifiesHost() {
+ QSTileHost host = mock(QSTileHost.class);
+ mTileAdapter.resetTileSpecs(host, Collections.emptyList());
+ verify(host).changeTiles(any(), any());
+ }
+}
diff --git a/services/core/java/com/android/server/FontManagerService.java b/services/core/java/com/android/server/FontManagerService.java
index 55a945a..f172647 100644
--- a/services/core/java/com/android/server/FontManagerService.java
+++ b/services/core/java/com/android/server/FontManagerService.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.graphics.FontListParser;
+import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.text.FontConfig;
import android.util.Slog;
@@ -34,6 +35,7 @@
public class FontManagerService extends IFontManager.Stub {
private static final String TAG = "FontManagerService";
private static final String FONTS_CONFIG = "/system/etc/fonts.xml";
+ private static final String SYSTEM_FONT_DIR = "/system/fonts/";
@GuardedBy("mLock")
private FontConfig mConfig;
@@ -63,28 +65,22 @@
public FontConfig getSystemFonts() {
synchronized (mLock) {
if (mConfig != null) {
- return new FontConfig(mConfig);
+ return mConfig;
}
- FontConfig config = loadFromSystem();
- if (config == null) {
+ mConfig = loadFromSystem();
+ if (mConfig == null) {
return null;
}
- for (FontConfig.Family family : config.getFamilies()) {
+ for (FontConfig.Family family : mConfig.getFamilies()) {
for (FontConfig.Font font : family.getFonts()) {
- File fontFile = new File(font.getFontName());
- try {
- font.setFd(ParcelFileDescriptor.open(
- fontFile, ParcelFileDescriptor.MODE_READ_ONLY));
- } catch (IOException e) {
- Slog.e(TAG, "Error opening font file " + font.getFontName(), e);
- }
+ File fontFile = new File(SYSTEM_FONT_DIR, font.getFontName());
+ font.setUri(Uri.fromFile(fontFile));
}
}
- mConfig = config;
- return new FontConfig(mConfig);
+ return mConfig;
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 79be3e6..0ccaf8e 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -114,7 +114,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -201,7 +200,7 @@
private final HashMap<Account, Integer> signinRequiredNotificationIds =
new HashMap<Account, Integer>();
final Object cacheLock = new Object();
- final Object dbLock = new Object();
+ final Object dbLock = new Object(); // if needed, dbLock must be obtained before cacheLock
/** protected by the {@link #cacheLock} */
final HashMap<String, Account[]> accountCache = new LinkedHashMap<>();
/** protected by the {@link #cacheLock} */
@@ -586,13 +585,11 @@
*/
private int getAccountVisibilityFromCache(Account account, String packageName,
UserAccounts accounts) {
- synchronized (accounts.dbLock) {
- synchronized (accounts.cacheLock) {
- Map<String, Integer> accountVisibility =
- getPackagesAndVisibilityForAccountLocked(account, accounts);
- Integer visibility = accountVisibility.get(packageName);
- return visibility != null ? visibility : AccountManager.VISIBILITY_UNDEFINED;
- }
+ synchronized (accounts.cacheLock) {
+ Map<String, Integer> accountVisibility =
+ getPackagesAndVisibilityForAccountLocked(account, accounts);
+ Integer visibility = accountVisibility.get(packageName);
+ return visibility != null ? visibility : AccountManager.VISIBILITY_UNDEFINED;
}
}
@@ -2240,16 +2237,23 @@
long identityToken = clearCallingIdentity();
try {
UserAccounts accounts = getUserAccounts(userId);
+ List<Pair<Account, String>> deletedTokens;
synchronized (accounts.dbLock) {
+ accounts.accountsDb.beginTransaction();
+ try {
+ deletedTokens = invalidateAuthTokenLocked(accounts, accountType, authToken);
+ accounts.accountsDb.setTransactionSuccessful();
+ } finally {
+ accounts.accountsDb.endTransaction();
+ }
synchronized (accounts.cacheLock) {
- accounts.accountsDb.beginTransaction();
- try {
- invalidateAuthTokenLocked(accounts, accountType, authToken);
- invalidateCustomTokenLocked(accounts, accountType, authToken);
- accounts.accountsDb.setTransactionSuccessful();
- } finally {
- accounts.accountsDb.endTransaction();
+ for (Pair<Account, String> tokenInfo : deletedTokens) {
+ Account act = tokenInfo.first;
+ String tokenType = tokenInfo.second;
+ writeAuthTokenIntoCacheLocked(accounts, act, tokenType, null);
}
+ // wipe out cached token in memory.
+ accounts.accountTokenCaches.remove(accountType, authToken);
}
}
} finally {
@@ -2257,38 +2261,24 @@
}
}
- private void invalidateCustomTokenLocked(
- UserAccounts accounts,
- String accountType,
+ private List<Pair<Account, String>> invalidateAuthTokenLocked(UserAccounts accounts, String accountType,
String authToken) {
- if (authToken == null || accountType == null) {
- return;
- }
- // Also wipe out cached token in memory.
- accounts.accountTokenCaches.remove(accountType, authToken);
- }
-
- private void invalidateAuthTokenLocked(UserAccounts accounts, String accountType,
- String authToken) {
- if (authToken == null || accountType == null) {
- return;
- }
+ // TODO Move to AccountsDB
+ List<Pair<Account, String>> results = new ArrayList<>();
Cursor cursor = accounts.accountsDb.findAuthtokenForAllAccounts(accountType, authToken);
+
try {
while (cursor.moveToNext()) {
String authTokenId = cursor.getString(0);
String accountName = cursor.getString(1);
String authTokenType = cursor.getString(2);
accounts.accountsDb.deleteAuthToken(authTokenId);
- writeAuthTokenIntoCacheLocked(
- accounts,
- new Account(accountName, accountType),
- authTokenType,
- null);
+ results.add(Pair.create(new Account(accountName, accountType), authTokenType));
}
} finally {
cursor.close();
}
+ return results;
}
private void saveCachedToken(
@@ -2305,11 +2295,9 @@
}
cancelNotification(getSigninRequiredNotificationId(accounts, account),
UserHandle.of(accounts.userId));
- synchronized (accounts.dbLock) {
- synchronized (accounts.cacheLock) {
- accounts.accountTokenCaches.put(
- account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
- }
+ synchronized (accounts.cacheLock) {
+ accounts.accountTokenCaches.put(
+ account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
}
}
@@ -2321,22 +2309,26 @@
cancelNotification(getSigninRequiredNotificationId(accounts, account),
UserHandle.of(accounts.userId));
synchronized (accounts.dbLock) {
- synchronized (accounts.cacheLock) {
- accounts.accountsDb.beginTransaction();
- try {
- long accountId = accounts.accountsDb.findDeAccountId(account);
- if (accountId < 0) {
- return false;
- }
- accounts.accountsDb.deleteAuthtokensByAccountIdAndType(accountId, type);
- if (accounts.accountsDb.insertAuthToken(accountId, type, authToken) >= 0) {
- accounts.accountsDb.setTransactionSuccessful();
- writeAuthTokenIntoCacheLocked(accounts, account, type, authToken);
- return true;
- }
+ accounts.accountsDb.beginTransaction();
+ boolean updateCache = false;
+ try {
+ long accountId = accounts.accountsDb.findDeAccountId(account);
+ if (accountId < 0) {
return false;
- } finally {
- accounts.accountsDb.endTransaction();
+ }
+ accounts.accountsDb.deleteAuthtokensByAccountIdAndType(accountId, type);
+ if (accounts.accountsDb.insertAuthToken(accountId, type, authToken) >= 0) {
+ accounts.accountsDb.setTransactionSuccessful();
+ updateCache = true;
+ return true;
+ }
+ return false;
+ } finally {
+ accounts.accountsDb.endTransaction();
+ if (updateCache) {
+ synchronized (accounts.cacheLock) {
+ writeAuthTokenIntoCacheLocked(accounts, account, type, authToken);
+ }
}
}
}
@@ -3947,13 +3939,8 @@
@Override
public void run() throws RemoteException {
- synchronized (mAccounts.dbLock) {
- synchronized (mAccounts.cacheLock) {
- mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType,
- mCallingUid,
- mPackageName, false /* include managed not visible*/);
- }
- }
+ mAccountsOfType = getAccountsFromCache(mAccounts, mAccountType,
+ mCallingUid, mPackageName, false /* include managed not visible*/);
// check whether each account matches the requested features
mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
mCurrentAccount = 0;
@@ -4097,18 +4084,14 @@
for (int userId : userIds) {
UserAccounts userAccounts = getUserAccounts(userId);
if (userAccounts == null) continue;
- synchronized (userAccounts.dbLock) {
- synchronized (userAccounts.cacheLock) {
- Account[] accounts = getAccountsFromCacheLocked(
- userAccounts,
- null /* type */,
- Binder.getCallingUid(),
- null /* packageName */,
- false /* include managed not visible*/);
- for (int a = 0; a < accounts.length; a++) {
- runningAccounts.add(new AccountAndUser(accounts[a], userId));
- }
- }
+ Account[] accounts = getAccountsFromCache(
+ userAccounts,
+ null /* type */,
+ Binder.getCallingUid(),
+ null /* packageName */,
+ false /* include managed not visible*/);
+ for (Account account : accounts) {
+ runningAccounts.add(new AccountAndUser(account, userId));
}
}
@@ -4194,24 +4177,20 @@
String callingPackage,
List<String> visibleAccountTypes,
boolean includeUserManagedNotVisible) {
- synchronized (userAccounts.dbLock) {
- synchronized (userAccounts.cacheLock) {
- ArrayList<Account> visibleAccounts = new ArrayList<>();
- for (String visibleType : visibleAccountTypes) {
- Account[] accountsForType = getAccountsFromCacheLocked(
- userAccounts, visibleType, callingUid, callingPackage,
- includeUserManagedNotVisible);
- if (accountsForType != null) {
- visibleAccounts.addAll(Arrays.asList(accountsForType));
- }
- }
- Account[] result = new Account[visibleAccounts.size()];
- for (int i = 0; i < visibleAccounts.size(); i++) {
- result[i] = visibleAccounts.get(i);
- }
- return result;
+ ArrayList<Account> visibleAccounts = new ArrayList<>();
+ for (String visibleType : visibleAccountTypes) {
+ Account[] accountsForType = getAccountsFromCache(
+ userAccounts, visibleType, callingUid, callingPackage,
+ includeUserManagedNotVisible);
+ if (accountsForType != null) {
+ visibleAccounts.addAll(Arrays.asList(accountsForType));
}
}
+ Account[] result = new Account[visibleAccounts.size()];
+ for (int i = 0; i < visibleAccounts.size(); i++) {
+ result[i] = visibleAccounts.get(i);
+ }
+ return result;
}
@Override
@@ -4277,10 +4256,12 @@
public Account[] getSharedAccountsAsUser(int userId) {
userId = handleIncomingUser(userId);
UserAccounts accounts = getUserAccounts(userId);
- List<Account> accountList = accounts.accountsDb.getSharedAccounts();
- Account[] accountArray = new Account[accountList.size()];
- accountList.toArray(accountArray);
- return accountArray;
+ synchronized (accounts.dbLock) {
+ List<Account> accountList = accounts.accountsDb.getSharedAccounts();
+ Account[] accountArray = new Account[accountList.size()];
+ accountList.toArray(accountArray);
+ return accountArray;
+ }
}
@Override
@@ -4360,13 +4341,8 @@
try {
UserAccounts userAccounts = getUserAccounts(userId);
if (features == null || features.length == 0) {
- Account[] accounts;
- synchronized (userAccounts.dbLock) {
- synchronized (userAccounts.cacheLock) {
- accounts = getAccountsFromCacheLocked(
- userAccounts, type, callingUid, opPackageName, false);
- }
- }
+ Account[] accounts = getAccountsFromCache(userAccounts, type, callingUid,
+ opPackageName, false);
Bundle result = new Bundle();
result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
onResult(response, result);
@@ -4922,36 +4898,36 @@
private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
String[] args, boolean isCheckinRequest) {
- synchronized (userAccounts.dbLock) {
- synchronized (userAccounts.cacheLock) {
- if (isCheckinRequest) {
- // This is a checkin request. *Only* upload the account types and the count of
- // each.
- userAccounts.accountsDb.dumpDeAccountsTable(fout);
- } else {
- Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
- Process.SYSTEM_UID, null /* packageName */, false);
- fout.println("Accounts: " + accounts.length);
- for (Account account : accounts) {
- fout.println(" " + account);
- }
+ if (isCheckinRequest) {
+ // This is a checkin request. *Only* upload the account types and the count of
+ // each.
+ synchronized (userAccounts.dbLock) {
+ userAccounts.accountsDb.dumpDeAccountsTable(fout);
+ }
+ } else {
+ Account[] accounts = getAccountsFromCache(userAccounts, null /* type */,
+ Process.SYSTEM_UID, null /* packageName */, false);
+ fout.println("Accounts: " + accounts.length);
+ for (Account account : accounts) {
+ fout.println(" " + account);
+ }
- // Add debug information.
- fout.println();
- userAccounts.accountsDb.dumpDebugTable(fout);
- fout.println();
- synchronized (mSessions) {
- final long now = SystemClock.elapsedRealtime();
- fout.println("Active Sessions: " + mSessions.size());
- for (Session session : mSessions.values()) {
- fout.println(" " + session.toDebugString(now));
- }
- }
-
- fout.println();
- mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
+ // Add debug information.
+ fout.println();
+ synchronized (userAccounts.dbLock) {
+ userAccounts.accountsDb.dumpDebugTable(fout);
+ }
+ fout.println();
+ synchronized (mSessions) {
+ final long now = SystemClock.elapsedRealtime();
+ fout.println("Active Sessions: " + mSessions.size());
+ for (Session session : mSessions.values()) {
+ fout.println(" " + session.toDebugString(now));
}
}
+
+ fout.println();
+ mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
}
}
@@ -5609,12 +5585,20 @@
/*
* packageName can be null. If not null, it should be used to filter out restricted accounts
* that the package is not allowed to access.
+ *
+ * <p>The method shouldn't be called with UserAccounts#cacheLock held, otherwise it will cause a
+ * deadlock
*/
@NonNull
- protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
+ protected Account[] getAccountsFromCache(UserAccounts userAccounts, String accountType,
int callingUid, @Nullable String callingPackage, boolean includeManagedNotVisible) {
+ Preconditions.checkState(!Thread.holdsLock(userAccounts.cacheLock),
+ "Method should not be called with cacheLock");
if (accountType != null) {
- final Account[] accounts = userAccounts.accountCache.get(accountType);
+ Account[] accounts;
+ synchronized (userAccounts.cacheLock) {
+ accounts = userAccounts.accountCache.get(accountType);
+ }
if (accounts == null) {
return EMPTY_ACCOUNT_ARRAY;
} else {
@@ -5623,20 +5607,23 @@
}
} else {
int totalLength = 0;
- for (Account[] accounts : userAccounts.accountCache.values()) {
- totalLength += accounts.length;
+ Account[] accountsArray;
+ synchronized (userAccounts.cacheLock) {
+ for (Account[] accounts : userAccounts.accountCache.values()) {
+ totalLength += accounts.length;
+ }
+ if (totalLength == 0) {
+ return EMPTY_ACCOUNT_ARRAY;
+ }
+ accountsArray = new Account[totalLength];
+ totalLength = 0;
+ for (Account[] accountsOfType : userAccounts.accountCache.values()) {
+ System.arraycopy(accountsOfType, 0, accountsArray, totalLength,
+ accountsOfType.length);
+ totalLength += accountsOfType.length;
+ }
}
- if (totalLength == 0) {
- return EMPTY_ACCOUNT_ARRAY;
- }
- Account[] accounts = new Account[totalLength];
- totalLength = 0;
- for (Account[] accountsOfType : userAccounts.accountCache.values()) {
- System.arraycopy(accountsOfType, 0, accounts, totalLength,
- accountsOfType.length);
- totalLength += accountsOfType.length;
- }
- return filterAccounts(userAccounts, accounts, callingUid, callingPackage,
+ return filterAccounts(userAccounts, accountsArray, callingUid, callingPackage,
includeManagedNotVisible);
}
}
@@ -5669,6 +5656,7 @@
}
}
+ /** protected by the {@code dbLock}, {@code cacheLock} */
protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts,
Account account, String key, String value) {
Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
@@ -5685,6 +5673,14 @@
protected String readAuthTokenInternal(UserAccounts accounts, Account account,
String authTokenType) {
+ // Fast path - check if account is already cached
+ synchronized (accounts.cacheLock) {
+ Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
+ if (authTokensForAccount != null) {
+ return authTokensForAccount.get(authTokenType);
+ }
+ }
+ // If not cached yet - do slow path and sync with db if necessary
synchronized (accounts.dbLock) {
synchronized (accounts.cacheLock) {
Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
diff --git a/services/core/java/com/android/server/fingerprint/EnumerateClient.java b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
index 1b8b89c..34f245f 100644
--- a/services/core/java/com/android/server/fingerprint/EnumerateClient.java
+++ b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
@@ -58,7 +58,7 @@
public int stop(boolean initiatedByClient) {
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
- Slog.w(TAG, "stopEnumeration: no fingerprint HAL!");
+ Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
return ERROR_ESRCH;
}
try {
@@ -102,12 +102,12 @@
@Override
public boolean onEnrollResult(int fingerId, int groupId, int rem) {
if (DEBUG) Slog.w(TAG, "onEnrollResult() called for enumerate!");
- return true; // Invalid for Enumerate.
+ return true; // Invalid for Remove
}
@Override
public boolean onRemoved(int fingerId, int groupId, int remaining) {
if (DEBUG) Slog.w(TAG, "onRemoved() called for enumerate!");
- return true; // Invalid for Enumerate.
+ return true; // Invalid for Authenticate
}
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index b6e7932..7d97ce4 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -85,7 +85,6 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.LinkedList;
/**
* A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -135,20 +134,6 @@
private ClientMonitor mPendingClient;
private PerformanceStats mPerformanceStats;
-
- private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration
- private LinkedList<Integer> mEnumeratingUserIds = new LinkedList<>();
- private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw finterprints
-
- private class UserFingerprint {
- Fingerprint f;
- int userId;
- public UserFingerprint(Fingerprint f, int userId) {
- this.f = f;
- this.userId = userId;
- }
- }
-
// Normal fingerprint authentications are tracked by mPerformanceMap.
private HashMap<Integer, PerformanceStats> mPerformanceMap = new HashMap<>();
@@ -272,12 +257,10 @@
// This operation can be expensive, so keep track of the elapsed time. Might need to move to
// background if it takes too long.
long t = System.currentTimeMillis();
+
mAuthenticatorIds.clear();
- mEnumeratingUserIds.clear();
- mUnknownFingerprints.clear();
for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
int userId = getUserOrWorkProfileId(null, user.id);
- mEnumeratingUserIds.add(userId);
if (!mAuthenticatorIds.containsKey(userId)) {
updateActiveGroup(userId, null);
}
@@ -287,70 +270,12 @@
if (t > 1000) {
Slog.w(TAG, "loadAuthenticatorIds() taking too long: " + t + "ms");
}
-
- if (!mEnumeratingUserIds.isEmpty()) {
- enumerateNextUser();
- }
- }
-
- private void enumerateNextUser() {
- int nextUser = mEnumeratingUserIds.getFirst();
- updateActiveGroup(nextUser, null);
- boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
-
- if (DEBUG) Slog.v(TAG, "Enumerating user id " + nextUser + " of "
- + mEnumeratingUserIds.size() + " remaining users");
-
- startEnumerate(mToken, nextUser, null, restricted, true /* internal */);
- }
-
- // Remove unknown fingerprints from hardware
- private void cleanupUnknownFingerprints() {
- if (!mUnknownFingerprints.isEmpty()) {
- Slog.w(TAG, "unknown fingerprint size: " + mUnknownFingerprints.size());
- UserFingerprint uf = mUnknownFingerprints.get(0);
- mUnknownFingerprints.remove(uf);
- boolean restricted = !hasPermission(MANAGE_FINGERPRINT);
- updateActiveGroup(uf.userId, null);
- startRemove(mToken, uf.f.getFingerId(), uf.f.getGroupId(), uf.userId, null,
- restricted, true /* internal */);
- }
}
protected void handleEnumerate(long deviceId, int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "Enumerate: fid=" + fingerId
- + ", gid=" + groupId
- + ", dev=" + deviceId
- + ", rem=" + remaining);
-
- ClientMonitor client = mCurrentClient;
-
- if (client != null) {
- client.onEnumerationResult(fingerId, groupId, remaining);
- }
-
- // All fingerprints in hardware for this user were enumerated
- if (remaining == 0) {
- mEnumeratingUserIds.poll();
-
- if (client instanceof InternalEnumerateClient) {
- List<Fingerprint> enrolled = ((InternalEnumerateClient) client).getEnumeratedList();
- Slog.w(TAG, "Added " + enrolled.size() + " enumerated fingerprints for deletion");
- for (Fingerprint f : enrolled) {
- mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId()));
- }
- }
-
- removeClient(client);
-
- if (!mEnumeratingUserIds.isEmpty()) {
- enumerateNextUser();
- } else if (client instanceof InternalEnumerateClient) {
- if (DEBUG) Slog.v(TAG, "Finished enumerating all users");
- // This will start a chain of InternalRemovalClients
- cleanupUnknownFingerprints();
- }
- }
+ if (DEBUG) Slog.w(TAG, "Enumerate: fid=" + fingerId + ", gid="
+ + groupId + "rem=" + remaining);
+ // TODO: coordinate names with framework
}
protected void handleError(long deviceId, int error, int vendorCode) {
@@ -379,18 +304,10 @@
}
protected void handleRemoved(long deviceId, int fingerId, int groupId, int remaining) {
- if (DEBUG) Slog.w(TAG, "Removed: fid=" + fingerId
- + ", gid=" + groupId
- + ", dev=" + deviceId
- + ", rem=" + remaining);
-
ClientMonitor client = mCurrentClient;
if (client != null && client.onRemoved(fingerId, groupId, remaining)) {
removeClient(client);
}
- if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) {
- cleanupUnknownFingerprints();
- }
}
protected void handleAuthenticated(long deviceId, int fingerId, int groupId,
@@ -517,15 +434,7 @@
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
- if (currentClient instanceof InternalEnumerateClient ||
- currentClient instanceof InternalRemovalClient) {
- // This condition means we're currently running internal diagnostics to
- // remove extra fingerprints in the hardware and/or the software
- // TODO: design an escape hatch in case client never finishes
- }
- else {
- currentClient.stop(initiatedByClient);
- }
+ currentClient.stop(initiatedByClient);
mPendingClient = newClient;
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
@@ -542,86 +451,47 @@
}
void startRemove(IBinder token, int fingerId, int groupId, int userId,
- IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
+ IFingerprintServiceReceiver receiver, boolean restricted) {
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startRemove: no fingerprint HAL!");
return;
}
+ RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
+ receiver, fingerId, groupId, userId, restricted, token.toString()) {
+ @Override
+ public void notifyUserActivity() {
+ FingerprintService.this.userActivity();
+ }
- if (internal) {
- Context context = getContext();
- InternalRemovalClient client = new InternalRemovalClient(context, mHalDeviceId,
- token, receiver, fingerId, groupId, userId, restricted,
- context.getOpPackageName()) {
- @Override
- public void notifyUserActivity() {
-
- }
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
- else {
- RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
- receiver, fingerId, groupId, userId, restricted, token.toString()) {
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
+ @Override
+ public IBiometricsFingerprint getFingerprintDaemon() {
+ return FingerprintService.this.getFingerprintDaemon();
+ }
+ };
+ startClient(client, true);
}
void startEnumerate(IBinder token, int userId,
- IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
+ IFingerprintServiceReceiver receiver, boolean restricted) {
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startEnumerate: no fingerprint HAL!");
return;
}
- if (internal) {
- List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
- Context context = getContext();
- InternalEnumerateClient client = new InternalEnumerateClient(context, mHalDeviceId,
- token, receiver, userId, userId, restricted, context.getOpPackageName(),
- enrolledList) {
- @Override
- public void notifyUserActivity() {
+ EnumerateClient client = new EnumerateClient(getContext(), mHalDeviceId, token,
+ receiver, userId, userId, restricted, token.toString()) {
+ @Override
+ public void notifyUserActivity() {
+ FingerprintService.this.userActivity();
+ }
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
- else {
- EnumerateClient client = new EnumerateClient(getContext(), mHalDeviceId, token,
- receiver, userId, userId, restricted, token.toString()) {
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
-
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
- }
+ @Override
+ public IBiometricsFingerprint getFingerprintDaemon() {
+ return FingerprintService.this.getFingerprintDaemon();
+ }
+ };
+ startClient(client, true);
}
public List<Fingerprint> getEnrolledFingerprints(int userId) {
@@ -1108,14 +978,12 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- startRemove(token, fingerId, groupId, userId, receiver,
- restricted, false /* internal */);
+ startRemove(token, fingerId, groupId, userId, receiver, restricted);
}
});
}
- @Override // Binder call
public void enumerate(final IBinder token, final int userId,
final IFingerprintServiceReceiver receiver) {
checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
@@ -1123,7 +991,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- startEnumerate(token, userId, receiver, restricted, false /* internal */);
+ startEnumerate(token, userId, receiver, restricted);
}
});
diff --git a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java b/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
deleted file mode 100644
index f4d2596..0000000
--- a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2017 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.fingerprint;
-
-import android.content.Context;
-import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import android.os.IBinder;
-import android.util.Slog;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An internal class to help clean up unknown fingerprints in the hardware and software
- */
-public abstract class InternalEnumerateClient extends EnumerateClient {
-
- private List<Fingerprint> mEnrolledList;
- private List<Fingerprint> mEnumeratedList = new ArrayList<>(); // list of fp to delete
-
- public InternalEnumerateClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int groupId, int userId,
- boolean restricted, String owner, List<Fingerprint> enrolledList) {
-
- super(context, halDeviceId, token, receiver, userId, groupId, restricted, owner);
- mEnrolledList = enrolledList;
- }
-
- private void handleEnumeratedFingerprint(int fingerId, int groupId, int remaining) {
-
- boolean matched = false;
- for (int i=0; i<mEnrolledList.size(); i++) {
- if (mEnrolledList.get(i).getFingerId() == fingerId) {
- mEnrolledList.remove(i);
- matched = true;
- Slog.e(TAG, "Matched fingerprint fid=" + fingerId);
- break;
- }
- }
-
- // fingerId 0 means no fingerprints are in hardware
- if (!matched && fingerId != 0) {
- Fingerprint fingerprint = new Fingerprint("", groupId, fingerId, getHalDeviceId());
- mEnumeratedList.add(fingerprint);
- }
- }
-
- private void doFingerprintCleanup() {
-
- if (mEnrolledList == null) {
- return;
- }
-
- for (Fingerprint f : mEnrolledList) {
- Slog.e(TAG, "Internal Enumerate: Removing dangling enrolled fingerprint: "
- + f.getName() + " " + f.getFingerId() + " " + f.getGroupId()
- + " " + f.getDeviceId());
-
- FingerprintUtils.getInstance().removeFingerprintIdForUser(getContext(),
- f.getFingerId(), getTargetUserId());
- }
- mEnrolledList.clear();
- }
-
- public List<Fingerprint> getEnumeratedList() {
- return mEnumeratedList;
- }
-
- @Override
- public boolean onEnumerationResult(int fingerId, int groupId, int remaining) {
-
- handleEnumeratedFingerprint(fingerId, groupId, remaining);
- if (remaining == 0) {
- doFingerprintCleanup();
- }
-
- return fingerId == 0; // done when id hits 0
- }
-
-}
diff --git a/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java b/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java
deleted file mode 100644
index 19f61fe..0000000
--- a/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2017 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.fingerprint;
-
-import android.content.Context;
-import android.os.IBinder;
-import android.hardware.fingerprint.IFingerprintServiceReceiver;
-import com.android.server.fingerprint.RemovalClient;
-
-public abstract class InternalRemovalClient extends RemovalClient {
-
- public InternalRemovalClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int fingerId, int groupId, int userId,
- boolean restricted, String owner) {
-
- super(context, halDeviceId, token, receiver, fingerId, groupId, userId, restricted, owner);
-
- }
-}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ede5a5e..7468b95 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -90,6 +90,7 @@
import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -3126,8 +3127,9 @@
+ ", incomingUserId=" + incomingUserId
+ ", notificationUid=" + notificationUid
+ ", notification=" + notification;
- // STOPSHIP TODO: should throw instead of logging.
- // throw new IllegalArgumentException(noChannelStr);
+ if (Build.IS_DEBUGGABLE) {
+ throw new IllegalArgumentException(noChannelStr);
+ }
Log.e(TAG, noChannelStr);
return;
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index db133f8..5bdef9e 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -102,7 +102,8 @@
if (oi == null) {
// This overlay does not exist in our settings.
- if (mDefaultOverlays.contains(overlayPackage.packageName)) {
+ if (overlayPackage.isStaticOverlay ||
+ mDefaultOverlays.contains(overlayPackage.packageName)) {
// Enable this overlay by default.
mSettings.setEnabled(overlayPackage.packageName, newUserId, true);
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 5abc4e4..f138add 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -222,11 +222,10 @@
|| mWindowsForAccessibilityObserver != null);
}
- /** NOTE: This has to be called within a surface transaction. */
public void setForceShowMagnifiableBoundsLocked(boolean show) {
if (mDisplayMagnifier != null) {
mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
- mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+ mDisplayMagnifier.showMagnificationBoundsIfNeeded();
}
}
@@ -440,6 +439,12 @@
mMagnifedViewport.destroyWindow();
}
+ // Can be called outside of a surface transaction
+ public void showMagnificationBoundsIfNeeded() {
+ mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
+ .sendToTarget();
+ }
+
/** NOTE: This has to be called within a surface transaction. */
public void drawMagnifiedRegionBorderIfNeededLocked() {
mMagnifedViewport.drawWindowIfNeededLocked();
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 8590482..7d4d90b 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -387,8 +387,8 @@
/**
* Inject an SMS PDU into the android application framework.
*
- * The caller should have carrier privileges.
- * @see android.telephony.TelephonyManager#hasCarrierPrivileges
+ * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier
+ * privileges. @see android.telephony.TelephonyManager#hasCarrierPrivileges
*
* @param pdu is the byte array of pdu to be injected into android application framework
* @param format is the format of SMS pdu (3gpp or 3gpp2)
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index bcaac6e..73ee25a 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -261,7 +261,7 @@
* by the system.
*/
public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS
- = "android.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS";
+ = "com.android.internal.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS";
/**
* <p>Broadcast Action: Indicates that the action is forbidden by network.
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 5e9b81a..36c1de6 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -25,7 +25,7 @@
static const char* sMajorVersion = "2";
// Update minor version whenever a feature or flag is added.
-static const char* sMinorVersion = "11";
+static const char* sMinorVersion = "12";
int PrintVersion() {
std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 6e4b450..1947628 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -32,22 +32,19 @@
namespace aapt {
-static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs,
- ResourceType rhs) {
+static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
return lhs->type < rhs;
}
template <typename T>
-static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs,
- const StringPiece& rhs) {
+static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
}
ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) {
const auto last = packages.end();
- auto iter =
- std::lower_bound(packages.begin(), last, name,
- less_than_struct_with_name<ResourceTablePackage>);
+ auto iter = std::lower_bound(packages.begin(), last, name,
+ less_than_struct_with_name<ResourceTablePackage>);
if (iter != last && name == (*iter)->name) {
return iter->get();
}
@@ -63,8 +60,7 @@
return nullptr;
}
-ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name,
- Maybe<uint8_t> id) {
+ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
ResourceTablePackage* package = FindOrCreatePackage(name);
if (id && !package->id) {
package->id = id;
@@ -77,18 +73,15 @@
return package;
}
-ResourceTablePackage* ResourceTable::FindOrCreatePackage(
- const StringPiece& name) {
+ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
const auto last = packages.end();
- auto iter =
- std::lower_bound(packages.begin(), last, name,
- less_than_struct_with_name<ResourceTablePackage>);
+ auto iter = std::lower_bound(packages.begin(), last, name,
+ less_than_struct_with_name<ResourceTablePackage>);
if (iter != last && name == (*iter)->name) {
return iter->get();
}
- std::unique_ptr<ResourceTablePackage> new_package =
- util::make_unique<ResourceTablePackage>();
+ std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
new_package->name = name.to_string();
return packages.emplace(iter, std::move(new_package))->get();
}
@@ -113,8 +106,8 @@
ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name) {
const auto last = entries.end();
- auto iter = std::lower_bound(entries.begin(), last, name,
- less_than_struct_with_name<ResourceEntry>);
+ auto iter =
+ std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
if (iter != last && name == (*iter)->name) {
return iter->get();
}
@@ -123,8 +116,8 @@
ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name) {
auto last = entries.end();
- auto iter = std::lower_bound(entries.begin(), last, name,
- less_than_struct_with_name<ResourceEntry>);
+ auto iter =
+ std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
if (iter != last && name == (*iter)->name) {
return iter->get();
}
@@ -140,8 +133,7 @@
const StringPiece& product;
};
-bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs,
- const ConfigKey& rhs) {
+bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
int cmp = lhs->config.compare(*rhs.config);
if (cmp == 0) {
cmp = StringPiece(lhs->product).compare(rhs.product);
@@ -151,8 +143,8 @@
ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
const StringPiece& product) {
- auto iter = std::lower_bound(values.begin(), values.end(),
- ConfigKey{&config, product}, ltConfigKeyRef);
+ auto iter =
+ std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef);
if (iter != values.end()) {
ResourceConfigValue* value = iter->get();
if (value->config == config && StringPiece(value->product) == product) {
@@ -162,10 +154,10 @@
return nullptr;
}
-ResourceConfigValue* ResourceEntry::FindOrCreateValue(
- const ConfigDescription& config, const StringPiece& product) {
- auto iter = std::lower_bound(values.begin(), values.end(),
- ConfigKey{&config, product}, ltConfigKeyRef);
+ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
+ const StringPiece& product) {
+ auto iter =
+ std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef);
if (iter != values.end()) {
ResourceConfigValue* value = iter->get();
if (value->config == config && StringPiece(value->product) == product) {
@@ -173,14 +165,11 @@
}
}
ResourceConfigValue* newValue =
- values
- .insert(iter, util::make_unique<ResourceConfigValue>(config, product))
- ->get();
+ values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
return newValue;
}
-std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(
- const ConfigDescription& config) {
+std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
std::vector<ResourceConfigValue*> results;
auto iter = values.begin();
@@ -237,8 +226,8 @@
* format for there to be
* no error.
*/
-ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(
- Value* existing, Value* incoming) {
+ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
+ Value* incoming) {
Attribute* existing_attr = ValueCast<Attribute>(existing);
Attribute* incoming_attr = ValueCast<Attribute>(incoming);
if (!incoming_attr) {
@@ -278,18 +267,15 @@
// The two attributes are both DECLs, but they are plain attributes
// with the same formats.
// Keep the strongest one.
- return existing_attr->IsWeak() ? CollisionResult::kTakeNew
- : CollisionResult::kKeepOriginal;
+ return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
}
- if (existing_attr->IsWeak() &&
- existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
+ if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
// Any incoming attribute is better than this.
return CollisionResult::kTakeNew;
}
- if (incoming_attr->IsWeak() &&
- incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
+ if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
// The incoming attribute may be a USE instead of a DECL.
// Keep the existing attribute.
return CollisionResult::kKeepOriginal;
@@ -298,15 +284,26 @@
}
static constexpr const char* kValidNameChars = "._-";
-static constexpr const char* kValidNameMangledChars = "._-$";
+
+static StringPiece ValidateName(const StringPiece& name) {
+ auto iter = util::FindNonAlphaNumericAndNotInSet(name, kValidNameChars);
+ if (iter != name.end()) {
+ return StringPiece(iter, 1);
+ }
+ return {};
+}
+
+static StringPiece SkipValidateName(const StringPiece& /*name*/) {
+ return {};
+}
bool ResourceTable::AddResource(const ResourceNameRef& name,
const ConfigDescription& config,
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return AddResourceImpl(name, {}, config, product, std::move(value),
- kValidNameChars, ResolveValueCollision, diag);
+ return AddResourceImpl(name, {}, config, product, std::move(value), ValidateName,
+ ResolveValueCollision, diag);
}
bool ResourceTable::AddResource(const ResourceNameRef& name,
@@ -315,8 +312,8 @@
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return AddResourceImpl(name, res_id, config, product, std::move(value),
- kValidNameChars, ResolveValueCollision, diag);
+ return AddResourceImpl(name, res_id, config, product, std::move(value), ValidateName,
+ ResolveValueCollision, diag);
}
bool ResourceTable::AddFileReference(const ResourceNameRef& name,
@@ -324,29 +321,26 @@
const Source& source,
const StringPiece& path,
IDiagnostics* diag) {
- return AddFileReferenceImpl(name, config, source, path, nullptr,
- kValidNameChars, diag);
+ return AddFileReferenceImpl(name, config, source, path, nullptr, ValidateName, diag);
}
bool ResourceTable::AddFileReferenceAllowMangled(
const ResourceNameRef& name, const ConfigDescription& config,
const Source& source, const StringPiece& path, io::IFile* file,
IDiagnostics* diag) {
- return AddFileReferenceImpl(name, config, source, path, file,
- kValidNameMangledChars, diag);
+ return AddFileReferenceImpl(name, config, source, path, file, SkipValidateName, diag);
}
-bool ResourceTable::AddFileReferenceImpl(
- const ResourceNameRef& name, const ConfigDescription& config,
- const Source& source, const StringPiece& path, io::IFile* file,
- const char* valid_chars, IDiagnostics* diag) {
+bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name,
+ const ConfigDescription& config, const Source& source,
+ const StringPiece& path, io::IFile* file,
+ NameValidator name_validator, IDiagnostics* diag) {
std::unique_ptr<FileReference> fileRef =
util::make_unique<FileReference>(string_pool.MakeRef(path));
fileRef->SetSource(source);
fileRef->file = file;
- return AddResourceImpl(name, ResourceId{}, config, StringPiece{},
- std::move(fileRef), valid_chars, ResolveValueCollision,
- diag);
+ return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
+ name_validator, ResolveValueCollision, diag);
}
bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
@@ -354,8 +348,8 @@
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
- kValidNameMangledChars, ResolveValueCollision, diag);
+ return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipValidateName,
+ ResolveValueCollision, diag);
}
bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
@@ -364,25 +358,24 @@
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return AddResourceImpl(name, id, config, product, std::move(value),
- kValidNameMangledChars, ResolveValueCollision, diag);
+ return AddResourceImpl(name, id, config, product, std::move(value), SkipValidateName,
+ ResolveValueCollision, diag);
}
-bool ResourceTable::AddResourceImpl(
- const ResourceNameRef& name, const ResourceId& res_id,
- const ConfigDescription& config, const StringPiece& product,
- std::unique_ptr<Value> value, const char* valid_chars,
- const CollisionResolverFunc& conflictResolver, IDiagnostics* diag) {
+bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
+ const ConfigDescription& config, const StringPiece& product,
+ std::unique_ptr<Value> value, NameValidator name_validator,
+ const CollisionResolverFunc& conflictResolver,
+ IDiagnostics* diag) {
CHECK(value != nullptr);
CHECK(diag != nullptr);
- auto bad_char_iter =
- util::FindNonAlphaNumericAndNotInSet(name.entry, valid_chars);
- if (bad_char_iter != name.entry.end()) {
- diag->Error(DiagMessage(value->GetSource())
- << "resource '" << name << "' has invalid entry name '"
- << name.entry << "'. Invalid character '"
- << StringPiece(bad_char_iter, 1) << "'");
+ const StringPiece bad_char = name_validator(name.entry);
+ if (!bad_char.empty()) {
+ diag->Error(DiagMessage(value->GetSource()) << "resource '" << name
+ << "' has invalid entry name '" << name.entry
+ << "'. Invalid character '" << bad_char << "'");
+
return false;
}
@@ -450,30 +443,26 @@
bool ResourceTable::SetSymbolState(const ResourceNameRef& name,
const ResourceId& res_id,
const Symbol& symbol, IDiagnostics* diag) {
- return SetSymbolStateImpl(name, res_id, symbol, kValidNameChars, diag);
+ return SetSymbolStateImpl(name, res_id, symbol, ValidateName, diag);
}
bool ResourceTable::SetSymbolStateAllowMangled(const ResourceNameRef& name,
const ResourceId& res_id,
const Symbol& symbol,
IDiagnostics* diag) {
- return SetSymbolStateImpl(name, res_id, symbol, kValidNameMangledChars, diag);
+ return SetSymbolStateImpl(name, res_id, symbol, SkipValidateName, diag);
}
-bool ResourceTable::SetSymbolStateImpl(const ResourceNameRef& name,
- const ResourceId& res_id,
- const Symbol& symbol,
- const char* valid_chars,
+bool ResourceTable::SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
+ const Symbol& symbol, NameValidator name_validator,
IDiagnostics* diag) {
CHECK(diag != nullptr);
- auto bad_char_iter =
- util::FindNonAlphaNumericAndNotInSet(name.entry, valid_chars);
- if (bad_char_iter != name.entry.end()) {
- diag->Error(DiagMessage(symbol.source)
- << "resource '" << name << "' has invalid entry name '"
- << name.entry << "'. Invalid character '"
- << StringPiece(bad_char_iter, 1) << "'");
+ const StringPiece bad_char = name_validator(name.entry);
+ if (!bad_char.empty()) {
+ diag->Error(DiagMessage(symbol.source) << "resource '" << name << "' has invalid entry name '"
+ << name.entry << "'. Invalid character '" << bad_char
+ << "'");
return false;
}
@@ -532,8 +521,7 @@
return true;
}
-Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(
- const ResourceNameRef& name) {
+Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) {
ResourceTablePackage* package = FindPackage(name.package);
if (!package) {
return {};
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 6b69aaf..b032121 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -113,8 +113,7 @@
const android::StringPiece& product);
ResourceConfigValue* FindOrCreateValue(const ConfigDescription& config,
const android::StringPiece& product);
- std::vector<ResourceConfigValue*> findAllValues(
- const ConfigDescription& config);
+ std::vector<ResourceConfigValue*> FindAllValues(const ConfigDescription& config);
std::vector<ResourceConfigValue*> FindValuesIf(
const std::function<bool(ResourceConfigValue*)>& f);
@@ -189,8 +188,7 @@
* When a collision of resources occurs, this method decides which value to
* keep.
*/
- static CollisionResult ResolveValueCollision(Value* existing,
- Value* incoming);
+ static CollisionResult ResolveValueCollision(Value* existing, Value* incoming);
bool AddResource(const ResourceNameRef& name, const ConfigDescription& config,
const android::StringPiece& product, std::unique_ptr<Value> value,
@@ -274,20 +272,24 @@
std::map<size_t, std::string> included_packages_;
private:
+ // The function type that validates a symbol name. Returns a non-empty StringPiece representing
+ // the offending character (which may be more than one byte in UTF-8). Returns an empty string
+ // if the name was valid.
+ using NameValidator = android::StringPiece(const android::StringPiece&);
+
ResourceTablePackage* FindOrCreatePackage(const android::StringPiece& name);
bool AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
const ConfigDescription& config, const android::StringPiece& product,
- std::unique_ptr<Value> value, const char* valid_chars,
+ std::unique_ptr<Value> value, NameValidator name_validator,
const CollisionResolverFunc& conflict_resolver, IDiagnostics* diag);
bool AddFileReferenceImpl(const ResourceNameRef& name, const ConfigDescription& config,
const Source& source, const android::StringPiece& path, io::IFile* file,
- const char* valid_chars, IDiagnostics* diag);
+ NameValidator name_validator, IDiagnostics* diag);
bool SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
- const Symbol& symbol, const char* valid_chars,
- IDiagnostics* diag);
+ const Symbol& symbol, NameValidator name_validator, IDiagnostics* diag);
DISALLOW_COPY_AND_ASSIGN(ResourceTable);
};
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index cb3699a0..e2b37be 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -40,6 +40,14 @@
test::GetDiagnostics()));
}
+TEST(ResourceTableTest, AddResourceWithWeirdNameWhenAddingMangledResources) {
+ ResourceTable table;
+
+ EXPECT_TRUE(table.AddResourceAllowMangled(
+ test::ParseNameOrDie("android:id/heythere "), ConfigDescription{}, "",
+ test::ValueBuilder<Id>().SetSource("test.xml", 21u).Build(), test::GetDiagnostics()));
+}
+
TEST(ResourceTableTest, AddOneResource) {
ResourceTable table;
@@ -130,7 +138,7 @@
table.FindResource(test::ParseNameOrDie("android:string/foo"));
AAPT_ASSERT_TRUE(sr);
std::vector<ResourceConfigValue*> values =
- sr.value().entry->findAllValues(test::ParseConfigOrDie("land"));
+ sr.value().entry->FindAllValues(test::ParseConfigOrDie("land"));
ASSERT_EQ(2u, values.size());
EXPECT_EQ(std::string("phone"), values[0]->product);
EXPECT_EQ(std::string("tablet"), values[1]->product);
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index 9899f80..2e674bd 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -1,5 +1,13 @@
# Android Asset Packaging Tool 2.0 (AAPT2) release notes
+## Version 2.12
+### `aapt2 optimize ...`
+- aapt2 optimize now understands map (complex) values under the type `id`. It ignores their
+ contents and interprets them as a sentinel `id` type. This was added to support existing
+ apps that build with their `id` types as map values.
+ AAPT and AAPT2 always generate a simple value for the type `ID`, so it is unclear how some
+ these apps are encoded.
+
## Version 2.11
### `aapt2 link ...`
- Adds the ability to specify assets directories with the -A parameter. Assets work just like
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 66bcfa0..35bf618 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -168,10 +168,11 @@
}
bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) {
- const ResTable_package* package_header = ConvertTo<ResTable_package>(chunk);
+ constexpr size_t kMinPackageSize =
+ sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset);
+ const ResTable_package* package_header = ConvertTo<ResTable_package, kMinPackageSize>(chunk);
if (!package_header) {
- context_->GetDiagnostics()->Error(DiagMessage(source_)
- << "corrupt ResTable_package chunk");
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_package chunk");
return false;
}
@@ -498,8 +499,14 @@
return ParseArray(name, config, map);
case ResourceType::kPlurals:
return ParsePlural(name, config, map);
+ case ResourceType::kId:
+ // Special case: An ID is not a bag, but some apps have defined the auto-generated
+ // IDs that come from declaring an enum value in an attribute as an empty map...
+ // We can ignore the value here.
+ return util::make_unique<Id>();
default:
- LOG(FATAL) << "unknown map type";
+ context_->GetDiagnostics()->Error(DiagMessage() << "illegal map type '" << ToString(name.type)
+ << "' (" << (int)name.type << ")");
break;
}
return {};
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index da9aa06..afee290 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -336,12 +336,12 @@
/**
* Indicates venue name (such as 'San Francisco Airport') published by access point; only
- * available on passpoint network and if published by access point.
+ * available on Passpoint network and if published by access point.
*/
public CharSequence venueName;
/**
- * Indicates passpoint operator name published by access point.
+ * Indicates Passpoint operator name published by access point.
*/
public CharSequence operatorFriendlyName;
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 04f9059..7defa7c 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -367,12 +367,12 @@
public WifiEnterpriseConfig enterpriseConfig;
/**
- * Fully qualified domain name of a passpoint configuration
+ * Fully qualified domain name of a Passpoint configuration
*/
public String FQDN;
/**
- * Name of passpoint credential provider
+ * Name of Passpoint credential provider
*/
public String providerFriendlyName;
@@ -385,8 +385,8 @@
public boolean isHomeProviderNetwork;
/**
- * Roaming Consortium Id list for passpoint credential; identifies a set of networks where
- * passpoint credential will be considered valid
+ * Roaming Consortium Id list for Passpoint credential; identifies a set of networks where
+ * Passpoint credential will be considered valid
*/
public long[] roamingConsortiumIds;
@@ -1425,7 +1425,7 @@
}
/**
- * Identify if this configuration represents a passpoint network
+ * Identify if this configuration represents a Passpoint network
*/
public boolean isPasspoint() {
return !TextUtils.isEmpty(FQDN)
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 4268f24..18f30f8 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -940,8 +940,8 @@
}
/**
- * Set realm for passpoint credential; realm identifies a set of networks where your
- * passpoint credential can be used
+ * Set realm for Passpoint credential; realm identifies a set of networks where your
+ * Passpoint credential can be used
* @param realm the realm
*/
public void setRealm(String realm) {
@@ -949,7 +949,7 @@
}
/**
- * Get realm for passpoint credential; see {@link #setRealm(String)} for more information
+ * Get realm for Passpoint credential; see {@link #setRealm(String)} for more information
* @return the realm
*/
public String getRealm() {
@@ -957,7 +957,7 @@
}
/**
- * Set plmn (Public Land Mobile Network) of the provider of passpoint credential
+ * Set plmn (Public Land Mobile Network) of the provider of Passpoint credential
* @param plmn the plmn value derived from mcc (mobile country code) & mnc (mobile network code)
*/
public void setPlmn(String plmn) {
@@ -965,7 +965,7 @@
}
/**
- * Get plmn (Public Land Mobile Network) for passpoint credential; see {@link #setPlmn
+ * Get plmn (Public Land Mobile Network) for Passpoint credential; see {@link #setPlmn
* (String)} for more information
* @return the plmn
*/
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ae6a679..9ee772a 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -858,6 +858,10 @@
/**
* Returns a WifiConfiguration matching this ScanResult
+ *
+ * An {@link UnsupportedOperationException} will be thrown if Passpoint is not enabled
+ * on the device.
+ *
* @param scanResult scanResult that represents the BSSID
* @return {@link WifiConfiguration} that matches this BSSID or null
* @hide
@@ -944,6 +948,8 @@
* FQDN, the new configuration will replace the existing configuration.
*
* An {@link IllegalArgumentException} will be thrown on failure.
+ * An {@link UnsupportedOperationException} will be thrown if Passpoint is not enabled
+ * on the device.
*
* @param config The Passpoint configuration to be added
*/
@@ -961,8 +967,10 @@
* Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
*
* An {@link IllegalArgumentException} will be thrown on failure.
+ * An {@link UnsupportedOperationException} will be thrown if Passpoint is not enabled
+ * on the device.
*
- * @param fqdn The FQDN of the passpoint configuration to be removed
+ * @param fqdn The FQDN of the Passpoint configuration to be removed
*/
public void removePasspointConfiguration(String fqdn) {
try {
@@ -979,6 +987,9 @@
*
* An empty list will be returned when no configurations are installed.
*
+ * An {@link UnsupportedOperationException} will be thrown if Passpoint is not enabled
+ * on the device.
+ *
* @return A list of {@link PasspointConfiguration}
*/
public List<PasspointConfiguration> getPasspointConfigurations() {
@@ -995,6 +1006,9 @@
* {@link #EXTRA_ICON} will indicate the result of the request.
* A missing intent extra {@link #EXTRA_ICON} will indicate a failure.
*
+ * An {@link UnsupportedOperationException} will be thrown if Passpoint is not enabled
+ * on the device.
+ *
* @param bssid The BSSID of the AP
* @param fileName Name of the icon file (remote file) to query from the AP
*/
@@ -1254,7 +1268,7 @@
}
/**
- * @return true if this adapter supports passpoint
+ * @return true if this adapter supports Passpoint
* @hide
*/
public boolean isPasspointSupported() {
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index 9645b1d..334205b 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -85,7 +85,7 @@
/**
* Called when a discovery session (publish or subscribe) terminates. Termination may be due
* to user-request (either directly through {@link DiscoverySession#destroy()} or
- * application-specified expiration, e.g. {@link PublishConfig.Builder#setPublishCount(int)}
+ * application-specified expiration, e.g. {@link PublishConfig.Builder#setTtlSec(int)}
* or {@link SubscribeConfig.Builder#setTtlSec(int)}).
*/
public void onSessionTerminated() {
diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
index a996844..1ce12f3 100644
--- a/wifi/java/android/net/wifi/aware/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -75,9 +75,6 @@
public final int mPublishType;
/** @hide */
- public final int mPublishCount;
-
- /** @hide */
public final int mTtlSec;
/** @hide */
@@ -85,12 +82,11 @@
/** @hide */
public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
- int publishType, int publichCount, int ttlSec, boolean enableTerminateNotification) {
+ int publishType, int ttlSec, boolean enableTerminateNotification) {
mServiceName = serviceName;
mServiceSpecificInfo = serviceSpecificInfo;
mMatchFilter = matchFilter;
mPublishType = publishType;
- mPublishCount = publichCount;
mTtlSec = ttlSec;
mEnableTerminateNotification = enableTerminateNotification;
}
@@ -100,8 +96,8 @@
return "PublishConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + (
(mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo))
+ ", mMatchFilter=" + (new TlvBufferUtils.TlvIterable(0, 1,
- mMatchFilter)).toString() + ", mPublishType=" + mPublishType + ", mPublishCount="
- + mPublishCount + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification="
+ mMatchFilter)).toString() + ", mPublishType=" + mPublishType
+ + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification="
+ mEnableTerminateNotification + "]";
}
@@ -116,7 +112,6 @@
dest.writeByteArray(mServiceSpecificInfo);
dest.writeByteArray(mMatchFilter);
dest.writeInt(mPublishType);
- dest.writeInt(mPublishCount);
dest.writeInt(mTtlSec);
dest.writeInt(mEnableTerminateNotification ? 1 : 0);
}
@@ -133,11 +128,10 @@
byte[] ssi = in.createByteArray();
byte[] matchFilter = in.createByteArray();
int publishType = in.readInt();
- int publishCount = in.readInt();
int ttlSec = in.readInt();
boolean enableTerminateNotification = in.readInt() != 0;
- return new PublishConfig(serviceName, ssi, matchFilter, publishType, publishCount,
+ return new PublishConfig(serviceName, ssi, matchFilter, publishType,
ttlSec, enableTerminateNotification);
}
};
@@ -156,7 +150,7 @@
return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
- && mPublishType == lhs.mPublishType && mPublishCount == lhs.mPublishCount
+ && mPublishType == lhs.mPublishType
&& mTtlSec == lhs.mTtlSec
&& mEnableTerminateNotification == lhs.mEnableTerminateNotification;
}
@@ -169,7 +163,6 @@
result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
result = 31 * result + Arrays.hashCode(mMatchFilter);
result = 31 * result + mPublishType;
- result = 31 * result + mPublishCount;
result = 31 * result + mTtlSec;
result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
@@ -193,9 +186,6 @@
if (mPublishType < PUBLISH_TYPE_UNSOLICITED || mPublishType > PUBLISH_TYPE_SOLICITED) {
throw new IllegalArgumentException("Invalid publishType - " + mPublishType);
}
- if (mPublishCount < 0) {
- throw new IllegalArgumentException("Invalid publishCount - must be non-negative");
- }
if (mTtlSec < 0) {
throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
}
@@ -229,7 +219,6 @@
private byte[] mServiceSpecificInfo;
private byte[] mMatchFilter;
private int mPublishType = PUBLISH_TYPE_UNSOLICITED;
- private int mPublishCount = 0;
private int mTtlSec = 0;
private boolean mEnableTerminateNotification = true;
@@ -317,30 +306,6 @@
}
/**
- * Sets the number of times an unsolicited (configured using
- * {@link PublishConfig.Builder#setPublishType(int)}) publish session
- * will be broadcast. When the count is reached an event will be
- * generated for {@link DiscoverySessionCallback#onSessionTerminated()}
- * [unless {@link #setTerminateNotificationEnabled(boolean)} disables the callback].
- * <p>
- * Optional. 0 by default - indicating the session doesn't terminate on its own.
- * Session will be terminated when {@link DiscoverySession#destroy()} is
- * called.
- *
- * @param publishCount Number of publish packets to broadcast.
- *
- * @return The builder to facilitate chaining
- * {@code builder.setXXX(..).setXXX(..)}.
- */
- public Builder setPublishCount(int publishCount) {
- if (publishCount < 0) {
- throw new IllegalArgumentException("Invalid publishCount - must be non-negative");
- }
- mPublishCount = publishCount;
- return this;
- }
-
- /**
* Sets the time interval (in seconds) an unsolicited (
* {@link PublishConfig.Builder#setPublishType(int)}) publish session
* will be alive - broadcasting a packet. When the TTL is reached
@@ -387,7 +352,7 @@
*/
public PublishConfig build() {
return new PublishConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType,
- mPublishCount, mTtlSec, mEnableTerminateNotification);
+ mTtlSec, mEnableTerminateNotification);
}
}
}
diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
index 3397c4b..97a6a3f 100644
--- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -94,9 +94,6 @@
public final int mSubscribeType;
/** @hide */
- public final int mSubscribeCount;
-
- /** @hide */
public final int mTtlSec;
/** @hide */
@@ -107,13 +104,12 @@
/** @hide */
public SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
- int subscribeType, int publichCount, int ttlSec, int matchStyle,
+ int subscribeType, int ttlSec, int matchStyle,
boolean enableTerminateNotification) {
mServiceName = serviceName;
mServiceSpecificInfo = serviceSpecificInfo;
mMatchFilter = matchFilter;
mSubscribeType = subscribeType;
- mSubscribeCount = publichCount;
mTtlSec = ttlSec;
mMatchStyle = matchStyle;
mEnableTerminateNotification = enableTerminateNotification;
@@ -125,7 +121,7 @@
(mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo))
+ ", mMatchFilter=" + (new TlvBufferUtils.TlvIterable(0, 1,
mMatchFilter)).toString() + ", mSubscribeType=" + mSubscribeType
- + ", mSubscribeCount=" + mSubscribeCount + ", mTtlSec=" + mTtlSec + ", mMatchType="
+ + ", mTtlSec=" + mTtlSec + ", mMatchType="
+ mMatchStyle + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+ "]";
}
@@ -141,7 +137,6 @@
dest.writeByteArray(mServiceSpecificInfo);
dest.writeByteArray(mMatchFilter);
dest.writeInt(mSubscribeType);
- dest.writeInt(mSubscribeCount);
dest.writeInt(mTtlSec);
dest.writeInt(mMatchStyle);
dest.writeInt(mEnableTerminateNotification ? 1 : 0);
@@ -159,12 +154,11 @@
byte[] ssi = in.createByteArray();
byte[] matchFilter = in.createByteArray();
int subscribeType = in.readInt();
- int subscribeCount = in.readInt();
int ttlSec = in.readInt();
int matchStyle = in.readInt();
boolean enableTerminateNotification = in.readInt() != 0;
- return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType, subscribeCount,
+ return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType,
ttlSec, matchStyle, enableTerminateNotification);
}
};
@@ -183,7 +177,7 @@
return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
lhs.mServiceSpecificInfo) && Arrays.equals(mMatchFilter, lhs.mMatchFilter)
- && mSubscribeType == lhs.mSubscribeType && mSubscribeCount == lhs.mSubscribeCount
+ && mSubscribeType == lhs.mSubscribeType
&& mTtlSec == lhs.mTtlSec && mMatchStyle == lhs.mMatchStyle
&& mEnableTerminateNotification == lhs.mEnableTerminateNotification;
}
@@ -196,7 +190,6 @@
result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
result = 31 * result + Arrays.hashCode(mMatchFilter);
result = 31 * result + mSubscribeType;
- result = 31 * result + mSubscribeCount;
result = 31 * result + mTtlSec;
result = 31 * result + mMatchStyle;
result = 31 * result + (mEnableTerminateNotification ? 1 : 0);
@@ -221,9 +214,6 @@
if (mSubscribeType < SUBSCRIBE_TYPE_PASSIVE || mSubscribeType > SUBSCRIBE_TYPE_ACTIVE) {
throw new IllegalArgumentException("Invalid subscribeType - " + mSubscribeType);
}
- if (mSubscribeCount < 0) {
- throw new IllegalArgumentException("Invalid subscribeCount - must be non-negative");
- }
if (mTtlSec < 0) {
throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
}
@@ -261,7 +251,6 @@
private byte[] mServiceSpecificInfo;
private byte[] mMatchFilter;
private int mSubscribeType = SUBSCRIBE_TYPE_PASSIVE;
- private int mSubscribeCount = 0;
private int mTtlSec = 0;
private int mMatchStyle = MATCH_STYLE_ALL;
private boolean mEnableTerminateNotification = true;
@@ -350,29 +339,6 @@
}
/**
- * Sets the number of times an active (
- * {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session
- * will broadcast. When the count is reached an event will be
- * generated for {@link DiscoverySessionCallback#onSessionTerminated()}.
- * <p>
- * Optional. 0 by default - indicating the session doesn't terminate on its own.
- * Session will be terminated when {@link DiscoverySession#destroy()} is
- * called.
- *
- * @param subscribeCount Number of subscribe packets to broadcast.
- *
- * @return The builder to facilitate chaining
- * {@code builder.setXXX(..).setXXX(..)}.
- */
- public Builder setSubscribeCount(int subscribeCount) {
- if (subscribeCount < 0) {
- throw new IllegalArgumentException("Invalid subscribeCount - must be non-negative");
- }
- mSubscribeCount = subscribeCount;
- return this;
- }
-
- /**
* Sets the time interval (in seconds) an active (
* {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session
* will be alive - i.e. broadcasting a packet. When the TTL is reached
@@ -440,7 +406,7 @@
*/
public SubscribeConfig build() {
return new SubscribeConfig(mServiceName, mServiceSpecificInfo, mMatchFilter,
- mSubscribeType, mSubscribeCount, mTtlSec, mMatchStyle,
+ mSubscribeType, mTtlSec, mMatchStyle,
mEnableTerminateNotification);
}
}
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index eceb365..830db22 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -18,11 +18,10 @@
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -140,8 +139,8 @@
// (1) connect + success
mDut.attach(mockCallback, mMockLooperHandler);
- inOrder.verify(mockAwareService).connect(binder.capture(), anyString(),
- clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+ inOrder.verify(mockAwareService).connect(binder.capture(), any(),
+ clientProxyCallback.capture(), isNull(), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
@@ -150,8 +149,7 @@
// (2) publish - should succeed
PublishConfig publishConfig = new PublishConfig.Builder().build();
session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
- inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
- any(IWifiAwareDiscoverySessionCallback.class));
+ inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig), any());
// (3) disconnect
session.destroy();
@@ -163,8 +161,8 @@
// (5) connect
mDut.attach(mockCallback, mMockLooperHandler);
- inOrder.verify(mockAwareService).connect(binder.capture(), anyString(),
- any(IWifiAwareEventCallback.class), (ConfigRequest) isNull(), eq(false));
+ inOrder.verify(mockAwareService).connect(binder.capture(), any(), any(), isNull(),
+ eq(false));
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
}
@@ -185,16 +183,16 @@
// (1) connect + failure
mDut.attach(mockCallback, mMockLooperHandler);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ isNull(), eq(false));
clientProxyCallback.getValue().onConnectFail(reason);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onAttachFailed();
// (2) connect + success
mDut.attach(mockCallback, mMockLooperHandler);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ isNull(), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
@@ -203,8 +201,7 @@
// (4) subscribe: should succeed
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
- inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
- any(IWifiAwareDiscoverySessionCallback.class));
+ inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig), any());
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
}
@@ -223,19 +220,19 @@
// (1) connect + success
mDut.attach(mockCallback, mMockLooperHandler);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ isNull(), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId);
mMockLooper.dispatchAll();
- inOrder.verify(mockCallback).onAttached(any(WifiAwareSession.class));
+ inOrder.verify(mockCallback).onAttached(any());
// (2) connect + success
mDut.attach(mockCallback, mMockLooperHandler);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ isNull(), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId + 1);
mMockLooper.dispatchAll();
- inOrder.verify(mockCallback).onAttached(any(WifiAwareSession.class));
+ inOrder.verify(mockCallback).onAttached(any());
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
}
@@ -278,8 +275,8 @@
// (0) connect + success
mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), eq(configRequest), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ eq(configRequest), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
@@ -370,8 +367,8 @@
// (1) connect successfully
mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), eq(configRequest), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ eq(configRequest), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
@@ -426,8 +423,8 @@
// (0) connect + success
mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), eq(configRequest), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ eq(configRequest), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
@@ -507,8 +504,8 @@
// (1) connect successfully
mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), eq(configRequest), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ eq(configRequest), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
@@ -691,7 +688,6 @@
collector.checkThat("mServiceName", subscribeConfig.mServiceName, equalTo(null));
collector.checkThat("mSubscribeType", subscribeConfig.mSubscribeType,
equalTo(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE));
- collector.checkThat("mSubscribeCount", subscribeConfig.mSubscribeCount, equalTo(0));
collector.checkThat("mTtlSec", subscribeConfig.mTtlSec, equalTo(0));
collector.checkThat("mMatchStyle", subscribeConfig.mMatchStyle,
equalTo(SubscribeConfig.MATCH_STYLE_ALL));
@@ -714,7 +710,7 @@
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setSubscribeType(subscribeType)
- .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+ .setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
.setTerminateNotificationEnabled(enableTerminateNotification).build();
collector.checkThat("mServiceName", serviceName.getBytes(),
@@ -724,8 +720,6 @@
collector.checkThat("mMatchFilter", matchFilter, equalTo(subscribeConfig.mMatchFilter));
collector.checkThat("mSubscribeType", subscribeType,
equalTo(subscribeConfig.mSubscribeType));
- collector.checkThat("mSubscribeCount", subscribeCount,
- equalTo(subscribeConfig.mSubscribeCount));
collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeConfig.mTtlSec));
collector.checkThat("mMatchStyle", matchStyle, equalTo(subscribeConfig.mMatchStyle));
collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
@@ -747,7 +741,7 @@
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setSubscribeType(subscribeType)
- .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+ .setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
.setTerminateNotificationEnabled(enableTerminateNotification).build();
Parcel parcelW = Parcel.obtain();
@@ -769,11 +763,6 @@
}
@Test(expected = IllegalArgumentException.class)
- public void testSubscribeConfigBuilderNegativeCount() {
- new SubscribeConfig.Builder().setSubscribeCount(-1);
- }
-
- @Test(expected = IllegalArgumentException.class)
public void testSubscribeConfigBuilderNegativeTtl() {
new SubscribeConfig.Builder().setTtlSec(-100);
}
@@ -797,7 +786,6 @@
collector.checkThat("mServiceName", publishConfig.mServiceName, equalTo(null));
collector.checkThat("mPublishType", publishConfig.mPublishType,
equalTo(PublishConfig.PUBLISH_TYPE_UNSOLICITED));
- collector.checkThat("mPublishCount", publishConfig.mPublishCount, equalTo(0));
collector.checkThat("mTtlSec", publishConfig.mTtlSec, equalTo(0));
collector.checkThat("mEnableTerminateNotification",
publishConfig.mEnableTerminateNotification, equalTo(true));
@@ -817,7 +805,7 @@
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setPublishType(publishType)
- .setPublishCount(publishCount).setTtlSec(publishTtl)
+ .setTtlSec(publishTtl)
.setTerminateNotificationEnabled(enableTerminateNotification).build();
collector.checkThat("mServiceName", serviceName.getBytes(),
@@ -826,7 +814,6 @@
serviceSpecificInfo.getBytes(), equalTo(publishConfig.mServiceSpecificInfo));
collector.checkThat("mMatchFilter", matchFilter, equalTo(publishConfig.mMatchFilter));
collector.checkThat("mPublishType", publishType, equalTo(publishConfig.mPublishType));
- collector.checkThat("mPublishCount", publishCount, equalTo(publishConfig.mPublishCount));
collector.checkThat("mTtlSec", publishTtl, equalTo(publishConfig.mTtlSec));
collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
equalTo(publishConfig.mEnableTerminateNotification));
@@ -846,7 +833,7 @@
.setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
.setPublishType(publishType)
- .setPublishCount(publishCount).setTtlSec(publishTtl)
+ .setTtlSec(publishTtl)
.setTerminateNotificationEnabled(enableTerminateNotification).build();
Parcel parcelW = Parcel.obtain();
@@ -868,11 +855,6 @@
}
@Test(expected = IllegalArgumentException.class)
- public void testPublishConfigBuilderNegativeCount() {
- new PublishConfig.Builder().setPublishCount(-4);
- }
-
- @Test(expected = IllegalArgumentException.class)
public void testPublishConfigBuilderNegativeTtl() {
new PublishConfig.Builder().setTtlSec(-10);
}
@@ -899,8 +881,7 @@
final RttManager.RttResult rttResults = new RttManager.RttResult();
rttResults.distance = 10;
- when(mockAwareService.startRanging(anyInt(), anyInt(),
- any(RttManager.ParcelableRttParams.class))).thenReturn(rangingId);
+ when(mockAwareService.startRanging(anyInt(), anyInt(), any())).thenReturn(rangingId);
InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
mockPublishSession, mockRttListener);
@@ -919,8 +900,8 @@
// (1) connect successfully
mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), eq(configRequest), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ eq(configRequest), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
@@ -994,8 +975,8 @@
// (1) connect successfully
mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), eq(configRequest), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ eq(configRequest), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
@@ -1085,8 +1066,8 @@
// (1) connect successfully
mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
- inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
- clientProxyCallback.capture(), eq(configRequest), eq(false));
+ inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(),
+ eq(configRequest), eq(false));
clientProxyCallback.getValue().onConnectSuccess(clientId);
mMockLooper.dispatchAll();
inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());