summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt37
-rw-r--r--api/system-current.txt36
-rw-r--r--api/test-current.txt46
-rw-r--r--core/java/android/app/AuthenticationRequiredException.java99
-rw-r--r--core/java/android/app/Fragment.java3
-rw-r--r--core/java/android/app/RecoverableSecurityException.java3
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java14
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageParser.java2
-rw-r--r--core/java/android/database/PageViewCursor.java8
-rw-r--r--core/java/android/hardware/Sensor.java12
-rw-r--r--core/java/android/os/Parcel.java4
-rw-r--r--core/java/android/os/Process.java6
-rw-r--r--core/java/android/provider/DocumentsContract.java9
-rw-r--r--core/java/android/provider/DocumentsProvider.java120
-rw-r--r--core/java/android/text/StaticLayout.java2
-rw-r--r--core/java/android/widget/ListPopupWindow.java9
-rw-r--r--core/java/android/widget/PopupWindow.java66
-rw-r--r--core/java/android/widget/TextView.java4
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java14
-rw-r--r--core/java/com/android/internal/view/menu/CascadingMenuPopup.java31
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp50
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_mmcc_indication_icn.pngbin0 -> 3688 bytes
-rw-r--r--core/res/res/drawable-xhdpi/stat_notify_mmcc_indication_icn.pngbin0 -> 3720 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/stat_notify_mmcc_indication_icn.pngbin0 -> 3876 bytes
-rw-r--r--core/res/res/values-ldrtl-television/config.xml4
-rw-r--r--core/res/res/values-television/config.xml4
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--core/res/res/values/symbols.xml6
-rw-r--r--graphics/java/android/graphics/Typeface.java6
-rw-r--r--keystore/java/android/security/KeyChain.java3
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp6
-rw-r--r--media/java/android/media/tv/TvContract.java5
-rw-r--r--packages/SystemUI/res/drawable/ic_add_circle_qs.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml47
-rw-r--r--packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java7
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java8
-rw-r--r--services/core/java/com/android/server/fingerprint/EnumerateClient.java6
-rw-r--r--services/core/java/com/android/server/fingerprint/FingerprintService.java198
-rw-r--r--services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java94
-rw-r--r--services/core/java/com/android/server/fingerprint/InternalRemovalClient.java33
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java25
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java56
-rw-r--r--services/core/java/com/android/server/policy/GlobalActions.java21
-rw-r--r--services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java20
-rw-r--r--services/core/java/com/android/server/security/KeyChainSystemService.java109
-rw-r--r--services/java/com/android/server/SystemServer.java5
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java73
-rw-r--r--tests/testables/src/android/testing/AndroidTestingRunner.java48
-rw-r--r--tests/testables/src/android/testing/TestableLooper.java215
-rw-r--r--tests/testables/tests/src/android/testing/TestableLooperTest.java60
-rw-r--r--wifi/java/android/net/wifi/IconInfo.aidl19
-rw-r--r--wifi/java/android/net/wifi/IconInfo.java111
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java21
-rw-r--r--wifi/java/android/net/wifi/hotspot2/ConfigParser.java21
-rw-r--r--wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java6
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64171
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf5
-rw-r--r--wifi/tests/assets/pps/PerProviderSubscription.xml7
-rw-r--r--wifi/tests/src/android/net/wifi/IconInfoTest.java122
68 files changed, 1399 insertions, 836 deletions
diff --git a/api/current.txt b/api/current.txt
index e341739ab6ef..5018ecfd6803 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4297,6 +4297,14 @@ package android.app {
field public java.lang.String serviceDetails;
}
+ public final class AuthenticationRequiredException extends java.lang.SecurityException implements android.os.Parcelable {
+ ctor public AuthenticationRequiredException(java.lang.Throwable, android.app.PendingIntent);
+ method public int describeContents();
+ method public android.app.PendingIntent getUserAction();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.AuthenticationRequiredException> CREATOR;
+ }
+
public final class AutomaticZenRule implements android.os.Parcelable {
ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
ctor public AutomaticZenRule(android.os.Parcel);
@@ -5708,17 +5716,6 @@ package android.app {
field public static final int STYLE_SPINNER = 0; // 0x0
}
- public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
- ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction);
- method public int describeContents();
- method public android.app.RemoteAction getUserAction();
- method public java.lang.CharSequence getUserMessage();
- method public void showAsDialog(android.app.Activity);
- method public void showAsNotification(android.content.Context, java.lang.String);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR;
- }
-
public final class RemoteAction implements android.os.Parcelable {
ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.RemoteAction clone();
@@ -6426,7 +6423,7 @@ package android.app.admin {
field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMERS = "android.app.extra.PROVISIONING_DISCLAIMERS";
field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_CONTENT = "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";
field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_HEADER = "android.app.extra.PROVISIONING_DISCLAIMER_HEADER";
- field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
+ field public static final deprecated java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
field public static final java.lang.String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION";
field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
@@ -24621,6 +24618,7 @@ package android.media.tv {
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+ field public static final java.lang.String COLUMN_LOCKED = "locked";
field public static final java.lang.String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
@@ -26280,16 +26278,6 @@ package android.net.sip {
package android.net.wifi {
- public final class IconInfo implements android.os.Parcelable {
- ctor public IconInfo(java.lang.String, byte[]);
- ctor public IconInfo(android.net.wifi.IconInfo);
- method public int describeContents();
- method public byte[] getData();
- method public java.lang.String getFilename();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.wifi.IconInfo> CREATOR;
- }
-
public class ScanResult implements android.os.Parcelable {
method public int describeContents();
method public boolean is80211mcResponder();
@@ -26538,7 +26526,8 @@ package android.net.wifi {
field public static final java.lang.String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS";
- field public static final java.lang.String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO";
+ field public static final java.lang.String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME";
+ field public static final java.lang.String EXTRA_ICON = "android.net.wifi.extra.ICON";
field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi";
field public static final java.lang.String EXTRA_NEW_STATE = "newState";
@@ -31197,7 +31186,6 @@ package android.os {
method public final android.util.SizeF readSizeF();
method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader);
method public final android.util.SparseBooleanArray readSparseBooleanArray();
- method public final android.util.SparseIntArray readSparseIntArray();
method public final java.lang.String readString();
method public final void readStringArray(java.lang.String[]);
method public final void readStringList(java.util.List<java.lang.String>);
@@ -31242,7 +31230,6 @@ package android.os {
method public final void writeSizeF(android.util.SizeF);
method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>);
method public final void writeSparseBooleanArray(android.util.SparseBooleanArray);
- method public final void writeSparseIntArray(android.util.SparseIntArray);
method public final void writeString(java.lang.String);
method public final void writeStringArray(java.lang.String[]);
method public final void writeStringList(java.util.List<java.lang.String>);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2cc0186e2e32..2bf01d1d2c85 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4441,6 +4441,14 @@ package android.app {
field public java.lang.String serviceDetails;
}
+ public final class AuthenticationRequiredException extends java.lang.SecurityException implements android.os.Parcelable {
+ ctor public AuthenticationRequiredException(java.lang.Throwable, android.app.PendingIntent);
+ method public int describeContents();
+ method public android.app.PendingIntent getUserAction();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.AuthenticationRequiredException> CREATOR;
+ }
+
public final class AutomaticZenRule implements android.os.Parcelable {
ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
ctor public AutomaticZenRule(android.os.Parcel);
@@ -5900,17 +5908,6 @@ package android.app {
field public static final int STYLE_SPINNER = 0; // 0x0
}
- public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
- ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction);
- method public int describeContents();
- method public android.app.RemoteAction getUserAction();
- method public java.lang.CharSequence getUserMessage();
- method public void showAsDialog(android.app.Activity);
- method public void showAsNotification(android.content.Context, java.lang.String);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR;
- }
-
public final class RemoteAction implements android.os.Parcelable {
ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.RemoteAction clone();
@@ -6654,7 +6651,7 @@ package android.app.admin {
field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMERS = "android.app.extra.PROVISIONING_DISCLAIMERS";
field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_CONTENT = "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";
field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_HEADER = "android.app.extra.PROVISIONING_DISCLAIMER_HEADER";
- field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
+ field public static final deprecated java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
field public static final java.lang.String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION";
field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
@@ -28564,16 +28561,6 @@ package android.net.wifi {
field public boolean truncated;
}
- public final class IconInfo implements android.os.Parcelable {
- ctor public IconInfo(java.lang.String, byte[]);
- ctor public IconInfo(android.net.wifi.IconInfo);
- method public int describeContents();
- method public byte[] getData();
- method public java.lang.String getFilename();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.wifi.IconInfo> CREATOR;
- }
-
public class RttManager {
method public void disableResponder(android.net.wifi.RttManager.ResponderCallback);
method public void enableResponder(android.net.wifi.RttManager.ResponderCallback);
@@ -29069,7 +29056,8 @@ package android.net.wifi {
field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason";
field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS";
- field public static final java.lang.String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO";
+ field public static final java.lang.String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME";
+ field public static final java.lang.String EXTRA_ICON = "android.net.wifi.extra.ICON";
field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi";
@@ -33925,7 +33913,6 @@ package android.os {
method public final android.util.SizeF readSizeF();
method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader);
method public final android.util.SparseBooleanArray readSparseBooleanArray();
- method public final android.util.SparseIntArray readSparseIntArray();
method public final java.lang.String readString();
method public final void readStringArray(java.lang.String[]);
method public final void readStringList(java.util.List<java.lang.String>);
@@ -33970,7 +33957,6 @@ package android.os {
method public final void writeSizeF(android.util.SizeF);
method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>);
method public final void writeSparseBooleanArray(android.util.SparseBooleanArray);
- method public final void writeSparseIntArray(android.util.SparseIntArray);
method public final void writeString(java.lang.String);
method public final void writeStringArray(java.lang.String[]);
method public final void writeStringList(java.util.List<java.lang.String>);
diff --git a/api/test-current.txt b/api/test-current.txt
index 8aa352de7804..da25aa954c25 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4307,6 +4307,14 @@ package android.app {
field public java.lang.String serviceDetails;
}
+ public final class AuthenticationRequiredException extends java.lang.SecurityException implements android.os.Parcelable {
+ ctor public AuthenticationRequiredException(java.lang.Throwable, android.app.PendingIntent);
+ method public int describeContents();
+ method public android.app.PendingIntent getUserAction();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.AuthenticationRequiredException> CREATOR;
+ }
+
public final class AutomaticZenRule implements android.os.Parcelable {
ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean);
ctor public AutomaticZenRule(android.os.Parcel);
@@ -5719,17 +5727,6 @@ package android.app {
field public static final int STYLE_SPINNER = 0; // 0x0
}
- public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
- ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction);
- method public int describeContents();
- method public android.app.RemoteAction getUserAction();
- method public java.lang.CharSequence getUserMessage();
- method public void showAsDialog(android.app.Activity);
- method public void showAsNotification(android.content.Context, java.lang.String);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR;
- }
-
public final class RemoteAction implements android.os.Parcelable {
ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
method public android.app.RemoteAction clone();
@@ -6452,7 +6449,7 @@ package android.app.admin {
field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMERS = "android.app.extra.PROVISIONING_DISCLAIMERS";
field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_CONTENT = "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";
field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_HEADER = "android.app.extra.PROVISIONING_DISCLAIMER_HEADER";
- field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
+ field public static final deprecated java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
field public static final java.lang.String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION";
field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
field public static final java.lang.String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
@@ -11751,6 +11748,15 @@ package android.database {
field protected final java.util.ArrayList<T> mObservers;
}
+ public final class PageViewCursor extends android.database.CursorWrapper implements android.database.CrossProcessCursor {
+ ctor public PageViewCursor(android.database.Cursor, int, int);
+ method public void fillWindow(int, android.database.CursorWindow);
+ method public android.database.CursorWindow getWindow();
+ method public boolean onMove(int, int);
+ method public static android.database.Cursor wrap(android.database.Cursor, android.os.Bundle);
+ field public static final java.lang.String EXTRA_AUTO_PAGED = "android.content.extra.AUTO_PAGED";
+ }
+
public class SQLException extends java.lang.RuntimeException {
ctor public SQLException();
ctor public SQLException(java.lang.String);
@@ -24723,6 +24729,7 @@ package android.media.tv {
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+ field public static final java.lang.String COLUMN_LOCKED = "locked";
field public static final java.lang.String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
@@ -26382,16 +26389,6 @@ package android.net.sip {
package android.net.wifi {
- public final class IconInfo implements android.os.Parcelable {
- ctor public IconInfo(java.lang.String, byte[]);
- ctor public IconInfo(android.net.wifi.IconInfo);
- method public int describeContents();
- method public byte[] getData();
- method public java.lang.String getFilename();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.net.wifi.IconInfo> CREATOR;
- }
-
public class ScanResult implements android.os.Parcelable {
method public int describeContents();
method public boolean is80211mcResponder();
@@ -26640,7 +26637,8 @@ package android.net.wifi {
field public static final java.lang.String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY";
field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS";
- field public static final java.lang.String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO";
+ field public static final java.lang.String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME";
+ field public static final java.lang.String EXTRA_ICON = "android.net.wifi.extra.ICON";
field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi";
field public static final java.lang.String EXTRA_NEW_STATE = "newState";
@@ -31320,7 +31318,6 @@ package android.os {
method public final android.util.SizeF readSizeF();
method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader);
method public final android.util.SparseBooleanArray readSparseBooleanArray();
- method public final android.util.SparseIntArray readSparseIntArray();
method public final java.lang.String readString();
method public final void readStringArray(java.lang.String[]);
method public final void readStringList(java.util.List<java.lang.String>);
@@ -31365,7 +31362,6 @@ package android.os {
method public final void writeSizeF(android.util.SizeF);
method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>);
method public final void writeSparseBooleanArray(android.util.SparseBooleanArray);
- method public final void writeSparseIntArray(android.util.SparseIntArray);
method public final void writeString(java.lang.String);
method public final void writeStringArray(java.lang.String[]);
method public final void writeStringList(java.util.List<java.lang.String>);
diff --git a/core/java/android/app/AuthenticationRequiredException.java b/core/java/android/app/AuthenticationRequiredException.java
new file mode 100644
index 000000000000..89609794a615
--- /dev/null
+++ b/core/java/android/app/AuthenticationRequiredException.java
@@ -0,0 +1,99 @@
+/*
+ * 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.app;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Specialization of {@link SecurityException} that is thrown when authentication is needed from the
+ * end user before viewing the content.
+ * <p>
+ * This exception is only appropriate where there is a concrete action the user can take to
+ * authorize and make forward progress, such as confirming or entering authentication credentials,
+ * or granting access via other means.
+ * <p class="note">
+ * Note: legacy code that receives this exception may treat it as a general
+ * {@link SecurityException}, and thus there is no guarantee that the action contained will be
+ * invoked by the user.
+ * </p>
+ */
+public final class AuthenticationRequiredException extends SecurityException implements Parcelable {
+ private static final String TAG = "AuthenticationRequiredException";
+
+ private final PendingIntent mUserAction;
+
+ /** {@hide} */
+ public AuthenticationRequiredException(Parcel in) {
+ this(new SecurityException(in.readString()), PendingIntent.CREATOR.createFromParcel(in));
+ }
+
+ /**
+ * Create an instance ready to be thrown.
+ *
+ * @param cause original cause with details designed for engineering
+ * audiences.
+ * @param userAction primary action that will initiate the recovery. This
+ * must launch an activity that is expected to set
+ * {@link Activity#setResult(int)} before finishing to
+ * communicate the final status of the recovery. For example,
+ * apps that observe {@link Activity#RESULT_OK} may choose to
+ * immediately retry their operation. If this exception was
+ * thrown from a {@link ContentProvider}, you should also send
+ * any relevant {@link ContentResolver#notifyChange} events to
+ * trigger reloading of data.
+ */
+ public AuthenticationRequiredException(Throwable cause, PendingIntent userAction) {
+ super(cause.getMessage());
+ mUserAction = Preconditions.checkNotNull(userAction);
+ }
+
+ /**
+ * Return primary action that will initiate the authorization.
+ */
+ public PendingIntent getUserAction() {
+ return mUserAction;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(getMessage());
+ mUserAction.writeToParcel(dest, flags);
+ }
+
+ public static final Creator<AuthenticationRequiredException> CREATOR =
+ new Creator<AuthenticationRequiredException>() {
+ @Override
+ public AuthenticationRequiredException createFromParcel(Parcel source) {
+ return new AuthenticationRequiredException(source);
+ }
+
+ @Override
+ public AuthenticationRequiredException[] newArray(int size) {
+ return new AuthenticationRequiredException[size];
+ }
+ };
+}
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index eff77b56068e..44fefd38048c 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1670,8 +1670,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
- }
- if (mLoaderManager != null) {
+ } else if (mLoaderManager != null) {
mLoaderManager.doStart();
}
}
diff --git a/core/java/android/app/RecoverableSecurityException.java b/core/java/android/app/RecoverableSecurityException.java
index 8612f186ade4..a503a46a29dc 100644
--- a/core/java/android/app/RecoverableSecurityException.java
+++ b/core/java/android/app/RecoverableSecurityException.java
@@ -45,7 +45,8 @@ import com.android.internal.util.Preconditions;
* Note: legacy code that receives this exception may treat it as a general
* {@link SecurityException}, and thus there is no guarantee that the messages
* contained will be shown to the end user.
- * </p>
+ *
+ * @hide
*/
public final class RecoverableSecurityException extends SecurityException implements Parcelable {
private static final String TAG = "RecoverableSecurityException";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3be4dd85262f..391885dad45a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -535,18 +535,10 @@ public class DevicePolicyManager {
= "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION";
/**
- * A String extra that, holds the email address of the account which a managed profile is
- * created for. Used with {@link #ACTION_PROVISION_MANAGED_PROFILE} and
- * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE}.
- *
- * <p> This extra is part of the {@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}.
- *
- * <p> If the {@link #ACTION_PROVISION_MANAGED_PROFILE} intent that starts managed provisioning
- * contains this extra, it is forwarded in the
- * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} intent to the mobile
- * device management application that was set as the profile owner during provisioning.
- * It is usually used to avoid that the user has to enter their email address twice.
+ * @deprecated From {@link android.os.Build.VERSION_CODES#O}, never used while provisioning the
+ * device.
*/
+ @Deprecated
public static final String EXTRA_PROVISIONING_EMAIL_ADDRESS
= "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4de64c41e913..59b022ded9bf 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -628,4 +628,6 @@ interface IPackageManager {
ParceledListSlice getSharedLibraries(int flags, int userId);
boolean canRequestPackageInstalls(String packageName, int userId);
+
+ void deletePreloadsFileCache();
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2d073ab2b168..fb69986686bd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -184,7 +184,7 @@ public class PackageParser {
private static final String TAG_RESTRICT_UPDATE = "restrict-update";
private static final String TAG_USES_SPLIT = "uses-split";
- // STOPSHIP remove the ability to expose components via meta-data
+ // [b/36551762] STOPSHIP remove the ability to expose components via meta-data
// Temporary workaround; allow meta-data to expose components to instant apps
private static final String META_DATA_INSTANT_APPS = "instantapps.clients.allowed";
diff --git a/core/java/android/database/PageViewCursor.java b/core/java/android/database/PageViewCursor.java
index 5f42f30be5ba..44727a0d300e 100644
--- a/core/java/android/database/PageViewCursor.java
+++ b/core/java/android/database/PageViewCursor.java
@@ -18,13 +18,13 @@ package android.database;
import static com.android.internal.util.Preconditions.checkArgument;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.ContentResolver;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.MathUtils;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
/**
@@ -34,11 +34,10 @@ import com.android.internal.util.ArrayUtils;
*
* @hide
*/
+@TestApi
public final class PageViewCursor extends CursorWrapper implements CrossProcessCursor {
- /**
- * An in internal extra added to results that are auto-paged using the wrapper.
- */
+ /** An extra added to results that are auto-paged using the wrapper. */
public static final String EXTRA_AUTO_PAGED = "android.content.extra.AUTO_PAGED";
private static final String TAG = "PageViewCursor";
@@ -56,7 +55,6 @@ public final class PageViewCursor extends CursorWrapper implements CrossProcessC
/**
* @see PageViewCursor#wrap(Cursor, Bundle)
*/
- @VisibleForTesting
public PageViewCursor(Cursor cursor, int offset, int limit) {
super(cursor);
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 8c13cc893cc7..0218cb5f3058 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -771,15 +771,15 @@ public final class Sensor {
3, // SENSOR_TYPE_GEOMAGNETIC_FIELD
3, // SENSOR_TYPE_ORIENTATION
3, // SENSOR_TYPE_GYROSCOPE
- 3, // SENSOR_TYPE_LIGHT
- 3, // SENSOR_TYPE_PRESSURE
- 3, // SENSOR_TYPE_TEMPERATURE
- 3, // SENSOR_TYPE_PROXIMITY
+ 1, // SENSOR_TYPE_LIGHT
+ 1, // SENSOR_TYPE_PRESSURE
+ 1, // SENSOR_TYPE_TEMPERATURE
+ 1, // SENSOR_TYPE_PROXIMITY
3, // SENSOR_TYPE_GRAVITY
3, // SENSOR_TYPE_LINEAR_ACCELERATION
5, // SENSOR_TYPE_ROTATION_VECTOR
- 3, // SENSOR_TYPE_RELATIVE_HUMIDITY
- 3, // SENSOR_TYPE_AMBIENT_TEMPERATURE
+ 1, // SENSOR_TYPE_RELATIVE_HUMIDITY
+ 1, // SENSOR_TYPE_AMBIENT_TEMPERATURE
6, // SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED
4, // SENSOR_TYPE_GAME_ROTATION_VECTOR
6, // SENSOR_TYPE_GYROSCOPE_UNCALIBRATED
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 7a39d239f84b..2e35a51afe46 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -898,6 +898,9 @@ public final class Parcel {
}
}
+ /**
+ * @hide
+ */
public final void writeSparseIntArray(SparseIntArray val) {
if (val == null) {
writeInt(-1);
@@ -2323,6 +2326,7 @@ public final class Parcel {
/**
* Read and return a new SparseIntArray object from the parcel at the current
* dataPosition(). Returns null if the previously written array object was null.
+ * @hide
*/
public final SparseIntArray readSparseIntArray() {
int N = readInt();
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index f69c996c5368..31376587e144 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -92,6 +92,12 @@ public class Process {
public static final int VPN_UID = 1016;
/**
+ * Defines the UID/GID for keystore.
+ * @hide
+ */
+ public static final int KEYSTORE_UID = 1017;
+
+ /**
* Defines the UID/GID for the NFC service process.
* @hide
*/
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 56d4ff79eb04..9e56e01c24c4 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1444,14 +1444,15 @@ public final class DocumentsContract {
* <p>Providers are required to show confirmation UI for all new permissions granted
* for the linked document.
*
- * <p>If list of recipients is known, then it can be passed in options as
- * {@link Intent#EXTRA_EMAIL} as either a string or list of strings. Note, that
+ * <p>If list of recipients is known, then it should be passed in options as
+ * {@link Intent#EXTRA_EMAIL} as a list of email addresses. Note, that
* this is just a hint for the provider, which can ignore the list. In either
* case the provider is required to show a UI for letting the user confirm
* any new permission grants.
*
- * <p>Note, that the entire <code>options</code> bundle is send to the provider.
- * Make sure that you trust the provider before passing any sensitive information.
+ * <p>Note, that the entire <code>options</code> bundle will be sent to the provider
+ * backing the passed <code>uri</code>. Make sure that you trust the provider
+ * before passing any sensitive information.
*
* <p>Since this API may show a UI, it cannot be called from background.
*
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 620d33a5e915..9e68afbb5297 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -38,7 +38,7 @@ import static android.provider.DocumentsContract.isTreeUri;
import android.Manifest;
import android.annotation.CallSuper;
import android.annotation.Nullable;
-import android.app.RecoverableSecurityException;
+import android.app.AuthenticationRequiredException;
import android.content.ClipDescription;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -235,10 +235,6 @@ public abstract class DocumentsProvider extends ContentProvider {
* {@link Document#COLUMN_DOCUMENT_ID}. You must allocate a new
* {@link Document#COLUMN_DOCUMENT_ID} to represent the document, which must
* not change once returned.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.
*
* @param parentDocumentId the parent directory to create the new document
* under.
@@ -247,6 +243,10 @@ public abstract class DocumentsProvider extends ContentProvider {
* @param displayName the display name of the new document. The provider may
* alter this name to meet any internal constraints, such as
* avoiding conflicting names.
+
+ * @throws AuthenticationRequiredException If authentication is required from the user (such as
+ * login credentials), but it is not guaranteed that the client will handle this
+ * properly.
*/
@SuppressWarnings("unused")
public String createDocument(String parentDocumentId, String mimeType, String displayName)
@@ -262,15 +262,14 @@ public abstract class DocumentsProvider extends ContentProvider {
* URI permission grants will be updated to point at the new document. If
* the original {@link Document#COLUMN_DOCUMENT_ID} is still valid after the
* rename, return {@code null}.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.
*
* @param documentId the document to rename.
* @param displayName the updated display name of the document. The provider
* may alter this name to meet any internal constraints, such as
* avoiding conflicting names.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
*/
@SuppressWarnings("unused")
public String renameDocument(String documentId, String displayName)
@@ -286,12 +285,11 @@ public abstract class DocumentsProvider extends ContentProvider {
* call (such as documents inside a directory) the implementor is
* responsible for revoking those permissions using
* {@link #revokeDocumentPermission(String)}.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.
*
* @param documentId the document to delete.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
*/
@SuppressWarnings("unused")
public void deleteDocument(String documentId) throws FileNotFoundException {
@@ -305,13 +303,12 @@ public abstract class DocumentsProvider extends ContentProvider {
* the same document provider. Upon completion returns the document id of
* the copied document at the target destination. {@code null} must never
* be returned.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.
*
* @param sourceDocumentId the document to copy.
* @param targetParentDocumentId the target document to be copied into as a child.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
*/
@SuppressWarnings("unused")
public String copyDocument(String sourceDocumentId, String targetParentDocumentId)
@@ -329,15 +326,14 @@ public abstract class DocumentsProvider extends ContentProvider {
*
* <p>It's the responsibility of the provider to revoke grants if the document
* is no longer accessible using <code>sourceDocumentId</code>.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.
*
* @param sourceDocumentId the document to move.
* @param sourceParentDocumentId the parent of the document to move.
* @param targetParentDocumentId the target document to be a new parent of the
* source document.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
*/
@SuppressWarnings("unused")
public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
@@ -355,11 +351,11 @@ public abstract class DocumentsProvider extends ContentProvider {
* <p>It's the responsibility of the provider to revoke grants if the document is
* removed from the last parent, and effectively the document is deleted.
*
- * <p>{@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.
* @param documentId the document to remove.
* @param parentDocumentId the parent of the document to move.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
*/
@SuppressWarnings("unused")
public void removeDocument(String documentId, String parentDocumentId)
@@ -377,9 +373,6 @@ public abstract class DocumentsProvider extends ContentProvider {
* <p>This API assumes that document ID has enough info to infer the root.
* Different roots should use different document ID to refer to the same
* document.
- * <p>{@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.perly.
*
*
* @param parentDocumentId the document from which the path starts if not null,
@@ -388,6 +381,9 @@ public abstract class DocumentsProvider extends ContentProvider {
* @return the path of the requested document. If parentDocumentId is null
* returned root ID must not be null. If parentDocumentId is not null
* returned root ID must be null.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
*/
public Path findDocumentPath(@Nullable String parentDocumentId, String childDocumentId)
throws FileNotFoundException {
@@ -397,7 +393,7 @@ public abstract class DocumentsProvider extends ContentProvider {
/**
* Creates an intent sender for a web link, if the document is web linkable.
* <p>
- * {@link RecoverableSecurityException} can be thrown if user does not have
+ * {@link AuthenticationRequiredException} can be thrown if user does not have
* sufficient permission for the linked document. Before any new permissions
* are granted for the linked document, a visible UI must be shown, so the
* user can explicitly confirm whether the permission grants are expected.
@@ -414,6 +410,9 @@ public abstract class DocumentsProvider extends ContentProvider {
*
* @param documentId the document to create a web link intent for.
* @param options additional information, such as list of recipients. Optional.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
*
* @see DocumentsContract.Document#FLAG_WEB_LINKABLE
* @see android.app.PendingIntent#getIntentSender
@@ -436,9 +435,6 @@ public abstract class DocumentsProvider extends ContentProvider {
* android.database.ContentObserver, boolean)} with
* {@link DocumentsContract#buildRootsUri(String)} to notify the system.
* <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), or returned as part of
- * Cursor's bundle. It is not guaranteed that the client will handle this properly.
*
* @param projection list of {@link Root} columns to put into the cursor. If
* {@code null} all supported columns should be included.
@@ -452,10 +448,6 @@ public abstract class DocumentsProvider extends ContentProvider {
* sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order, and
* limited to only return the 64 most recently modified documents.
* <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), or returned as part of
- * Cursor's bundle. It is not guaranteed that the client will handle this properly.
- * <p>
* Recent documents do not support change notifications.
*
* @param projection list of {@link Document} columns to put into the
@@ -472,16 +464,14 @@ public abstract class DocumentsProvider extends ContentProvider {
/**
* Return metadata for the single requested document. You should avoid
* making network requests to keep this request fast.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), or returned as part of
- * Cursor's bundle. It is not guaranteed that the client will handle this properly.
- *
*
* @param documentId the document to return.
* @param projection list of {@link Document} columns to put into the
* cursor. If {@code null} all supported columns should be
* included.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
*/
public abstract Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException;
@@ -509,11 +499,6 @@ public abstract class DocumentsProvider extends ContentProvider {
* you can call {@link ContentResolver#notifyChange(Uri,
* android.database.ContentObserver, boolean)} with that Uri to send change
* notifications.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), or returned as part of
- * Cursor's bundle. It is not guaranteed that the client will handle this properly.
- *
*
* @param parentDocumentId the directory to return children for.
* @param projection list of {@link Document} columns to put into the
@@ -525,6 +510,9 @@ public abstract class DocumentsProvider extends ContentProvider {
* may be unordered. This ordering is a hint that can be used to
* prioritize how data is fetched from the network, but UI may
* always enforce a specific ordering.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
* @see DocumentsContract#EXTRA_LOADING
* @see DocumentsContract#EXTRA_INFO
* @see DocumentsContract#EXTRA_ERROR
@@ -552,10 +540,6 @@ public abstract class DocumentsProvider extends ContentProvider {
* you can call {@link ContentResolver#notifyChange(Uri,
* android.database.ContentObserver, boolean)} with that Uri to send change
* notifications.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), or returned as part of
- * Cursor's bundle. It is not guaranteed that the client will handle this properly.
*
* @param parentDocumentId the directory to return children for.
* @param projection list of {@link Document} columns to put into the
@@ -567,6 +551,9 @@ public abstract class DocumentsProvider extends ContentProvider {
* will be used, which may be unordered. See
* {@link ContentResolver#QUERY_ARG_SORT_COLUMNS} for
* details.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
*
* @see DocumentsContract#EXTRA_LOADING
* @see DocumentsContract#EXTRA_INFO
@@ -609,16 +596,16 @@ public abstract class DocumentsProvider extends ContentProvider {
* String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri,
* android.database.ContentObserver, boolean)} with that Uri to send change
* notifications.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), or returned as part of
- * Cursor's bundle. It is not guaranteed that the client will handle this properly.
*
* @param rootId the root to search under.
* @param query string to match documents against.
* @param projection list of {@link Document} columns to put into the
* cursor. If {@code null} all supported columns should be
* included.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
+ *
* @see DocumentsContract#EXTRA_LOADING
* @see DocumentsContract#EXTRA_INFO
* @see DocumentsContract#EXTRA_ERROR
@@ -641,9 +628,9 @@ public abstract class DocumentsProvider extends ContentProvider {
* implementation queries {@link #queryDocument(String, String[])}, so
* providers may choose to override this as an optimization.
* <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
*/
public String getDocumentType(String documentId) throws FileNotFoundException {
final Cursor cursor = queryDocument(documentId, null);
@@ -669,15 +656,14 @@ public abstract class DocumentsProvider extends ContentProvider {
* <p>
* If you block while downloading content, you should periodically check
* {@link CancellationSignal#isCanceled()} to abort abandoned open requests.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.
*
* @param documentId the document to return.
* @param mode the mode to open with, such as 'r', 'w', or 'rw'.
* @param signal used by the caller to signal if the request should be
* cancelled. May be null.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
* @see ParcelFileDescriptor#open(java.io.File, int, android.os.Handler,
* OnCloseListener)
* @see ParcelFileDescriptor#createReliablePipe()
@@ -697,15 +683,14 @@ public abstract class DocumentsProvider extends ContentProvider {
* If you perform expensive operations to download or generate a thumbnail,
* you should periodically check {@link CancellationSignal#isCanceled()} to
* abort abandoned thumbnail requests.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.
*
* @param documentId the document to return.
* @param sizeHint hint of the optimal thumbnail dimensions.
* @param signal used by the caller to signal if the request should be
* cancelled. May be null.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
* @see Document#FLAG_SUPPORTS_THUMBNAIL
*/
@SuppressWarnings("unused")
@@ -723,10 +708,6 @@ public abstract class DocumentsProvider extends ContentProvider {
* matching the specified MIME type filter.
* <p>
* Virtual documents must have at least one streamable format.
- * <p>
- * {@link RecoverableSecurityException} can be thrown if more input is required
- * from the user (such as insufficient permission), but it is not guaranteed that
- * the client will handle this properly.
*
* @param documentId the document to return.
* @param mimeTypeFilter the MIME type filter for the requested format. May
@@ -735,6 +716,9 @@ public abstract class DocumentsProvider extends ContentProvider {
* provider.
* @param signal used by the caller to signal if the request should be
* cancelled. May be null.
+ * @throws AuthenticationRequiredException If authentication is required from
+ * the user (such as login credentials), but it is not guaranteed
+ * that the client will handle this properly.
* @see #getDocumentStreamTypes(String, String)
*/
@SuppressWarnings("unused")
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 353dfed343e0..9a2e0bb9a0b8 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -322,6 +322,8 @@ public class StaticLayout extends Layout {
/**
* Enables or disables paragraph justification. The default value is disabled (false).
+ * If the last line is too short for justification, the last line will be displayed with
+ * the alignment set by {@link #setAlignment}.
*
* @param justify true for enabling and false for disabling paragraph justification.
* @return this builder, useful for chaining.
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 7b99d07d2457..78d18fdbca5f 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -75,6 +75,7 @@ public class ListPopupWindow implements ShowableListMenu {
private int mDropDownWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
private boolean mDropDownVerticalOffsetSet;
private boolean mIsAnimatedFromAnchor = true;
+ private boolean mOverlapAnchor;
private int mDropDownGravity = Gravity.NO_GRAVITY;
@@ -672,6 +673,7 @@ public class ListPopupWindow implements ShowableListMenu {
mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
mPopup.setTouchInterceptor(mTouchInterceptor);
mPopup.setEpicenterBounds(mEpicenterBounds);
+ mPopup.setOverlapAnchor(mOverlapAnchor);
mPopup.showAsDropDown(getAnchorView(), mDropDownHorizontalOffset,
mDropDownVerticalOffset, mDropDownGravity);
mDropDownList.setSelection(ListView.INVALID_POSITION);
@@ -1245,6 +1247,13 @@ public class ListPopupWindow implements ShowableListMenu {
return listContent + otherHeights;
}
+ /**
+ * @hide
+ */
+ public void setOverlapAnchor(boolean overlap) {
+ mOverlapAnchor = overlap;
+ }
+
private class PopupDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index d096baf3c943..9f10531841e3 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -55,6 +55,7 @@ import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+import android.view.WindowManagerGlobal;
import com.android.internal.R;
@@ -137,6 +138,7 @@ public class PopupWindow {
private final int[] mTmpDrawingLocation = new int[2];
private final int[] mTmpScreenLocation = new int[2];
+ private final int[] mTmpAppLocation = new int[2];
private final Rect mTempRect = new Rect();
private Context mContext;
@@ -242,6 +244,9 @@ public class PopupWindow {
private final OnScrollChangedListener mOnScrollChangedListener = this::alignToAnchor;
+ private final View.OnLayoutChangeListener mOnLayoutChangeListener =
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> alignToAnchor();
+
private int mAnchorXoff;
private int mAnchorYoff;
private int mAnchoredGravity;
@@ -1238,7 +1243,8 @@ public class PopupWindow {
mIsShowing = true;
mIsDropdown = true;
- final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
+ final WindowManager.LayoutParams p =
+ createPopupLayoutParams(anchor.getApplicationWindowToken());
preparePopup(p);
final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
@@ -1547,13 +1553,21 @@ public class PopupWindow {
}
// Initially, align to the bottom-left corner of the anchor plus offsets.
+ final int[] appScreenLocation = mTmpAppLocation;
+ final View appRootView = getAppRootView(anchor);
+ appRootView.getLocationOnScreen(appScreenLocation);
+
+ final int[] screenLocation = mTmpScreenLocation;
+ anchor.getLocationOnScreen(screenLocation);
+
final int[] drawingLocation = mTmpDrawingLocation;
- anchor.getLocationInWindow(drawingLocation);
+ drawingLocation[0] = screenLocation[0] - appScreenLocation[0];
+ drawingLocation[1] = screenLocation[1] - appScreenLocation[1];
outParams.x = drawingLocation[0] + xOffset;
outParams.y = drawingLocation[1] + anchorHeight + yOffset;
final Rect displayFrame = new Rect();
- anchor.getWindowVisibleDisplayFrame(displayFrame);
+ appRootView.getWindowVisibleDisplayFrame(displayFrame);
if (width == MATCH_PARENT) {
width = displayFrame.right - displayFrame.left;
}
@@ -1574,9 +1588,6 @@ public class PopupWindow {
outParams.x -= width - anchorWidth;
}
- final int[] screenLocation = mTmpScreenLocation;
- anchor.getLocationOnScreen(screenLocation);
-
// First, attempt to fit the popup vertically without resizing.
final boolean fitsVertical = tryFitVertical(outParams, yOffset, height,
anchorHeight, drawingLocation[1], screenLocation[1], displayFrame.top,
@@ -1595,7 +1606,9 @@ public class PopupWindow {
scrollY + height + anchorHeight + yOffset);
if (allowScroll && anchor.requestRectangleOnScreen(r, true)) {
// Reset for the new anchor position.
- anchor.getLocationInWindow(drawingLocation);
+ anchor.getLocationOnScreen(screenLocation);
+ drawingLocation[0] = screenLocation[0] - appScreenLocation[0];
+ drawingLocation[1] = screenLocation[1] - appScreenLocation[1];
outParams.x = drawingLocation[0] + xOffset;
outParams.y = drawingLocation[1] + anchorHeight + yOffset;
@@ -1620,17 +1633,32 @@ public class PopupWindow {
int anchorHeight, int drawingLocationY, int screenLocationY, int displayFrameTop,
int displayFrameBottom, boolean allowResize) {
final int winOffsetY = screenLocationY - drawingLocationY;
- final int anchorTopInScreen = outParams.y + winOffsetY;
- final int spaceBelow = displayFrameBottom - anchorTopInScreen;
- if (anchorTopInScreen >= 0 && height <= spaceBelow) {
+ final int popupScreenTop = outParams.y + winOffsetY;
+ final int spaceBelow = displayFrameBottom - popupScreenTop;
+ if (popupScreenTop >= 0 && height <= spaceBelow) {
return true;
}
- final int spaceAbove = anchorTopInScreen - anchorHeight - displayFrameTop;
+ final int popupScreenBottom;
+ if (mOverlapAnchor) {
+ // popupScreenTop equals the anchor's top at this point.
+ // When shown above the anchor, an overlapping popup's bottom should be aligned with
+ // the anchor's bottom.
+ popupScreenBottom = popupScreenTop + anchorHeight;
+ } else {
+ // popupScreenTop equals the anchor's bottom at this point.
+ // When shown above the anchor, a non-overlapping popup's bottom is aligned with
+ // the anchor's top.
+ popupScreenBottom = popupScreenTop - anchorHeight;
+ }
+ final int spaceAbove = popupScreenBottom - displayFrameTop;
if (height <= spaceAbove) {
// Move everything up.
if (mOverlapAnchor) {
- yOffset += anchorHeight;
+ // Add one anchorHeight to compensate for the correction made at the start of
+ // findDropDownPosition, and another to account for being aligned to the anchor's
+ // bottom, not top.
+ yOffset += anchorHeight * 2;
}
outParams.y = drawingLocationY - height + yOffset;
@@ -1778,7 +1806,8 @@ public class PopupWindow {
Rect displayFrame = null;
final Rect visibleDisplayFrame = new Rect();
- anchor.getWindowVisibleDisplayFrame(visibleDisplayFrame);
+ final View appView = getAppRootView(anchor);
+ appView.getWindowVisibleDisplayFrame(visibleDisplayFrame);
if (ignoreBottomDecorations) {
// In the ignore bottom decorations case we want to
// still respect all other decorations so we use the inset visible
@@ -2225,6 +2254,7 @@ public class PopupWindow {
final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
if (anchorRoot != null) {
anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
+ anchorRoot.removeOnLayoutChangeListener(mOnLayoutChangeListener);
}
mAnchor = null;
@@ -2243,6 +2273,7 @@ public class PopupWindow {
final View anchorRoot = anchor.getRootView();
anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
+ anchorRoot.addOnLayoutChangeListener(mOnLayoutChangeListener);
mAnchor = new WeakReference<>(anchor);
mAnchorRoot = new WeakReference<>(anchorRoot);
@@ -2266,6 +2297,15 @@ public class PopupWindow {
}
}
+ private View getAppRootView(View anchor) {
+ final View appWindowView = WindowManagerGlobal.getInstance().getWindowView(
+ anchor.getApplicationWindowToken());
+ if (appWindowView != null) {
+ return appWindowView;
+ }
+ return anchor.getRootView();
+ }
+
private class PopupDecorView extends FrameLayout {
private TransitionListenerAdapter mPendingExitListener;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f2a7f25decc1..8d0ea08ba593 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3743,7 +3743,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
- * Enables or disables full justification. The default value is false.
+ * Enables or disables full justification. The default value is false. If the last line is too
+ * short for justification, the last line will be displayed with the alignment set by
+ * {@link android.view.View#setTextAlignment}.
*
* @see #getJustify()
*/
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6aa77665db51..9d2141d814b9 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -31,12 +31,15 @@ import android.os.BatteryStats;
import android.os.Build;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.IBatteryPropertiesRegistrar;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFormatException;
import android.os.Parcelable;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.WorkSource;
@@ -3151,6 +3154,7 @@ public class BatteryStatsImpl extends BatteryStats {
boolean unpluggedScreenOff = unplugged && screenOff;
if (unpluggedScreenOff != mOnBatteryScreenOffTimeBase.isRunning()) {
updateKernelWakelocksLocked();
+ updateBatteryPropertiesLocked();
if (DEBUG_ENERGY_CPU) {
Slog.d(TAG, "Updating cpu time because screen is now " +
(unpluggedScreenOff ? "off" : "on"));
@@ -3160,6 +3164,16 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ private void updateBatteryPropertiesLocked() {
+ try {
+ IBatteryPropertiesRegistrar registrar = IBatteryPropertiesRegistrar.Stub.asInterface(
+ ServiceManager.getService("batteryproperties"));
+ registrar.scheduleUpdate();
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ }
+
public void addIsolatedUidLocked(int isolatedUid, int appUid) {
mIsolatedUids.put(isolatedUid, appUid);
}
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index ebcec5cb5a42..6dff8b41eeb4 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -381,6 +381,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
if (parentView != null) {
// This menu is a cascading submenu anchored to a parent view.
+ popupWindow.setAnchorView(parentView);
popupWindow.setTouchModal(false);
popupWindow.setEnterTransition(null);
@@ -388,42 +389,30 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey
final boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT;
mLastPosition = nextMenuPosition;
- // A popup anchored to mAnchorView with (0,0) offset would be shown at this position.
- final int[] offsetOrigin = new int[2];
- mAnchorView.getLocationOnScreen(offsetOrigin);
- offsetOrigin[1] += mAnchorView.getHeight();
-
- final int[] parentViewScreenLocation = new int[2];
- parentView.getLocationOnScreen(parentViewScreenLocation);
-
- // Translate the parent view location into the offset coordinate space.
- // If used as horizontal/vertical offsets, these values would position the submenu
- // at the exact same position as the parent item.
- final int parentOffsetLeft = parentViewScreenLocation[0] - offsetOrigin[0];
- final int parentOffsetTop = parentViewScreenLocation[1] - offsetOrigin[1];
-
- // Adjust the horizontal offset to display the submenu to the right or to the left
+ // Compute the horizontal offset to display the submenu to the right or to the left
// of the parent item.
// By now, mDropDownGravity is the resolved absolute gravity, so
// this should work in both LTR and RTL.
final int x;
if ((mDropDownGravity & Gravity.RIGHT) == Gravity.RIGHT) {
if (showOnRight) {
- x = parentOffsetLeft + menuWidth;
+ x = menuWidth;
} else {
- x = parentOffsetLeft - parentView.getWidth();
+ x = -parentView.getWidth();
}
} else {
if (showOnRight) {
- x = parentOffsetLeft + parentView.getWidth();
+ x = parentView.getWidth();
} else {
- x = parentOffsetLeft - menuWidth;
+ x = -menuWidth;
}
}
popupWindow.setHorizontalOffset(x);
- // Use the same vertical offset as the parent item.
- popupWindow.setVerticalOffset(parentOffsetTop);
+ // Align with the top edge of the parent view (or the bottom edge when the submenu is
+ // flipped vertically).
+ popupWindow.setOverlapAnchor(true);
+ popupWindow.setVerticalOffset(0);
} else {
if (mHasXOffset) {
popupWindow.setHorizontalOffset(mXOffset);
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index a8d683028c13..de5e505af3da 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -621,7 +621,7 @@ static void ToColor_SA8(SkColor dst[], const void* src, int width, SkColorTable*
const uint8_t* s = (const uint8_t*)src;
do {
uint8_t c = *s++;
- *dst++ = SkColorSetARGB(c, c, c, c);
+ *dst++ = SkColorSetARGB(c, 0, 0, 0);
} while (--width != 0);
}
@@ -727,6 +727,50 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable));
}
+static bool bitmapCopyTo(SkBitmap* dst, SkColorType dstCT, const SkBitmap& src,
+ SkBitmap::Allocator* alloc) {
+ // Skia does not support copying from kAlpha8 to types that are not alpha only.
+ // We will handle this case here.
+ if (kAlpha_8_SkColorType == src.colorType() && kAlpha_8_SkColorType != dstCT) {
+ SkAutoPixmapUnlock srcUnlocker;
+ if (!src.requestLock(&srcUnlocker)) {
+ return false;
+ }
+ SkPixmap srcPixmap = srcUnlocker.pixmap();
+
+ SkImageInfo dstInfo = src.info().makeColorType(dstCT);
+ if (!dst->setInfo(dstInfo)) {
+ return false;
+ }
+ if (!dst->tryAllocPixels(alloc, nullptr)) {
+ return false;
+ }
+
+ switch (dstCT) {
+ case kRGBA_8888_SkColorType:
+ case kBGRA_8888_SkColorType: {
+ for (int y = 0; y < src.height(); y++) {
+ const uint8_t* srcRow = srcPixmap.addr8(0, y);
+ uint32_t* dstRow = dst->getAddr32(0, y);
+ ToColor_SA8(dstRow, srcRow, src.width(), nullptr);
+ }
+ return true;
+ }
+ case kRGB_565_SkColorType: {
+ for (int y = 0; y < src.height(); y++) {
+ uint16_t* dstRow = dst->getAddr16(0, y);
+ memset(dstRow, 0, sizeof(uint16_t) * src.width());
+ }
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+
+ return src.copyTo(dst, dstCT, alloc);
+}
+
static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
jint dstConfigHandle, jboolean isMutable) {
SkBitmap src;
@@ -743,7 +787,7 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
SkBitmap result;
HeapAllocator allocator;
- if (!src.copyTo(&result, dstCT, &allocator)) {
+ if (!bitmapCopyTo(&result, dstCT, src, &allocator)) {
return NULL;
}
auto bitmap = allocator.getStorageObjAndReset();
@@ -754,7 +798,7 @@ static Bitmap* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& ds
SkBitmap result;
AshmemPixelAllocator allocator(env);
- if (!src.copyTo(&result, dstCT, &allocator)) {
+ if (!bitmapCopyTo(&result, dstCT, src, &allocator)) {
return NULL;
}
auto bitmap = allocator.getStorageObjAndReset();
diff --git a/core/res/res/drawable-hdpi/stat_notify_mmcc_indication_icn.png b/core/res/res/drawable-hdpi/stat_notify_mmcc_indication_icn.png
new file mode 100644
index 000000000000..d7049517dc2a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_mmcc_indication_icn.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_notify_mmcc_indication_icn.png b/core/res/res/drawable-xhdpi/stat_notify_mmcc_indication_icn.png
new file mode 100644
index 000000000000..5dfc89ec1856
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/stat_notify_mmcc_indication_icn.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_mmcc_indication_icn.png b/core/res/res/drawable-xxhdpi/stat_notify_mmcc_indication_icn.png
new file mode 100644
index 000000000000..a648b0bffcbf
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/stat_notify_mmcc_indication_icn.png
Binary files differ
diff --git a/core/res/res/values-ldrtl-television/config.xml b/core/res/res/values-ldrtl-television/config.xml
index 503b9022aa3c..2a5a0c9a1f76 100644
--- a/core/res/res/values-ldrtl-television/config.xml
+++ b/core/res/res/values-ldrtl-television/config.xml
@@ -22,7 +22,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- The default gravity for the picture-in-picture window.
- Currently, this maps to Gravity.TOP | Gravity.LEFT -->
- <integer name="config_defaultPictureInPictureGravity">0x33</integer>
+ Currently, this maps to Gravity.BOTTOM | Gravity.LEFT -->
+ <integer name="config_defaultPictureInPictureGravity">0x53</integer>
</resources>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 1987ac454d86..78eeee99150a 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -34,6 +34,6 @@
<string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">56x27</string>
<!-- The default gravity for the picture-in-picture window.
- Currently, this maps to Gravity.TOP | Gravity.RIGHT -->
- <integer name="config_defaultPictureInPictureGravity">0x35</integer>
+ Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
+ <integer name="config_defaultPictureInPictureGravity">0x55</integer>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d4db258bd13d..d1c590069c94 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4601,4 +4601,10 @@
<!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for others -->
<string name="etws_primary_default_message_others"></string>
+
+ <!-- Title of notification when UE fails to register network with MM reject cause code. -->
+ <string name="mmcc_authentication_reject">SIM not allowed</string>
+ <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned</string>
+ <string name="mmcc_illegal_ms">SIM not allowed</string>
+ <string name="mmcc_illegal_me">Phone not allowed</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4ef3922aa1fe..11cde7a573ed 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1326,6 +1326,8 @@
<java-symbol type="drawable" name="ic_sim_card_multi_24px_clr" />
<java-symbol type="drawable" name="ic_sim_card_multi_48px_clr" />
+ <java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" />
+
<java-symbol type="drawable" name="ic_account_circle" />
<java-symbol type="color" name="user_icon_1" />
<java-symbol type="color" name="user_icon_2" />
@@ -1911,6 +1913,10 @@
<java-symbol type="string" name="low_internal_storage_view_text" />
<java-symbol type="string" name="low_internal_storage_view_text_no_boot" />
<java-symbol type="string" name="low_internal_storage_view_title" />
+ <java-symbol type="string" name="mmcc_authentication_reject" />
+ <java-symbol type="string" name="mmcc_imsi_unknown_in_hlr" />
+ <java-symbol type="string" name="mmcc_illegal_ms" />
+ <java-symbol type="string" name="mmcc_illegal_me" />
<java-symbol type="string" name="notification_listener_binding_label" />
<java-symbol type="string" name="vr_listener_binding_label" />
<java-symbol type="string" name="condition_provider_service_binding_label" />
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 042bac6441a9..228d9500180a 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -358,10 +358,10 @@ public class Typeface {
FileChannel.MapMode.READ_ONLY, 0, fontSize);
int style = result.getStyle();
int weight = (style & BOLD) != 0 ? 700 : 400;
- // TODO: this method should be
- // create(fd, ttcIndex, fontVariationSettings, style).
+ final ArrayList<FontConfig.Axis> axes = FontListParser.parseFontVariationSettings(
+ result.getFontVariationSettings());
if (!fontFamily.addFontFromBuffer(fontBuffer, result.getTtcIndex(),
- null, weight,
+ axes.toArray(new FontConfig.Axis[axes.size()]), weight,
(style & ITALIC) == 0 ? Builder.NORMAL : Builder.ITALIC)) {
Log.e(TAG, "Error creating font " + request.getQuery());
callback.onTypefaceRequestFailed(
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 9981668b7cca..5c64566f7987 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -196,7 +196,8 @@ public final class KeyChain {
*
* @deprecated Use {@link #ACTION_KEYCHAIN_CHANGED}, {@link #ACTION_TRUST_STORE_CHANGED} or
* {@link #ACTION_KEY_ACCESS_CHANGED}. Apps that target a version higher than
- * {@link Build.VERSION_CODES#N_MR1} will not receive this broadcast.
+ * {@link Build.VERSION_CODES#N_MR1} will only receive this broadcast if they register for it
+ * at runtime.
*/
public static final String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED";
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index cbea501e3df3..652954bd213e 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -389,10 +389,10 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
return new ProjectionTestCanvas(mDrawCounter);
}
sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
- return sk_sp<SkSurface>();
+ return nullptr;
}
- sk_sp<SkImage> onNewImageSnapshot(SkBudgeted) override {
- return sk_sp<SkImage>();
+ sk_sp<SkImage> onNewImageSnapshot() override {
+ return nullptr;
}
void onCopyOnWrite(ContentChangeMode) override {}
int* mDrawCounter;
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index c28aa5ec8711..6808b57c2642 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -1971,10 +1971,11 @@ public final class TvContract {
* channel is not locked thus the user is not prompted to enter passcode If not specified,
* this value is set to 0 (not locked) by default.
*
+ * <p>This column can only be set by applications having proper system permission to
+ * modify parental control settings. For other applications, this is a read-only column.
+
* <p>Type: INTEGER (boolean)
- * @hide
*/
- @SystemApi
public static final String COLUMN_LOCKED = "locked";
/**
diff --git a/packages/SystemUI/res/drawable/ic_add_circle_qs.xml b/packages/SystemUI/res/drawable/ic_add_circle_qs.xml
index 6415ecb44c2e..8e933445be0b 100644
--- a/packages/SystemUI/res/drawable/ic_add_circle_qs.xml
+++ b/packages/SystemUI/res/drawable/ic_add_circle_qs.xml
@@ -18,7 +18,7 @@ Copyright (C) 2014 The Android Open Source Project
android:height="48.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/colorControlNormal">
+ android:tint="?android:attr/colorForeground">
<group
android:scaleX="1.2"
android:scaleY="1.2"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 79ea6fe513c3..d6d01d8abac4 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1036,12 +1036,48 @@
<!-- Monitoring dialog subtitle for the section describing network logging [CHAR LIMIT=35]-->
<string name="monitoring_subtitle_network_logging">Network Logging</string>
+ <!-- Monitoring dialog subtitle for the section describing certificate authorities [CHAR LIMIT=35]-->
+ <string name="monitoring_subtitle_ca_certificate">CA certificates</string>
+
<!-- Monitoring dialog disable vpn button [CHAR LIMIT=30] -->
<string name="disable_vpn">Disable VPN</string>
<!-- Monitoring dialog disconnect vpn button [CHAR LIMIT=30] -->
<string name="disconnect_vpn">Disconnect VPN</string>
+ <!-- Monitoring dialog label for button opening a page with more information on the admin's abilities [CHAR LIMIT=30] -->
+ <string name="monitoring_button_view_policies">View Policies</string>
+
+ <!-- Monitoring dialog: Description of the device owner by name. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_named_management">Your device is managed by <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g>.\n\nYour admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device's location information.\n\nFor more information, contact your admin." </string>
+
+ <!-- Monitoring dialog: Description of a device owner. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_management">Your device is managed by your organization.\n\nYour admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device's location information.\n\nFor more information, contact your admin." </string>
+
+ <!-- Monitoring dialog: Description of a CA Certificate. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_management_ca_certificate">Your organization installed a certificate authority on this device. Your secure network traffic may be monitored or modified.</string>
+
+ <!-- Monitoring dialog: Description of a CA Certificate in the work profile. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_managed_profile_ca_certificate">Your organization installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified.</string>
+
+ <!-- Monitoring dialog: Description of a CA Certificate. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_ca_certificate">A certificate authority is installed on this device. Your secure network traffic may be monitored or modified.</string>
+
+ <!-- Monitoring dialog: Description of Network Logging. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_management_network_logging">Your admin has turned on network logging, which monitores traffic on your device.</string>
+
+ <!-- Monitoring dialog: Description of an active VPN. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_named_vpn">You're connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
+
+ <!-- Monitoring dialog: Description of two active VPNs. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_two_named_vpns">You're connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g> and <xliff:g id="vpn_app" example="Bar VPN App">%2$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
+
+ <!-- Monitoring dialog: Description of an active VPN in the work profile. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_managed_profile_named_vpn">Your work profile is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
+
+ <!-- Monitoring dialog: Description of an active VPN in the personal profile (as opposed to the work profile). [CHAR LIMIT=NONE]-->
+ <string name="monitoring_description_personal_profile_named_vpn">Your personal profile is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites.</string>
+
<!-- Monitoring dialog: Header indicating that the device is managed by a Device Owner app [CHAR LIMIT=80] -->
<string name="monitoring_description_do_header_generic">Your device is managed by <xliff:g id="device_owner_app" example="Google Mobile Management">%1$s</xliff:g>.</string>
@@ -1069,7 +1105,6 @@
<!-- Monitoring dialog: Network logging text [CHAR LIMIT=400] -->
<string name="monitoring_description_network_logging">Your admin has turned on network logging, which monitors traffic on your device.\n\nFor more information, contact your admin.</string>
-
<!-- Monitoring dialog VPN text [CHAR LIMIT=400] -->
<string name="monitoring_description_vpn">You gave an app permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps, and websites.</string>
@@ -1347,11 +1382,15 @@
of notifications. Replaces the channel name and only appears when there is more than one channel. -->
<string name="notification_num_channels"> <xliff:g id="number">%d</xliff:g> notification categories</string>
+ <!-- Notification: Control panel: Label that shows when an app has not upgraded to use channels.
+ Hints that the user's only option is to block all of the app's notifications. -->
+ <string name="notification_default_channel_desc">This app doesn\'t have notification categories</string>
+
<!-- Notification: Control panel: Label that shows how many channels this application has
- defined, describing the current notification channel as "1 out of n categories from this app". -->
+ defined, describing the current notification channel as "1 out of n notification categories from this app". -->
<plurals name="notification_num_channels_desc">
- <item quantity="one">1 out of <xliff:g id="number">%d</xliff:g> category from this app</item>
- <item quantity="other">1 out of <xliff:g id="number">%d</xliff:g> categories from this app</item>
+ <item quantity="one">1 out of <xliff:g id="number">%d</xliff:g> notification category from this app</item>
+ <item quantity="other">1 out of <xliff:g id="number">%d</xliff:g> notification categories from this app</item>
</plurals>
<!-- Notification: Control panel: For bundles of notifications, this label that lists the
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
index 1fb6c8704587..ec5f9e7eb6f5 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
@@ -59,7 +59,6 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
private static PluginManager sInstance;
- private final HandlerThread mBackgroundThread;
private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
= new ArrayMap<>();
private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
@@ -71,6 +70,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
private ClassLoaderFilter mParentClassLoader;
private boolean mListening;
private boolean mHasOneShot;
+ private Looper mLooper;
public PluginManagerImpl(Context context) {
this(context, new PluginInstanceManagerFactory(),
@@ -82,8 +82,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
UncaughtExceptionHandler defaultHandler) {
mContext = context;
mFactory = factory;
- mBackgroundThread = new HandlerThread("Plugins");
- mBackgroundThread.start();
+ mLooper = Dependency.get(Dependency.BG_LOOPER);
isDebuggable = debuggable;
mPluginPrefs = new PluginPrefs(mContext);
@@ -91,7 +90,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
defaultHandler);
Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
if (isDebuggable) {
- new Handler(mBackgroundThread.getLooper()).post(() -> {
+ new Handler(mLooper).post(() -> {
// Plugin dependencies that don't have another good home can go here, but
// dependencies that have better places to init can happen elsewhere.
Dependency.get(PluginDependencyProvider.class)
@@ -120,7 +119,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
throw new RuntimeException("Must be called from UI thread");
}
PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
- false, mBackgroundThread.getLooper(), cls, this);
+ false, mLooper, cls, this);
mPluginPrefs.addAction(action);
PluginInfo<T> info = p.getPlugin();
if (info != null) {
@@ -154,7 +153,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
}
mPluginPrefs.addAction(action);
PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
- allowMultiple, mBackgroundThread.getLooper(), cls, this);
+ allowMultiple, mLooper, cls, this);
p.loadAll();
mPluginMap.put(listener, p);
startListening();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 043490cc5823..2d0fe6f63219 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -20,6 +20,7 @@ import android.graphics.drawable.RippleDrawable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
@@ -110,7 +111,6 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
public void init(OnClickListener click, OnClickListener secondaryClick,
OnLongClickListener longClick) {
- setClickable(true);
setOnClickListener(click);
setOnLongClickListener(longClick);
}
@@ -148,6 +148,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
protected void handleStateChanged(QSTile.State state) {
+ setClickable(state.state != Tile.STATE_UNAVAILABLE);
mIcon.setIcon(state);
setContentDescription(state.contentDescription);
mAccessibilityClass = state.expandedAccessibilityClassName;
@@ -157,6 +158,12 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
@Override
+ public void setClickable(boolean clickable) {
+ super.setClickable(clickable);
+ setBackground(clickable ? mRipple : null);
+ }
+
+ @Override
public int getDetailY() {
return getTop() + getHeight() / 2;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index ca703367ac23..4351b2ca6dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -137,7 +137,8 @@ public class CellularTile extends QSTileImpl<SignalState> {
state.expandedAccessibilityClassName = Switch.class.getName();
state.value = mDataController.isMobileDataSupported()
&& mDataController.isMobileDataEnabled();
- state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.state = cb.airplaneModeEnabled ? Tile.STATE_UNAVAILABLE
+ : state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index a9043e4c83bb..54921a7108c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -99,11 +99,14 @@ public class NotificationInfo extends LinearLayout implements GutsContent {
mINotificationManager = iNotificationManager;
mPkg = pkg;
mNotificationChannels = notificationChannels;
+ boolean isSingleDefaultChannel = false;
if (mNotificationChannels.isEmpty()) {
throw new IllegalArgumentException("bindNotification requires at least one channel");
} else if (mNotificationChannels.size() == 1) {
mSingleNotificationChannel = mNotificationChannels.get(0);
mStartingUserImportance = mSingleNotificationChannel.getImportance();
+ isSingleDefaultChannel = mSingleNotificationChannel.getId()
+ .equals(NotificationChannel.DEFAULT_CHANNEL_ID);
} else {
mSingleNotificationChannel = null;
}
@@ -135,24 +138,30 @@ public class NotificationInfo extends LinearLayout implements GutsContent {
String channelsDescText;
mNumChannelsView = (TextView) (findViewById(R.id.num_channels_desc));
- switch (mNotificationChannels.size()) {
- case 1:
- channelsDescText = String.format(mContext.getResources().getQuantityString(
- R.plurals.notification_num_channels_desc, numChannels), numChannels);
- break;
- case 2:
- channelsDescText = mContext.getString(R.string.notification_channels_list_desc_2,
- mNotificationChannels.get(0).getName(),
- mNotificationChannels.get(1).getName());
- break;
- default:
- final int numOthers = mNotificationChannels.size() - 2;
- channelsDescText = String.format(
- mContext.getResources().getQuantityString(
- R.plurals.notification_channels_list_desc_2_and_others, numOthers),
- mNotificationChannels.get(0).getName(),
- mNotificationChannels.get(1).getName(),
- numOthers);
+ if (isSingleDefaultChannel) {
+ channelsDescText = mContext.getString(R.string.notification_default_channel_desc);
+ } else {
+ switch (mNotificationChannels.size()) {
+ case 1:
+ channelsDescText = String.format(mContext.getResources().getQuantityString(
+ R.plurals.notification_num_channels_desc, numChannels), numChannels);
+ break;
+ case 2:
+ channelsDescText = mContext.getString(
+ R.string.notification_channels_list_desc_2,
+ mNotificationChannels.get(0).getName(),
+ mNotificationChannels.get(1).getName());
+ break;
+ default:
+ final int numOthers = mNotificationChannels.size() - 2;
+ channelsDescText = String.format(
+ mContext.getResources().getQuantityString(
+ R.plurals.notification_channels_list_desc_2_and_others,
+ numOthers),
+ mNotificationChannels.get(0).getName(),
+ mNotificationChannels.get(1).getName(),
+ numOthers);
+ }
}
mNumChannelsView.setText(channelsDescText);
@@ -160,9 +169,8 @@ public class NotificationInfo extends LinearLayout implements GutsContent {
// Multiple channels don't use a channel name for the title.
channelNameText = mContext.getString(R.string.notification_num_channels,
mNotificationChannels.size());
- } else if (mSingleNotificationChannel.getId()
- .equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- // If this is the placeholder channel, don't use our channel-specific text.
+ } else if (isSingleDefaultChannel) {
+ // If this is the default channel, don't use our channel-specific text.
channelNameText = mContext.getString(R.string.notification_header_default_channel);
} else {
channelNameText = mSingleNotificationChannel.getName();
@@ -282,15 +290,9 @@ public class NotificationInfo extends LinearLayout implements GutsContent {
}
private void updateSecondaryText() {
- final boolean defaultChannel = mSingleNotificationChannel != null &&
- mSingleNotificationChannel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID);
final boolean disabled = mSingleNotificationChannel != null &&
getSelectedImportance() == NotificationManager.IMPORTANCE_NONE;
- if (defaultChannel) {
- // Don't show any secondary text if this is from the default channel.
- mChannelDisabledView.setVisibility(View.GONE);
- mNumChannelsView.setVisibility(View.GONE);
- } else if (disabled) {
+ if (disabled) {
mChannelDisabledView.setVisibility(View.VISIBLE);
mNumChannelsView.setVisibility(View.GONE);
} else {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
index a3d5d5fc3871..b8e9fcd29096 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
@@ -29,13 +29,18 @@ import android.net.Uri;
import android.support.test.annotation.UiThreadTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.annotations.ProvidesInterface;
import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,7 +50,8 @@ import org.mockito.Mockito;
import java.lang.Thread.UncaughtExceptionHandler;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
public class PluginManagerTest extends SysuiTestCase {
private PluginInstanceManagerFactory mMockFactory;
@@ -59,6 +65,8 @@ public class PluginManagerTest extends SysuiTestCase {
@Before
public void setup() throws Exception {
+ mDependency.injectTestDependency(Dependency.BG_LOOPER,
+ TestableLooper.get(this).getLooper());
mRealExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
mMockExceptionHandler = mock(UncaughtExceptionHandler.class);
mMockFactory = mock(PluginInstanceManagerFactory.class);
@@ -72,7 +80,7 @@ public class PluginManagerTest extends SysuiTestCase {
mMockListener = mock(PluginListener.class);
}
- @UiThreadTest
+ @RunWithLooper(setAsMainLooper = true)
@Test
public void testOneShot() {
Plugin mockPlugin = mock(Plugin.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 8aca546b2730..5632b719df23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -260,12 +260,14 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- public void testBindNotification_NumChannelsTextHiddenWhenDefaultChannel() throws Exception {
+ public void testBindNotification_NumChannelsTextUniqueWhenDefaultChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), null, null, null);
final TextView numChannelsView =
(TextView) mNotificationInfo.findViewById(R.id.num_channels_desc);
- assertTrue(numChannelsView.getVisibility() != View.VISIBLE);
+ assertEquals(View.VISIBLE, numChannelsView.getVisibility());
+ assertEquals(mContext.getString(R.string.notification_default_channel_desc),
+ numChannelsView.getText());
}
@Test
@@ -390,13 +392,14 @@ public class NotificationInfoTest extends SysuiTestCase {
@Test
@UiThreadTest
- public void testBindNotification_ChannelDisabledTextHiddenWhenDefaultChannel()
+ public void testBindNotification_ChannelDisabledTextShowsForDefaultChannel()
throws Exception {
+ mDefaultNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), null, null, null);
final TextView channelDisabledView =
(TextView) mNotificationInfo.findViewById(R.id.channel_disabled);
- assertTrue(channelDisabledView.getVisibility() != View.VISIBLE);
+ assertEquals(View.VISIBLE, channelDisabledView.getVisibility());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 8808988406a9..f516d74f4062 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -20,7 +20,9 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
+import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -31,10 +33,13 @@ import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
public class BluetoothControllerImplTest extends SysuiTestCase {
private LocalBluetoothManager mMockBluetoothManager;
@@ -47,7 +52,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
@Before
public void setup() throws Exception {
- mTestableLooper = new TestableLooper();
+ mTestableLooper = TestableLooper.get(this);
mMockBluetoothManager = mDependency.injectMockDependency(LocalBluetoothManager.class);
mDevices = new ArrayList<>();
mMockDeviceManager = mock(CachedBluetoothDeviceManager.class);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 01fc8ecd78bd..8e3e3eaa1a60 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5393,7 +5393,7 @@ public class AccountManagerService
@NonNull
private Account[] filterAccounts(UserAccounts accounts, Account[] unfiltered, int callingUid,
- String callingPackage, boolean includeManagedNotVisible) {
+ @Nullable String callingPackage, boolean includeManagedNotVisible) {
String visibilityFilterPackage = callingPackage;
if (visibilityFilterPackage == null) {
visibilityFilterPackage = getPackageNameForUid(callingUid);
@@ -5429,8 +5429,7 @@ public class AccountManagerService
}
UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
if (user != null && user.isRestricted()) {
- String[] packages =
- mPackageManager.getPackagesForUid(callingUid);
+ String[] packages = mPackageManager.getPackagesForUid(callingUid);
if (packages == null) {
packages = new String[] {};
}
@@ -5501,9 +5500,6 @@ public class AccountManagerService
@NonNull
protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
int callingUid, @Nullable String callingPackage, boolean includeManagedNotVisible) {
- if (callingPackage == null) {
- callingPackage = getPackageNameForUid(callingUid);
- }
if (accountType != null) {
final Account[] accounts = userAccounts.accountCache.get(accountType);
if (accounts == null) {
diff --git a/services/core/java/com/android/server/fingerprint/EnumerateClient.java b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
index 34f245f19d12..1b8b89c8bc37 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 abstract class EnumerateClient extends ClientMonitor {
public int stop(boolean initiatedByClient) {
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
- Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
+ Slog.w(TAG, "stopEnumeration: no fingerprint HAL!");
return ERROR_ESRCH;
}
try {
@@ -102,12 +102,12 @@ public abstract class EnumerateClient extends ClientMonitor {
@Override
public boolean onEnrollResult(int fingerId, int groupId, int rem) {
if (DEBUG) Slog.w(TAG, "onEnrollResult() called for enumerate!");
- return true; // Invalid for Remove
+ return true; // Invalid for Enumerate.
}
@Override
public boolean onRemoved(int fingerId, int groupId, int remaining) {
if (DEBUG) Slog.w(TAG, "onRemoved() called for enumerate!");
- return true; // Invalid for Authenticate
+ return true; // Invalid for Enumerate.
}
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 7d97ce41e8f3..b6e7932bdec4 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -85,6 +85,7 @@ import java.util.HashMap;
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.
@@ -134,6 +135,20 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
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<>();
@@ -257,10 +272,12 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
// 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);
}
@@ -270,12 +287,70 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
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 + "rem=" + remaining);
- // TODO: coordinate names with framework
+ 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();
+ }
+ }
}
protected void handleError(long deviceId, int error, int vendorCode) {
@@ -304,10 +379,18 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
}
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,
@@ -434,7 +517,15 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
ClientMonitor currentClient = mCurrentClient;
if (currentClient != null) {
if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
- currentClient.stop(initiatedByClient);
+ 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);
+ }
mPendingClient = newClient;
mHandler.removeCallbacks(mResetClientState);
mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
@@ -451,47 +542,86 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
}
void startRemove(IBinder token, int fingerId, int groupId, int userId,
- IFingerprintServiceReceiver receiver, boolean restricted) {
+ IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
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();
- }
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
+ 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);
+ }
}
void startEnumerate(IBinder token, int userId,
- IFingerprintServiceReceiver receiver, boolean restricted) {
+ IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startEnumerate: no fingerprint HAL!");
return;
}
- EnumerateClient client = new EnumerateClient(getContext(), mHalDeviceId, token,
- receiver, userId, userId, restricted, token.toString()) {
- @Override
- public void notifyUserActivity() {
- FingerprintService.this.userActivity();
- }
+ 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() {
- @Override
- public IBiometricsFingerprint getFingerprintDaemon() {
- return FingerprintService.this.getFingerprintDaemon();
- }
- };
- startClient(client, true);
+ }
+
+ @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);
+ }
}
public List<Fingerprint> getEnrolledFingerprints(int userId) {
@@ -978,12 +1108,14 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
mHandler.post(new Runnable() {
@Override
public void run() {
- startRemove(token, fingerId, groupId, userId, receiver, restricted);
+ startRemove(token, fingerId, groupId, userId, receiver,
+ restricted, false /* internal */);
}
});
}
+ @Override // Binder call
public void enumerate(final IBinder token, final int userId,
final IFingerprintServiceReceiver receiver) {
checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
@@ -991,7 +1123,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death
mHandler.post(new Runnable() {
@Override
public void run() {
- startEnumerate(token, userId, receiver, restricted);
+ startEnumerate(token, userId, receiver, restricted, false /* internal */);
}
});
diff --git a/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java b/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
new file mode 100644
index 000000000000..f4d2596c85bf
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/InternalEnumerateClient.java
@@ -0,0 +1,94 @@
+/*
+ * 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
new file mode 100644
index 000000000000..19f61feac1f4
--- /dev/null
+++ b/services/core/java/com/android/server/fingerprint/InternalRemovalClient.java
@@ -0,0 +1,33 @@
+/*
+ * 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/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 11cc52df180d..0774779a4cf2 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -583,7 +583,7 @@ abstract public class ManagedServices {
ServiceInfo info = mPm.getServiceInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]);
- if (!mConfig.bindPermission.equals(info.permission)) {
+ if (info == null || !mConfig.bindPermission.equals(info.permission)) {
Slog.w(TAG, "Skipping " + getCaption() + " service " + component
+ ": it does not require the permission " + mConfig.bindPermission);
continue;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3727a5b79440..ede5a5e8e337 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -179,6 +179,7 @@ import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -1441,11 +1442,6 @@ public class NotificationManagerService extends SystemService {
return ;
}
- if (isCallerInstantApp(pkg)) {
- throw new SecurityException("Instant app " + pkg
- + " is not allowed to create toasts");
- }
-
final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
final boolean isPackageSuspended =
isPackageSuspendedForUser(pkg, Binder.getCallingUid());
@@ -3121,8 +3117,19 @@ public class NotificationManagerService extends SystemService {
+ " - notification=" + notification);
return;
}
- throw new IllegalArgumentException("No Channel found for channelId=" + channelId
- + ", notification=" + notification);
+ final String noChannelStr = "No Channel found for "
+ + "pkg=" + pkg
+ + ", channelId=" + channelId
+ + ", opPkg=" + opPkg
+ + ", callingUid=" + callingUid
+ + ", userId=" + userId
+ + ", incomingUserId=" + incomingUserId
+ + ", notificationUid=" + notificationUid
+ + ", notification=" + notification;
+ // STOPSHIP TODO: should throw instead of logging.
+ // throw new IllegalArgumentException(noChannelStr);
+ Log.e(TAG, noChannelStr);
+ return;
}
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, notificationUid, callingPid, notification,
@@ -3645,6 +3652,10 @@ public class NotificationManagerService extends SystemService {
mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
effect, record.getAudioAttributes());
return true;
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Error creating vibration waveform with pattern: " +
+ Arrays.toString(vibration));
+ return false;
} finally{
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cb499d8f019a..f5ea74d6606b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2879,24 +2879,17 @@ public class PackageManagerService extends IPackageManager.Stub {
}
mInstallerService = new PackageInstallerService(context, this);
-
final ComponentName ephemeralResolverComponent = getEphemeralResolverLPr();
if (ephemeralResolverComponent != null) {
if (DEBUG_EPHEMERAL) {
- Slog.i(TAG, "Ephemeral resolver: " + ephemeralResolverComponent);
+ Slog.d(TAG, "Set ephemeral resolver: " + ephemeralResolverComponent);
}
mInstantAppResolverConnection =
new EphemeralResolverConnection(mContext, ephemeralResolverComponent);
} else {
mInstantAppResolverConnection = null;
}
- mInstantAppInstallerComponent = getEphemeralInstallerLPr();
- if (mInstantAppInstallerComponent != null) {
- if (DEBUG_EPHEMERAL) {
- Slog.i(TAG, "Ephemeral installer: " + mInstantAppInstallerComponent);
- }
- setUpInstantAppInstallerActivityLP(mInstantAppInstallerComponent);
- }
+ updateInstantAppInstallerLocked();
// Read and update the usage of dex files.
// Do this at the end of PM init so that all the packages have their
@@ -2936,6 +2929,21 @@ public class PackageManagerService extends IPackageManager.Stub {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
+ private void updateInstantAppInstallerLocked() {
+ final ComponentName oldInstantAppInstallerComponent = mInstantAppInstallerComponent;
+ final ComponentName newInstantAppInstallerComponent = getEphemeralInstallerLPr();
+ if (newInstantAppInstallerComponent != null
+ && !newInstantAppInstallerComponent.equals(oldInstantAppInstallerComponent)) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.d(TAG, "Set ephemeral installer: " + newInstantAppInstallerComponent);
+ }
+ setUpInstantAppInstallerActivityLP(newInstantAppInstallerComponent);
+ } else if (DEBUG_EPHEMERAL && newInstantAppInstallerComponent == null) {
+ Slog.d(TAG, "Unset ephemeral installer; none available");
+ }
+ mInstantAppInstallerComponent = newInstantAppInstallerComponent;
+ }
+
private static File preparePackageParserCache(boolean isUpgrade) {
if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
return null;
@@ -3811,6 +3819,16 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
+ public void deletePreloadsFileCache() {
+ if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) {
+ throw new SecurityException("Only system or settings may call deletePreloadsFileCache");
+ }
+ File dir = Environment.getDataPreloadsFileCacheDirectory();
+ Slog.i(TAG, "Deleting preloaded file cache " + dir);
+ FileUtils.deleteContents(dir);
+ }
+
+ @Override
public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize,
final IPackageDataObserver observer) {
mContext.enforceCallingOrSelfPermission(
@@ -3863,19 +3881,27 @@ public class PackageManagerService extends IPackageManager.Stub {
public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws IOException {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final File file = storage.findPathForUuid(volumeUuid);
+ if (file.getUsableSpace() >= bytes) return;
if (ENABLE_FREE_CACHE_V2) {
final boolean aggressive = (storageFlags
& StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0;
+ final boolean internalVolume = Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL,
+ volumeUuid);
// 1. Pre-flight to determine if we have any chance to succeed
// 2. Consider preloaded data (after 1w honeymoon, unless aggressive)
+ if (internalVolume && (aggressive || SystemProperties
+ .getBoolean("persist.sys.preloads.file_cache_expired", false))) {
+ deletePreloadsFileCache();
+ if (file.getUsableSpace() >= bytes) return;
+ }
// 3. Consider parsed APK data (aggressive only)
- if (aggressive) {
+ if (internalVolume && aggressive) {
FileUtils.deleteContents(mCacheDir);
+ if (file.getUsableSpace() >= bytes) return;
}
- if (file.getUsableSpace() >= bytes) return;
// 4. Consider cached app data (above quotas)
try {
@@ -16928,6 +16954,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
updateSequenceNumberLP(pkgName, res.newUsers);
+ updateInstantAppInstallerLocked();
}
}
}
@@ -17503,6 +17530,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mInstantAppRegistry.onPackageUninstalledLPw(pkg, info.removedUsers);
}
updateSequenceNumberLP(packageName, info.removedUsers);
+ updateInstantAppInstallerLocked();
}
}
}
@@ -19848,6 +19876,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
}
scheduleWritePackageRestrictionsLocked(userId);
updateSequenceNumberLP(packageName, new int[] { userId });
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ updateInstantAppInstallerLocked();
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
components = mPendingBroadcasts.get(userId, packageName);
final boolean newPackage = components == null;
if (newPackage) {
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 17e5e9f0fe69..342ec4b79fed 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -29,9 +29,10 @@ class GlobalActions implements GlobalActionsListener {
private static final boolean DEBUG = false;
private final Context mContext;
- private final LegacyGlobalActions mLegacyGlobalActions;
private final StatusBarManagerInternal mStatusBarInternal;
private final Handler mHandler;
+ private final WindowManagerFuncs mWindowManagerFuncs;
+ private LegacyGlobalActions mLegacyGlobalActions;
private boolean mKeyguardShowing;
private boolean mDeviceProvisioned;
private boolean mStatusBarConnected;
@@ -40,10 +41,19 @@ class GlobalActions implements GlobalActionsListener {
public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
mContext = context;
mHandler = new Handler();
- mLegacyGlobalActions = new LegacyGlobalActions(context, windowManagerFuncs,
- this::onGlobalActionsDismissed);
+ mWindowManagerFuncs = windowManagerFuncs;
mStatusBarInternal = LocalServices.getService(StatusBarManagerInternal.class);
- mStatusBarInternal.setGlobalActionsListener(this);
+
+ // Some form factors do not have a status bar.
+ if (mStatusBarInternal != null) {
+ mStatusBarInternal.setGlobalActionsListener(this);
+ }
+ }
+
+ private void ensureLegacyCreated() {
+ if (mLegacyGlobalActions != null) return;
+ mLegacyGlobalActions = new LegacyGlobalActions(mContext, mWindowManagerFuncs,
+ this::onGlobalActionsDismissed);
}
public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
@@ -56,6 +66,7 @@ class GlobalActions implements GlobalActionsListener {
mHandler.postDelayed(mShowTimeout, 5000);
} else {
// SysUI isn't alive, show legacy menu.
+ ensureLegacyCreated();
mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
}
}
@@ -79,6 +90,7 @@ class GlobalActions implements GlobalActionsListener {
mStatusBarConnected = connected;
if (mShowing && !mStatusBarConnected) {
// Status bar died but we need to be showing global actions still, show the legacy.
+ ensureLegacyCreated();
mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
}
}
@@ -88,6 +100,7 @@ class GlobalActions implements GlobalActionsListener {
public void run() {
if (DEBUG) Slog.d(TAG, "Global actions timeout");
// We haven't heard from sysui, show the legacy dialog.
+ ensureLegacyCreated();
mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned);
}
};
diff --git a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
index 0b80d819fd80..ab9ab6713eea 100644
--- a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
+++ b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.security.keymaster.KeyAttestationPackageInfo;
@@ -45,14 +46,19 @@ public class KeyAttestationApplicationIdProviderService
public KeyAttestationApplicationId getKeyAttestationApplicationId(int uid)
throws RemoteException {
- String[] packageNames = mPackageManager.getPackagesForUid(uid);
- if (packageNames == null) {
- throw new RemoteException("No packages for uid");
+ if (Binder.getCallingUid() != android.os.Process.KEYSTORE_UID) {
+ throw new SecurityException("This service can only be used by Keystore");
}
- int userId = UserHandle.getUserId(uid);
- KeyAttestationPackageInfo[] keyAttestationPackageInfos =
- new KeyAttestationPackageInfo[packageNames.length];
+ KeyAttestationPackageInfo[] keyAttestationPackageInfos = null;
+ final long token = Binder.clearCallingIdentity();
try {
+ String[] packageNames = mPackageManager.getPackagesForUid(uid);
+ if (packageNames == null) {
+ throw new RemoteException("No packages for uid");
+ }
+ int userId = UserHandle.getUserId(uid);
+ keyAttestationPackageInfos = new KeyAttestationPackageInfo[packageNames.length];
+
for (int i = 0; i < packageNames.length; ++i) {
PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageNames[i],
PackageManager.GET_SIGNATURES, userId);
@@ -61,6 +67,8 @@ public class KeyAttestationApplicationIdProviderService
}
} catch (NameNotFoundException nnfe) {
throw new RemoteException(nnfe.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
return new KeyAttestationApplicationId(keyAttestationPackageInfos);
}
diff --git a/services/core/java/com/android/server/security/KeyChainSystemService.java b/services/core/java/com/android/server/security/KeyChainSystemService.java
new file mode 100644
index 000000000000..2f681a3f568e
--- /dev/null
+++ b/services/core/java/com/android/server/security/KeyChainSystemService.java
@@ -0,0 +1,109 @@
+/*
+ * 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.security;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.UserHandle;
+import android.security.IKeyChainService;
+import android.util.Slog;
+
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+/**
+ * Service related to {@link android.security.KeyChain}.
+ * <p>
+ * Most of the implementation of KeyChain is provided by the com.android.keychain app. Until O,
+ * this was OK because a system app has roughly the same privileges as the system process.
+ * <p>
+ * With the introduction of background check, PACKAGE_* broadcasts (_ADDED, _REMOVED, _REPLACED)
+ * aren't received when the KeyChain app is in the background, which is bad as it uses those to
+ * drive internal cleanup.
+ * <p>
+ * TODO (b/35968281): take a more sophisticated look at what bits of KeyChain should be inside the
+ * system server and which make sense inside a system app.
+ */
+public class KeyChainSystemService extends SystemService {
+
+ private static final String TAG = "KeyChainSystemService";
+
+ /**
+ * Maximum time limit for the KeyChain app to deal with packages being removed.
+ */
+ private static final int KEYCHAIN_IDLE_WHITELIST_DURATION_MS = 30 * 1000;
+
+ public KeyChainSystemService(final Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ IntentFilter packageFilter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+ try {
+ getContext().registerReceiverAsUser(mPackageReceiver, UserHandle.ALL,
+ packageFilter, null /*broadcastPermission*/, null /*handler*/);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Unable to register for package removed broadcast", e);
+ }
+ }
+
+ private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(final Context context, final Intent broadcastIntent) {
+ if (broadcastIntent.getPackage() != null) {
+ return;
+ }
+
+ try {
+ final Intent intent = new Intent(IKeyChainService.class.getName());
+ ComponentName service =
+ intent.resolveSystemService(getContext().getPackageManager(), 0 /*flags*/);
+ if (service == null) {
+ return;
+ }
+ intent.setComponent(service);
+ intent.setAction(broadcastIntent.getAction());
+ startServiceInBackgroundAsUser(intent, UserHandle.of(getSendingUserId()));
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Unable to forward package removed broadcast to KeyChain", e);
+ }
+ }
+ };
+
+
+ private void startServiceInBackgroundAsUser(final Intent intent, final UserHandle user) {
+ if (intent.getComponent() == null) {
+ return;
+ }
+
+ final String packageName = intent.getComponent().getPackageName();
+ final DeviceIdleController.LocalService idleController =
+ LocalServices.getService(DeviceIdleController.LocalService.class);
+ idleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName,
+ KEYCHAIN_IDLE_WHITELIST_DURATION_MS, user.getIdentifier(), false, "keychain");
+
+ getContext().startServiceAsUser(intent, user);
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ce28cbaaf16a..f0732dd56f7a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -99,6 +99,7 @@ import com.android.server.power.ShutdownThread;
import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.retaildemo.RetailDemoModeService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
+import com.android.server.security.KeyChainSystemService;
import com.android.server.soundtrigger.SoundTriggerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
@@ -736,6 +737,10 @@ public final class SystemServer {
new KeyAttestationApplicationIdProviderService(context));
traceEnd();
+ traceBeginAndSlog("StartKeyChainSystemService");
+ mSystemServiceManager.startService(KeyChainSystemService.class);
+ traceEnd();
+
traceBeginAndSlog("StartSchedulingPolicyService");
ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
traceEnd();
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index ab83b9d84747..4c23d79cf7ff 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -52,6 +52,7 @@ import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import com.android.server.lights.Light;
@@ -259,6 +260,7 @@ public class NotificationManagerServiceTest {
@Test
@UiThreadTest
+ @Ignore("Flaky")
public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
generateNotificationRecord(null).getNotification(), new int[1], 0);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index ca3763127f4d..1aa952cd58b9 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -34,6 +34,7 @@ import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
import android.hardware.soundtrigger.SoundTriggerModule;
+import android.os.DeadObjectException;
import android.os.PowerManager;
import android.os.RemoteException;
import android.telephony.PhoneStateListener;
@@ -46,6 +47,8 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
import java.util.UUID;
/**
@@ -376,7 +379,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
ModelData modelData = getKeyphraseModelDataLocked(keyphraseId);
if (modelData == null || !modelData.isKeyphraseModel()) {
- Slog.e(TAG, "No model exists for given keyphrase Id.");
+ Slog.e(TAG, "No model exists for given keyphrase Id " + keyphraseId);
return STATUS_ERROR;
}
@@ -609,13 +612,16 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return;
}
+ model.setStopped();
try {
callback.onGenericSoundTriggerDetected((GenericRecognitionEvent) event);
+ } catch (DeadObjectException e) {
+ forceStopAndUnloadModel(model, e);
+ return;
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onGenericSoundTriggerDetected", e);
}
- model.setStopped();
RecognitionConfig config = model.getRecognitionConfig();
if (config == null) {
Slog.w(TAG, "Generic recognition event: Null RecognitionConfig for model handle: " +
@@ -699,6 +705,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
modelData.setStopped();
try {
modelData.getCallback().onRecognitionPaused();
+ } catch (DeadObjectException e) {
+ forceStopAndUnloadModel(modelData, e);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onRecognitionPaused", e);
}
@@ -710,8 +718,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
MetricsLogger.count(mContext, "sth_recognition_failure_event", 1);
try {
sendErrorCallbacksToAll(STATUS_ERROR);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in onError", e);
} finally {
internalClearModelStateLocked();
internalClearGlobalStateLocked();
@@ -748,15 +754,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
Slog.w(TAG, "Received onRecognition event without callback for keyphrase model.");
return;
}
+ modelData.setStopped();
try {
modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event);
+ } catch (DeadObjectException e) {
+ forceStopAndUnloadModel(modelData, e);
+ return;
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onKeyphraseDetected", e);
}
- modelData.setStopped();
-
RecognitionConfig config = modelData.getRecognitionConfig();
if (config != null) {
// Whether we should continue by starting this again.
@@ -791,10 +799,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private void onServiceDiedLocked() {
try {
- MetricsLogger.count(mContext, "sth_service_died", 1);
+ MetricsLogger.count(mContext, "sth_service_died", 1);
sendErrorCallbacksToAll(SoundTrigger.STATUS_DEAD_OBJECT);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in onError", e);
} finally {
internalClearModelStateLocked();
internalClearGlobalStateLocked();
@@ -879,11 +885,48 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
// Sends an error callback to all models with a valid registered callback.
- private void sendErrorCallbacksToAll(int errorCode) throws RemoteException {
+ private void sendErrorCallbacksToAll(int errorCode) {
for (ModelData modelData : mModelDataMap.values()) {
IRecognitionStatusCallback callback = modelData.getCallback();
if (callback != null) {
- callback.onError(STATUS_ERROR);
+ try {
+ callback.onError(errorCode);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException sendErrorCallbacksToAll for model handle " +
+ modelData.getHandle(), e);
+ }
+ }
+ }
+ }
+
+ private void forceStopAndUnloadModel(ModelData modelData, Exception exception) {
+ if (exception != null) {
+ Slog.e(TAG, "forceStopAndUnloadModel", exception);
+ }
+ if (modelData.isModelStarted()) {
+ Slog.d(TAG, "Stopping previously started dangling model " + modelData.getHandle());
+ if (mModule.stopRecognition(modelData.getHandle()) != STATUS_OK) {
+ modelData.setStopped();
+ modelData.setRequested(false);
+ } else {
+ Slog.e(TAG, "Failed to stop model " + modelData.getHandle());
+ }
+ }
+ if (modelData.isModelLoaded()) {
+ Slog.d(TAG, "Unloading previously loaded dangling model " + modelData.getHandle());
+ if (mModule.unloadSoundModel(modelData.getHandle()) == STATUS_OK) {
+ // Remove the model data from existence.
+ mModelDataMap.remove(modelData.getModelId());
+ Iterator it = mKeyphraseUuidMap.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry pair = (Map.Entry) it.next();
+ if (pair.getValue().equals(modelData.getModelId())) {
+ it.remove();
+ }
+ }
+ modelData.clearState();
+ } else {
+ Slog.e(TAG, "Failed to unload model " + modelData.getHandle());
}
}
}
@@ -976,6 +1019,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (notify) {
try {
callback.onError(status);
+ } catch (DeadObjectException e) {
+ forceStopAndUnloadModel(modelData, e);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onError", e);
}
@@ -988,6 +1033,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (notify) {
try {
callback.onRecognitionResumed();
+ } catch (DeadObjectException e) {
+ forceStopAndUnloadModel(modelData, e);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onRecognitionResumed", e);
}
@@ -1013,6 +1060,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (notify) {
try {
callback.onError(status);
+ } catch (DeadObjectException e) {
+ forceStopAndUnloadModel(modelData, e);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onError", e);
}
@@ -1024,6 +1073,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (notify) {
try {
callback.onRecognitionPaused();
+ } catch (DeadObjectException e) {
+ forceStopAndUnloadModel(modelData, e);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onRecognitionPaused", e);
}
diff --git a/tests/testables/src/android/testing/AndroidTestingRunner.java b/tests/testables/src/android/testing/AndroidTestingRunner.java
index 816ed033a3e2..a425f70e836c 100644
--- a/tests/testables/src/android/testing/AndroidTestingRunner.java
+++ b/tests/testables/src/android/testing/AndroidTestingRunner.java
@@ -18,7 +18,7 @@ import android.support.test.internal.runner.junit4.statement.RunAfters;
import android.support.test.internal.runner.junit4.statement.RunBefores;
import android.support.test.internal.runner.junit4.statement.UiThreadStatement;
-import android.testing.TestableLooper.LooperStatement;
+import android.testing.TestableLooper.LooperFrameworkMethod;
import android.testing.TestableLooper.RunWithLooper;
import org.junit.After;
@@ -30,6 +30,7 @@ import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -49,28 +50,21 @@ public class AndroidTestingRunner extends BlockJUnit4ClassRunner {
@Override
protected Statement methodInvoker(FrameworkMethod method, Object test) {
- return shouldRunOnUiThread(method) ? new UiThreadStatement(
- methodInvokerInt(method, test), true) : methodInvokerInt(method, test);
- }
-
- protected Statement methodInvokerInt(FrameworkMethod method, Object test) {
- RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
- if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class);
- if (annotation != null) {
- return new LooperStatement(super.methodInvoker(method, test),
- annotation.setAsMainLooper(), test);
- }
- return super.methodInvoker(method, test);
+ method = looperWrap(method, test, method);
+ final Statement statement = super.methodInvoker(method, test);
+ return shouldRunOnUiThread(method) ? new UiThreadStatement(statement, true) : statement;
}
protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) {
- List befores = this.getTestClass().getAnnotatedMethods(Before.class);
+ List befores = looperWrap(method, target,
+ this.getTestClass().getAnnotatedMethods(Before.class));
return befores.isEmpty() ? statement : new RunBefores(method, statement,
befores, target);
}
protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) {
- List afters = this.getTestClass().getAnnotatedMethods(After.class);
+ List afters = looperWrap(method, target,
+ this.getTestClass().getAnnotatedMethods(After.class));
return afters.isEmpty() ? statement : new RunAfters(method, statement, afters,
target);
}
@@ -88,6 +82,30 @@ public class AndroidTestingRunner extends BlockJUnit4ClassRunner {
return annotation == null ? 0L : annotation.timeout();
}
+ protected List<FrameworkMethod> looperWrap(FrameworkMethod method, Object test,
+ List<FrameworkMethod> methods) {
+ RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
+ if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class);
+ if (annotation != null) {
+ methods = new ArrayList<>(methods);
+ for (int i = 0; i < methods.size(); i++) {
+ methods.set(i, LooperFrameworkMethod.get(methods.get(i),
+ annotation.setAsMainLooper(), test));
+ }
+ }
+ return methods;
+ }
+
+ protected FrameworkMethod looperWrap(FrameworkMethod method, Object test,
+ FrameworkMethod base) {
+ RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
+ if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class);
+ if (annotation != null) {
+ return LooperFrameworkMethod.get(base, annotation.setAsMainLooper(), test);
+ }
+ return base;
+ }
+
public boolean shouldRunOnUiThread(FrameworkMethod method) {
if (mKlass.getAnnotation(UiThreadTest.class) != null) {
return true;
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 8a33cf918646..62490bc214a4 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -15,20 +15,21 @@
package android.testing;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
+import android.os.TestLooperManager;
+import android.support.test.InstrumentationRegistry;
import android.util.ArrayMap;
-import org.junit.runners.model.Statement;
+import org.junit.runners.model.FrameworkMethod;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.util.Map;
/**
@@ -38,65 +39,35 @@ import java.util.Map;
*/
public class TestableLooper {
- private final Method mNext;
- private final Method mRecycleUnchecked;
-
private Looper mLooper;
private MessageQueue mQueue;
private boolean mMain;
private Object mOriginalMain;
private MessageHandler mMessageHandler;
- private int mParsedCount;
private Handler mHandler;
private Message mEmptyMessage;
+ private TestLooperManager mQueueWrapper;
- public TestableLooper() throws Exception {
- this(true);
- }
-
- public TestableLooper(boolean setMyLooper) throws Exception {
- setupQueue(setMyLooper);
- mNext = mQueue.getClass().getDeclaredMethod("next");
- mNext.setAccessible(true);
- mRecycleUnchecked = Message.class.getDeclaredMethod("recycleUnchecked");
- mRecycleUnchecked.setAccessible(true);
+ public TestableLooper(Looper l) throws Exception {
+ this(InstrumentationRegistry.getInstrumentation().acquireLooperManager(l), l);
}
- public Looper getLooper() {
- return mLooper;
+ private TestableLooper(TestLooperManager wrapper, Looper l) throws Exception {
+ mQueueWrapper = wrapper;
+ setupQueue(l);
}
- private void clearLooper() throws NoSuchFieldException, IllegalAccessException {
- Field field = Looper.class.getDeclaredField("sThreadLocal");
- field.setAccessible(true);
- ThreadLocal<Looper> sThreadLocal = (ThreadLocal<Looper>) field.get(null);
- sThreadLocal.set(null);
+ private TestableLooper(Looper looper, boolean b) throws Exception {
+ setupQueue(looper);
}
- private boolean setForCurrentThread() throws NoSuchFieldException, IllegalAccessException {
- if (Looper.myLooper() != mLooper) {
- Field field = Looper.class.getDeclaredField("sThreadLocal");
- field.setAccessible(true);
- ThreadLocal<Looper> sThreadLocal = (ThreadLocal<Looper>) field.get(null);
- sThreadLocal.set(mLooper);
- return true;
- }
- return false;
+ public Looper getLooper() {
+ return mLooper;
}
- private void setupQueue(boolean setMyLooper) throws Exception {
- if (setMyLooper) {
- clearLooper();
- Looper.prepare();
- mLooper = Looper.myLooper();
- } else {
- Constructor<Looper> constructor = Looper.class.getDeclaredConstructor(
- boolean.class);
- constructor.setAccessible(true);
- mLooper = constructor.newInstance(true);
- }
-
+ private void setupQueue(Looper l) throws Exception {
+ mLooper = l;
mQueue = mLooper.getQueue();
mHandler = new Handler(mLooper);
}
@@ -121,9 +92,7 @@ public class TestableLooper {
* tests.
*/
public void destroy() throws NoSuchFieldException, IllegalAccessException {
- if (Looper.myLooper() == mLooper) {
- clearLooper();
- }
+ mQueueWrapper.release();
if (mMain && mOriginalMain != null) {
Field field = mLooper.getClass().getDeclaredField("sMainLooper");
field.setAccessible(true);
@@ -164,26 +133,26 @@ public class TestableLooper {
private boolean parseMessageInt() {
try {
- Message result = (Message) mNext.invoke(mQueue);
+ Message result = mQueueWrapper.next();
if (result != null) {
// This is a break message.
if (result == mEmptyMessage) {
- mRecycleUnchecked.invoke(result);
+ mQueueWrapper.recycle(result);
return false;
}
if (mMessageHandler != null) {
if (mMessageHandler.onMessageHandled(result)) {
result.getTarget().dispatchMessage(result);
- mRecycleUnchecked.invoke(result);
+ mQueueWrapper.recycle(result);
} else {
- mRecycleUnchecked.invoke(result);
+ mQueueWrapper.recycle(result);
// Message handler indicated it doesn't want us to continue.
return false;
}
} else {
result.getTarget().dispatchMessage(result);
- mRecycleUnchecked.invoke(result);
+ mQueueWrapper.recycle(result);
}
} else {
// No messages, don't continue parsing
@@ -199,10 +168,14 @@ public class TestableLooper {
* Runs an executable with myLooper set and processes all messages added.
*/
public void runWithLooper(RunnableWithException runnable) throws Exception {
- boolean set = setForCurrentThread();
- runnable.run();
+ new Handler(getLooper()).post(() -> {
+ try {
+ runnable.run();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
processAllMessages();
- if (set) clearLooper();
}
public interface RunnableWithException {
@@ -221,33 +194,131 @@ public class TestableLooper {
return sLoopers.get(test);
}
- public static class LooperStatement extends Statement {
- private final boolean mSetAsMain;
- private final Statement mBase;
- private final TestableLooper mLooper;
+ public static class LooperFrameworkMethod extends FrameworkMethod {
+ private HandlerThread mHandlerThread;
+
+ private final TestableLooper mTestableLooper;
+ private final Looper mLooper;
+ private final Handler mHandler;
- public LooperStatement(Statement base, boolean setAsMain, Object test) {
- mBase = base;
+ public LooperFrameworkMethod(FrameworkMethod base, boolean setAsMain, Object test) {
+ super(base.getMethod());
try {
- mLooper = new TestableLooper(false);
- sLoopers.put(test, mLooper);
- mSetAsMain = setAsMain;
+ mLooper = setAsMain ? Looper.getMainLooper() : createLooper();
+ mTestableLooper = new TestableLooper(mLooper, false);
} catch (Exception e) {
throw new RuntimeException(e);
}
+ sLoopers.put(test, mTestableLooper);
+ mHandler = new Handler(mLooper);
}
- @Override
- public void evaluate() throws Throwable {
- mLooper.setForCurrentThread();
- if (mSetAsMain) {
- mLooper.setAsMainLooper();
+ public LooperFrameworkMethod(TestableLooper other, FrameworkMethod base) {
+ super(base.getMethod());
+ mLooper = other.mLooper;
+ mTestableLooper = other;
+ mHandler = new Handler(mLooper);
+ }
+
+ public static FrameworkMethod get(FrameworkMethod base, boolean setAsMain, Object test) {
+ if (sLoopers.containsKey(test)) {
+ return new LooperFrameworkMethod(sLoopers.get(test), base);
}
+ return new LooperFrameworkMethod(base, setAsMain, test);
+ }
+ @Override
+ public Object invokeExplosively(Object target, Object... params) throws Throwable {
+ if (Looper.myLooper() == mLooper) {
+ // Already on the right thread from another statement, just execute then.
+ return super.invokeExplosively(target, params);
+ }
+ boolean set = mTestableLooper.mQueueWrapper == null;
+ if (set) {
+ mTestableLooper.mQueueWrapper = InstrumentationRegistry.getInstrumentation()
+ .acquireLooperManager(mLooper);
+ }
try {
- mBase.evaluate();
+ Object[] ret = new Object[1];
+ // Run the execution on the looper thread.
+ Runnable execute = () -> {
+ try {
+ ret[0] = super.invokeExplosively(target, params);
+ } catch (Throwable throwable) {
+ throw new LooperException(throwable);
+ }
+ };
+ mHandler.post(execute);
+ // Try to wait for the message to be queued.
+ for (int i = 0; i < 10; i++) {
+ if (!mTestableLooper.mQueueWrapper.hasMessages(mHandler, null, execute)) {
+ Thread.sleep(1);
+ }
+ }
+ if (!mTestableLooper.mQueueWrapper.hasMessages(mHandler, null, execute)) {
+ throw new RuntimeException("Message didn't queue...");
+ }
+ Message m = mTestableLooper.mQueueWrapper.next();
+ // Parse all other messages until we get to ours.
+ while (m.getTarget() != mHandler) {
+ try {
+ mTestableLooper.mQueueWrapper.execute(m);
+ } catch (LooperException e) {
+ throw e.getSource();
+ } finally {
+ mTestableLooper.mQueueWrapper.recycle(m);
+ }
+ m = mTestableLooper.mQueueWrapper.next();
+ }
+ // Dispatch our message.
+ try {
+ mTestableLooper.mQueueWrapper.execute(m);
+ } catch (LooperException e) {
+ throw e.getSource();
+ } catch (RuntimeException re) {
+ // If the TestLooperManager has to post, it will wrap what it throws in a
+ // RuntimeException, make sure we grab the actual source.
+ if (re.getCause() instanceof LooperException) {
+ throw ((LooperException) re.getCause()).getSource();
+ } else {
+ throw re.getCause();
+ }
+ } finally {
+ mTestableLooper.mQueueWrapper.recycle(m);
+ }
+ return ret[0];
} finally {
- mLooper.destroy();
+ if (set) {
+ mTestableLooper.mQueueWrapper.release();
+ mTestableLooper.mQueueWrapper = null;
+ }
+ }
+ }
+
+ private Looper createLooper() {
+ // TODO: Find way to share these.
+ mHandlerThread = new HandlerThread(TestableLooper.class.getSimpleName());
+ mHandlerThread.start();
+ return mHandlerThread.getLooper();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ if (mHandlerThread != null) {
+ mHandlerThread.quit();
+ }
+ }
+
+ private static class LooperException extends RuntimeException {
+ private final Throwable mSource;
+
+ public LooperException(Throwable t) {
+ mSource = t;
+ }
+
+ public Throwable getSource() {
+ return mSource;
}
}
}
diff --git a/tests/testables/tests/src/android/testing/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
index 18e5fffef992..12f1d0a5f414 100644
--- a/tests/testables/tests/src/android/testing/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -24,17 +24,16 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.testing.TestableLooper.MessageHandler;
import android.testing.TestableLooper.RunWithLooper;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class TestableLooperTest {
@@ -46,11 +45,6 @@ public class TestableLooperTest {
mTestableLooper = TestableLooper.get(this);
}
- @After
- public void tearDown() throws Exception {
- mTestableLooper.destroy();
- }
-
@Test
public void testMessageExecuted() throws Exception {
Handler h = new Handler();
@@ -133,39 +127,23 @@ public class TestableLooperTest {
@Test
public void testMainLooper() throws Exception {
assertNotEquals(Looper.myLooper(), Looper.getMainLooper());
-
- Looper originalMain = Looper.getMainLooper();
- mTestableLooper.setAsMainLooper();
- assertEquals(Looper.myLooper(), Looper.getMainLooper());
- Runnable r = mock(Runnable.class);
-
- new Handler(Looper.getMainLooper()).post(r);
- mTestableLooper.processAllMessages();
-
- verify(r).run();
- mTestableLooper.destroy();
-
- assertEquals(originalMain, Looper.getMainLooper());
- }
-
- @Test
- public void testNotMyLooper() throws Exception {
- TestableLooper looper = new TestableLooper(false);
-
- assertEquals(Looper.myLooper(), mTestableLooper.getLooper());
- assertNotEquals(Looper.myLooper(), looper.getLooper());
-
Runnable r = mock(Runnable.class);
Runnable r2 = mock(Runnable.class);
- new Handler().post(r);
- new Handler(looper.getLooper()).post(r2);
-
- looper.processAllMessages();
- verify(r2).run();
- verify(r, never()).run();
-
- mTestableLooper.processAllMessages();
- verify(r).run();
+ TestableLooper testableLooper = new TestableLooper(Looper.getMainLooper());
+
+ try {
+ testableLooper.setMessageHandler(m -> {
+ if (m.getCallback() == r) return true;
+ return false;
+ });
+ new Handler(Looper.getMainLooper()).post(r);
+ testableLooper.processAllMessages();
+
+ verify(r).run();
+ verify(r2, never()).run();
+ } finally {
+ testableLooper.destroy();
+ }
}
@Test
diff --git a/wifi/java/android/net/wifi/IconInfo.aidl b/wifi/java/android/net/wifi/IconInfo.aidl
deleted file mode 100644
index a7bb2ef8bc3f..000000000000
--- a/wifi/java/android/net/wifi/IconInfo.aidl
+++ /dev/null
@@ -1,19 +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 android.net.wifi;
-
-parcelable IconInfo;
diff --git a/wifi/java/android/net/wifi/IconInfo.java b/wifi/java/android/net/wifi/IconInfo.java
deleted file mode 100644
index 0eae363861f8..000000000000
--- a/wifi/java/android/net/wifi/IconInfo.java
+++ /dev/null
@@ -1,111 +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 android.net.wifi;
-
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.os.Parcel;
-
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * A class representing icon information.
- */
-public final class IconInfo implements Parcelable {
- /**
- * Name of the icon file.
- */
- private final String mFilename;
-
- /**
- * Raw binary data of the icon.
- */
- private final byte[] mData;
-
- public IconInfo(String filename, byte[] data) {
- mFilename = filename;
- mData = data;
- }
-
- public IconInfo(IconInfo source) {
- if (source == null) {
- mFilename = null;
- mData = null;
- return;
- }
-
- mFilename = source.mFilename;
- if (source.mData != null) {
- mData = Arrays.copyOf(source.mData, source.mData.length);
- } else {
- mData = null;
- }
- }
-
- public String getFilename() {
- return mFilename;
- }
-
- public byte[] getData() {
- return mData;
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (this == thatObject) {
- return true;
- }
- if (!(thatObject instanceof IconInfo)) {
- return false;
- }
- IconInfo that = (IconInfo) thatObject;
- return TextUtils.equals(mFilename, that.mFilename)
- && Arrays.equals(mData, that.mData);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mFilename, mData);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mFilename);
- dest.writeByteArray(mData);
- }
-
- public static final Creator<IconInfo> CREATOR =
- new Creator<IconInfo>() {
- @Override
- public IconInfo createFromParcel(Parcel in) {
- String filename = in.readString();
- byte[] data = in.createByteArray();
- return new IconInfo(filename, data);
- }
-
- @Override
- public IconInfo[] newArray(int size) {
- return new IconInfo[size];
- }
- };
-}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 4f2881b87616..824c436ac9fd 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -120,7 +120,8 @@ public class WifiManager {
*
* Included extras:
* {@link #EXTRA_BSSID_LONG}
- * {@link #EXTRA_ICON_INFO}
+ * {@link #EXTRA_FILENAME}
+ * {@link #EXTRA_ICON}
*
* Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE
*
@@ -136,12 +137,18 @@ public class WifiManager {
*/
public static final String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG";
/**
- * Icon information.
+ * Icon data.
*
* Retrieve with {@link android.content.Intent#getParcelableExtra(String)} and cast into
- * {@link IconInfo}.
+ * {@link android.graphics.drawable.Icon}.
*/
- public static final String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO";
+ public static final String EXTRA_ICON = "android.net.wifi.extra.ICON";
+ /**
+ * Name of a file.
+ *
+ * Retrieve with {@link android.content.Intent#getStringExtra(String)}.
+ */
+ public static final String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME";
/**
* Broadcast intent action indicating a Passpoint OSU Providers List element has been received.
@@ -984,9 +991,9 @@ public class WifiManager {
/**
* Query for a Hotspot 2.0 release 2 OSU icon file. An {@link #ACTION_PASSPOINT_ICON} intent
- * will be broadcasted once the request is completed. The return value of
- * {@link IconInfo#getData} from the intent extra will indicate the result of the request.
- * A value of {@code null} will indicate a failure.
+ * will be broadcasted once the request is completed. The presence of the intent extra
+ * {@link #EXTRA_ICON} will indicate the result of the request.
+ * A missing intent extra {@link #EXTRA_ICON} will indicate a failure.
*
* @param bssid The BSSID of the AP
* @param fileName Name of the icon file (remote file) to query from the AP
diff --git a/wifi/java/android/net/wifi/hotspot2/ConfigParser.java b/wifi/java/android/net/wifi/hotspot2/ConfigParser.java
index 027b049a68c9..e8e873199e17 100644
--- a/wifi/java/android/net/wifi/hotspot2/ConfigParser.java
+++ b/wifi/java/android/net/wifi/hotspot2/ConfigParser.java
@@ -111,6 +111,7 @@ public final class ConfigParser {
*
* Content-Type: multipart/mixed; boundary={boundary}
* Content-Transfer-Encoding: base64
+ * [Skip uninterested headers]
*
* --{boundary}
* Content-Type: application/x-passpoint-profile
@@ -326,7 +327,8 @@ public final class ConfigParser {
header.encodingType = entry.getValue();
break;
default:
- throw new IOException("Unexpected header: " + entry.getKey());
+ Log.d(TAG, "Ignore header: " + entry.getKey());
+ break;
}
}
return header;
@@ -344,21 +346,24 @@ public final class ConfigParser {
* @throws IOException
*/
private static Pair<String, String> parseContentType(String contentType) throws IOException {
- String[] attributes = contentType.toString().split(";");
+ String[] attributes = contentType.split(";");
String type = null;
String boundary = null;
- if (attributes.length < 1 || attributes.length > 2) {
+ if (attributes.length < 1) {
throw new IOException("Invalid Content-Type: " + contentType);
}
+ // The type is always the first attribute.
type = attributes[0].trim();
- if (attributes.length == 2) {
- boundary = attributes[1].trim();
- if (!boundary.startsWith(BOUNDARY)) {
- throw new IOException("Invalid Content-Type: " + contentType);
+ // Look for boundary string from the rest of the attributes.
+ for (int i = 1; i < attributes.length; i++) {
+ String attribute = attributes[i].trim();
+ if (!attribute.startsWith(BOUNDARY)) {
+ Log.d(TAG, "Ignore Content-Type attribute: " + attributes[i]);
+ continue;
}
- boundary = boundary.substring(BOUNDARY.length());
+ boundary = attribute.substring(BOUNDARY.length());
// Remove the leading and trailing quote if present.
if (boundary.length() > 1 && boundary.startsWith("\"") && boundary.endsWith("\"")) {
boundary = boundary.substring(1, boundary.length()-1);
diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
index 2ffe42859fc8..5dc5d13569c6 100644
--- a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
+++ b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java
@@ -144,6 +144,8 @@ public final class PpsMoParser {
private static final String NODE_TIME_LIMIT = "TimeLimit";
private static final String NODE_USAGE_TIME_PERIOD = "UsageTimePeriod";
private static final String NODE_CREDENTIAL_PRIORITY = "CredentialPriority";
+ private static final String NODE_EXTENSION = "Extension";
+
/**
* Fields under HomeSP subtree.
*/
@@ -629,6 +631,10 @@ public final class PpsMoParser {
case NODE_CREDENTIAL_PRIORITY:
config.setCredentialPriority(parseInteger(getPpsNodeValue(child)));
break;
+ case NODE_EXTENSION:
+ // All vendor specific information will be under this node.
+ Log.d(TAG, "Ignore Extension node for vendor specific information");
+ break;
default:
throw new ParsingException("Unknown node: " + child.getName());
}
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
index 995963d2b4cc..56919c25de46 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
@@ -1,85 +1,86 @@
-Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
-dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
-cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
-IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
-SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
-YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
-UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
-V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
-M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
-MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
-VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
-RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
-QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
-QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
-QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
-bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
-MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
-Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
-ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
-YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
-VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
-YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
-RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
-bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
-MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
-MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
-UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
-eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
-QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
-OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
-dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
-S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
-K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
-dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
-TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
-SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
-WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
-VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
-MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
-Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
-V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
-a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
-eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
-QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
-VTJSbWx1WjJWeWNISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
-KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
-bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
-OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
-Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
-VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
-UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
-SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
-WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
-V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
-bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
-QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
-LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
-UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
-VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
-bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
-azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
-VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
-TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
-TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
-dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
-RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
-U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
-ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
-M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
-CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
-TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
-U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
-YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
-MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
-akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
-MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
-amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
-ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
-OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
-MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
-MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
-aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
-S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
-a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
-RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
+TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5
+PXtib3VuZGFyeX07IGNoYXJzZXQ9VVRGLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFz
+ZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXBhc3Nwb2ludC1w
+cm9maWxlOyBjaGFyc2V0PVVURi04CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoK
+UEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29nSUR4V1pYSkVW
+RVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVoYldVK1VHVnlV
+SEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0FnUEZKVVVISnZj
+R1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhiV1UrZFhKdU9u
+ZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZM0pwY0hScGIy
+NDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThMMUpVVUhKdmNH
+VnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSthVEF3TVR3dlRt
+OWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVJYjIx
+bFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1
+dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNB
+OFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklDQWdQQzlPYjJS
+bFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrWlJSRTQ4
+TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1MWF6d3ZWbUZz
+ZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0Fn
+SUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2ClpHVk9ZVzFs
+UGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpoYkhWbFBnb2dJ
+Q0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4VG05a1pUNEtJ
+Q0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVoYldVK0NpQWdJ
+Q0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhiRzA4CkwwNXZa
+R1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHljbVZrTG1OdmJU
+d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lD
+QWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThMMDV2WkdWT1lX
+MWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0ClpU
+NVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lXeDFaVDVxWVcx
+bGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05
+a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BDOU9iMlJsVG1G
+dFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3dlZtRnNkV1Ur
+CkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0Fn
+SUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0Fn
+SUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1GdFpUNUZRVkJV
+ZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1UrTWpFOEwxWmhi
+SFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxQ
+Z29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2WkR3dlRtOWta
+VTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0VmpJOEwxWmhi
+SFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0Np
+QWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BF
+NXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
+QWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQa05sY25ScFpt
+bGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lXeDFaVDU0TlRB
+NWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1
+dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1qVTJSbWx1WjJW
+eWNISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdVKwpNV1l4WmpG
+bU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZt
+TVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNBOEwwNXZaR1Ur
+Q2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0Fn
+UEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4VG05awpaVDRL
+SUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJ
+Q0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNBOEwwNXZa
+R1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2WkdWT1lXMWxQ
+a1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1UrTWpROApM
+MVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lD
+QWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQQzlOWjIxMFZI
+SmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXg1MDktY2Et
+Y2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFDUlVkSlRpQkRS
+VkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtRVWxNYkVaa2Qz
+cE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5UbFlLUWtGTlZF
+SXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpUazFxV1hkTlZF
+RTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5VVEJGZUUxSlNV
+Skpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMwTkJVVVZCQ25w
+dVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZYTldkVzFFWWxs
+SWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01TdHZSMWhhZGto
+M2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJVRkRaV1pXYW1v
+d2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJoU1FqZzFNVEpR
+UWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdKck1IVjVhM1Jr
+WW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtzM2FFUTRjRkIy
+WmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxhCmFYQllOREY0
+UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVXTTJreGRIRXdO
+R3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZHU1hkWU5IWnpP
+RUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFFYlVGR1NYZFlO
+SFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJkMFJuCldVUldV
+VkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldVakJVUWtGVmQw
+RjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZMDVCVVVWTVFs
+RkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZDamxIUlZBdmRX
+OW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNYZEpWV00zCmQy
+azNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1EwOTBhWE5rUW5F
+eWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JXMVdUQW94Y1VK
+S2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVFMXVWR3c0ZUVW
+WFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhwaFNFb3hkVlk0
+Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpGS1VDdHNlRllL
+YlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNSa1RrNTJRMWw2
+YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmtsRFFWUkZMUzB0
+TFMwSwotLXtib3VuZGFyeX0tLQo=
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
index 3ddd09f91ff4..a44b54222589 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
@@ -1,8 +1,9 @@
-Content-Type: multipart/mixed; boundary={boundary}
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary={boundary}; charset=UTF-8
Content-Transfer-Encoding: base64
--{boundary}
-Content-Type: application/x-passpoint-profile
+Content-Type: application/x-passpoint-profile; charset=UTF-8
Content-Transfer-Encoding: base64
PE1nbXRUcmVlIHhtbG5zPSJzeW5jbWw6ZG1kZGYxLjIiPgogIDxWZXJEVEQ+MS4yPC9WZXJEVEQ+
diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml
index 7f2d95de95e9..1fb83094a002 100644
--- a/wifi/tests/assets/pps/PerProviderSubscription.xml
+++ b/wifi/tests/assets/pps/PerProviderSubscription.xml
@@ -14,6 +14,13 @@
<Node>
<NodeName>i001</NodeName>
<Node>
+ <NodeName>Extension</NodeName>
+ <Node>
+ <NodeName>VendorSpecific</NodeName>
+ <Value>Test</Value>
+ </Node>
+ </Node>
+ <Node>
<NodeName>HomeSP</NodeName>
<Node>
<NodeName>FriendlyName</NodeName>
diff --git a/wifi/tests/src/android/net/wifi/IconInfoTest.java b/wifi/tests/src/android/net/wifi/IconInfoTest.java
deleted file mode 100644
index 2fdb484fce19..000000000000
--- a/wifi/tests/src/android/net/wifi/IconInfoTest.java
+++ /dev/null
@@ -1,122 +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 android.net.wifi;
-
-import static org.junit.Assert.assertEquals;
-
-import android.net.wifi.IconInfo;
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Test;
-
-/**
- * Unit tests for {@link android.net.wifi.IconInfo}.
- */
-@SmallTest
-public class IconInfoTest {
- private static final String TEST_FILENAME = "testIcon";
- private static final byte[] TEST_DATA = new byte[] {0x12, 0x23, 0x34, 0x45, 0x56, 0x67};
-
- /**
- * Verify parcel write and read consistency for the given {@link IconInfo}
- *
- * @param writeIcon the {@link IconInfo} to write and verify
- * @throws Exception
- */
- private static void verifyParcel(IconInfo writeIcon) throws Exception {
- Parcel parcel = Parcel.obtain();
- writeIcon.writeToParcel(parcel, 0);
-
- parcel.setDataPosition(0); // Rewind data position back to the beginning for read.
- IconInfo readIcon = IconInfo.CREATOR.createFromParcel(parcel);
- assertEquals(writeIcon, readIcon);
- }
-
- /**
- * Verify parcel serialization for a {@link IconInfo} with null data.
- *
- * @throws Exception
- */
- @Test
- public void verifyParcelWithNullData() throws Exception {
- verifyParcel(new IconInfo(TEST_FILENAME, (byte[]) null));
- }
-
- /**
- * Verify parcel serialization for a {@link IconInfo} with zero length data.
- *
- * @throws Exception
- */
- @Test
- public void verifyParcelWithZeroLengthData() throws Exception {
- verifyParcel(new IconInfo(TEST_FILENAME, new byte[0]));
- }
-
- /**
- * Verify parcel serialization for a {@link IconInfo} with non-zero length data.
- *
- * @throws Exception
- */
- @Test
- public void verifyParcelWithNonZeroLengthData() throws Exception {
- verifyParcel(new IconInfo(TEST_FILENAME, TEST_DATA));
- }
-
- /**
- * Verify parcel serialization for a {@link IconInfo} with a null filename.
- *
- * @throws Exception
- */
- @Test
- public void verifyParcelWithNullFilename() throws Exception {
- verifyParcel(new IconInfo(null, TEST_DATA));
- }
-
- /**
- * Verify the copy constructor with non-null filename and data.
- *
- * @throws Exception
- */
- @Test
- public void verifyCopyConstructor() throws Exception {
- IconInfo source = new IconInfo(TEST_FILENAME, TEST_DATA);
- assertEquals(source, new IconInfo(source));
- }
-
- /**
- * Verify the copy constructor with null data.
- *
- * @throws Exception
- */
- @Test
- public void verifyCopyConstructorWithNullData() throws Exception {
- IconInfo source = new IconInfo(TEST_FILENAME, (byte[]) null);
- assertEquals(source, new IconInfo(source));
- }
-
- /**
- * Verify the copy constructor with null file name.
- *
- * @throws Exception
- */
- @Test
- public void verifyCopyConstructorWithNullFilename() throws Exception {
- IconInfo source = new IconInfo(null, TEST_DATA);
- assertEquals(source, new IconInfo(source));
- }
-}