diff options
55 files changed, 1228 insertions, 495 deletions
diff --git a/api/current.txt b/api/current.txt index e341739ab6ef..e87c6132bafa 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"; @@ -31197,7 +31195,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 +31239,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..073729957399 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"; @@ -33925,7 +33922,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 +33966,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..84d97fbaa04f 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"; @@ -31320,7 +31327,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 +31371,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/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/PopupWindow.java b/core/java/android/widget/PopupWindow.java index d096baf3c943..26b3ae2c42fc 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1620,17 +1620,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; 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/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 Binary files differnew file mode 100644 index 000000000000..d7049517dc2a --- /dev/null +++ b/core/res/res/drawable-hdpi/stat_notify_mmcc_indication_icn.png 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 Binary files differnew file mode 100644 index 000000000000..5dfc89ec1856 --- /dev/null +++ b/core/res/res/drawable-xhdpi/stat_notify_mmcc_indication_icn.png 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 Binary files differnew file mode 100644 index 000000000000..a648b0bffcbf --- /dev/null +++ b/core/res/res/drawable-xxhdpi/stat_notify_mmcc_indication_icn.png 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/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8dc694cc6e47..d6d01d8abac4 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1382,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/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/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/KeyChainSystemService.java b/services/core/java/com/android/server/security/KeyChainSystemService.java new file mode 100644 index 000000000000..bfeaee6af7c4 --- /dev/null +++ b/services/core/java/com/android/server/security/KeyChainSystemService.java @@ -0,0 +1,86 @@ +/* + * 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.UserHandle; +import android.security.IKeyChainService; +import android.util.Slog; + +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"; + + 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()); + getContext().startServiceAsUser(intent, UserHandle.of(getSendingUserId())); + } catch (RuntimeException e) { + Slog.e(TAG, "Unable to forward package removed broadcast to KeyChain", e); + } + } + }; +} 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/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> |