diff options
81 files changed, 2430 insertions, 670 deletions
diff --git a/Android.bp b/Android.bp index 43fb48a089f3..80f1c375f7f7 100644 --- a/Android.bp +++ b/Android.bp @@ -65,3 +65,25 @@ subdirs = [ optional_subdirs = [ "core/tests/utiltests/jni", ] + +java_library { + name: "hwbinder", + no_framework_libs: true, + + srcs: [ + "core/java/android/os/HidlSupport.java", + "core/java/android/annotation/NonNull.java", + "core/java/android/os/HwBinder.java", + "core/java/android/os/HwBlob.java", + "core/java/android/os/HwParcel.java", + "core/java/android/os/IHwBinder.java", + "core/java/android/os/IHwInterface.java", + "core/java/android/os/DeadObjectException.java", + "core/java/android/os/DeadSystemException.java", + "core/java/android/os/RemoteException.java", + "core/java/android/util/AndroidException.java", + ], + + dxflags: ["--core-library"], + installable: false, +} diff --git a/Android.mk b/Android.mk index 7d31fdc662b2..98e4299f40be 100644 --- a/Android.mk +++ b/Android.mk @@ -652,32 +652,6 @@ $(framework_module): | $(dir $(framework_module))framework-res.apk framework_built := $(call java-lib-deps,framework) -# HwBinder -# ======================================================= -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - core/java/android/os/HidlSupport.java \ - core/java/android/annotation/NonNull.java \ - core/java/android/os/HwBinder.java \ - core/java/android/os/HwBlob.java \ - core/java/android/os/HwParcel.java \ - core/java/android/os/IHwBinder.java \ - core/java/android/os/IHwInterface.java \ - core/java/android/os/DeadObjectException.java \ - core/java/android/os/DeadSystemException.java \ - core/java/android/os/RemoteException.java \ - core/java/android/util/AndroidException.java \ - -LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core-oj core-libart -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := hwbinder - -LOCAL_DX_FLAGS := --core-library -LOCAL_UNINSTALLABLE_MODULE := true -include $(BUILD_JAVA_LIBRARY) - # Copy AIDL files to be preprocessed and included in the SDK, # specified relative to the root of the build tree. # ============================================================ diff --git a/api/current.txt b/api/current.txt index 6fae882267fd..f0aa1d395cd9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6449,6 +6449,7 @@ package android.app.admin { method public void uninstallAllUserCaCerts(android.content.ComponentName); method public void uninstallCaCert(android.content.ComponentName, byte[]); method public void wipeData(int); + method public void wipeDataWithReason(int, java.lang.CharSequence); field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED"; field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE"; @@ -6683,6 +6684,7 @@ package android.app.assist { method public android.graphics.Matrix getTransformation(); method public int getVisibility(); method public java.lang.String getWebDomain(); + method public java.lang.String getWebScheme(); method public int getWidth(); method public boolean isAccessibilityFocused(); method public boolean isActivated(); @@ -40690,8 +40692,13 @@ package android.telephony.mbms { public class DownloadStateCallback { ctor public DownloadStateCallback(); + ctor public DownloadStateCallback(int); + method public final boolean isFilterFlagSet(int); method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int); method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int); + field public static final int ALL_UPDATES = 0; // 0x0 + field public static final int PROGRESS_UPDATES = 1; // 0x1 + field public static final int STATE_UPDATES = 2; // 0x2 } public final class FileInfo implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 8c1169a39b68..4e6826564713 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6683,6 +6683,7 @@ package android.app.admin { method public void uninstallAllUserCaCerts(android.content.ComponentName); method public void uninstallCaCert(android.content.ComponentName, byte[]); method public void wipeData(int); + method public void wipeDataWithReason(int, java.lang.CharSequence); field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED"; field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; @@ -6933,6 +6934,7 @@ package android.app.assist { method public android.graphics.Matrix getTransformation(); method public int getVisibility(); method public java.lang.String getWebDomain(); + method public java.lang.String getWebScheme(); method public int getWidth(); method public boolean isAccessibilityFocused(); method public boolean isActivated(); @@ -40890,17 +40892,22 @@ package android.service.settings.suggestions { public final class Suggestion implements android.os.Parcelable { method public int describeContents(); + method public int getFlags(); + method public android.graphics.drawable.Icon getIcon(); method public java.lang.String getId(); method public android.app.PendingIntent getPendingIntent(); method public java.lang.CharSequence getSummary(); method public java.lang.CharSequence getTitle(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.service.settings.suggestions.Suggestion> CREATOR; + field public static final int FLAG_HAS_BUTTON = 1; // 0x1 } public static class Suggestion.Builder { ctor public Suggestion.Builder(java.lang.String); method public android.service.settings.suggestions.Suggestion build(); + method public android.service.settings.suggestions.Suggestion.Builder setFlags(int); + method public android.service.settings.suggestions.Suggestion.Builder setIcon(android.graphics.drawable.Icon); method public android.service.settings.suggestions.Suggestion.Builder setPendingIntent(android.app.PendingIntent); method public android.service.settings.suggestions.Suggestion.Builder setSummary(java.lang.CharSequence); method public android.service.settings.suggestions.Suggestion.Builder setTitle(java.lang.CharSequence); @@ -44287,8 +44294,13 @@ package android.telephony.mbms { public class DownloadStateCallback { ctor public DownloadStateCallback(); + ctor public DownloadStateCallback(int); + method public final boolean isFilterFlagSet(int); method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int); method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int); + field public static final int ALL_UPDATES = 0; // 0x0 + field public static final int PROGRESS_UPDATES = 1; // 0x1 + field public static final int STATE_UPDATES = 2; // 0x2 } public final class FileInfo implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index 65918c5bb8c8..435f3b4a263f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -6517,6 +6517,7 @@ package android.app.admin { method public void uninstallAllUserCaCerts(android.content.ComponentName); method public void uninstallCaCert(android.content.ComponentName, byte[]); method public void wipeData(int); + method public void wipeDataWithReason(int, java.lang.CharSequence); field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED"; field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; @@ -6754,6 +6755,7 @@ package android.app.assist { method public android.graphics.Matrix getTransformation(); method public int getVisibility(); method public java.lang.String getWebDomain(); + method public java.lang.String getWebScheme(); method public int getWidth(); method public boolean isAccessibilityFocused(); method public boolean isActivated(); @@ -41040,8 +41042,13 @@ package android.telephony.mbms { public class DownloadStateCallback { ctor public DownloadStateCallback(); + ctor public DownloadStateCallback(int); + method public final boolean isFilterFlagSet(int); method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int); method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int); + field public static final int ALL_UPDATES = 0; // 0x0 + field public static final int PROGRESS_UPDATES = 1; // 0x1 + field public static final int STATE_UPDATES = 2; // 0x2 } public final class FileInfo implements android.os.Parcelable { diff --git a/core/java/android/annotation/NavigationRes.java b/core/java/android/annotation/NavigationRes.java new file mode 100644 index 000000000000..3af5ecff84a6 --- /dev/null +++ b/core/java/android/annotation/NavigationRes.java @@ -0,0 +1,37 @@ +/* + * 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.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Denotes that an integer parameter, field or method return value is expected + * to be a navigation resource reference (e.g. {@code R.navigation.flow}). + * + * {@hide} + */ +@Documented +@Retention(SOURCE) +@Target({METHOD, PARAMETER, FIELD}) +public @interface NavigationRes { +} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4e258a3a4b47..e0ac91130385 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -114,6 +114,7 @@ import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.autofill.AutofillManager; +import android.view.autofill.AutofillManager.AutofillClient; import android.view.autofill.AutofillPopupWindow; import android.view.autofill.IAutofillWindowPresenter; import android.widget.AdapterView; @@ -947,6 +948,18 @@ public class Activity extends ContextThemeWrapper return mAutofillManager; } + @Override + protected void attachBaseContext(Context newBase) { + super.attachBaseContext(newBase); + newBase.setAutofillClient(this); + } + + /** @hide */ + @Override + public final AutofillClient getAutofillClient() { + return this; + } + /** * Called when the activity is starting. This is where most initialization * should go: calling {@link #setContentView(int)} to inflate the diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b07327f650c7..2516a3e970df 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5780,7 +5780,7 @@ public final class ActivityThread { final int preloadedFontsResource = info.metaData.getInt( ApplicationInfo.METADATA_PRELOADED_FONTS, 0); if (preloadedFontsResource != 0) { - data.info.mResources.preloadFonts(preloadedFontsResource); + data.info.getResources().preloadFonts(preloadedFontsResource); } } } catch (RemoteException e) { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index c48be7705706..5f3432264ca0 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -74,6 +74,7 @@ import android.util.Log; import android.util.Slog; import android.view.Display; import android.view.DisplayAdjustments; +import android.view.autofill.AutofillManager.AutofillClient; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; @@ -185,6 +186,8 @@ class ContextImpl extends Context { // The name of the split this Context is representing. May be null. private @Nullable String mSplitName = null; + private AutofillClient mAutofillClient = null; + private final Object mSync = new Object(); @GuardedBy("mSync") @@ -2225,6 +2228,18 @@ class ContextImpl extends Context { return mUser.getIdentifier(); } + /** @hide */ + @Override + public AutofillClient getAutofillClient() { + return mAutofillClient; + } + + /** @hide */ + @Override + public void setAutofillClient(AutofillClient client) { + mAutofillClient = client; + } + static ContextImpl createSystemContext(ActivityThread mainThread) { LoadedApk packageInfo = new LoadedApk(mainThread); ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0, diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 163a8dcae2f4..64b9ae827da6 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -25,6 +25,7 @@ import android.os.Parcelable; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.text.TextUtils; +import android.util.proto.ProtoOutputStream; import org.json.JSONException; import org.json.JSONObject; @@ -135,12 +136,15 @@ public final class NotificationChannel implements Parcelable { private boolean mLights; private int mLightColor = DEFAULT_LIGHT_COLOR; private long[] mVibration; + // Bitwise representation of fields that have been changed by the user, preventing the app from + // making changes to these fields. private int mUserLockedFields; private boolean mVibrationEnabled; private boolean mShowBadge = DEFAULT_SHOW_BADGE; private boolean mDeleted = DEFAULT_DELETED; private String mGroup; private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; + // If this is a blockable system notification channel. private boolean mBlockableSystem = false; /** @@ -850,4 +854,35 @@ public final class NotificationChannel implements Parcelable { + ", mBlockableSystem=" + mBlockableSystem + '}'; } + + /** @hide */ + public void toProto(ProtoOutputStream proto) { + proto.write(NotificationChannelProto.ID, mId); + proto.write(NotificationChannelProto.NAME, mName); + proto.write(NotificationChannelProto.DESCRIPTION, mDesc); + proto.write(NotificationChannelProto.IMPORTANCE, mImportance); + proto.write(NotificationChannelProto.CAN_BYPASS_DND, mBypassDnd); + proto.write(NotificationChannelProto.LOCKSCREEN_VISIBILITY, mLockscreenVisibility); + if (mSound != null) { + proto.write(NotificationChannelProto.SOUND, mSound.toString()); + } + proto.write(NotificationChannelProto.USE_LIGHTS, mLights); + proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor); + if (mVibration != null) { + for (long v : mVibration) { + proto.write(NotificationChannelProto.VIBRATION, v); + } + } + proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields); + proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled); + proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge); + proto.write(NotificationChannelProto.IS_DELETED, mDeleted); + proto.write(NotificationChannelProto.GROUP, mGroup); + if (mAudioAttributes != null) { + long aToken = proto.start(NotificationChannelProto.AUDIO_ATTRIBUTES); + mAudioAttributes.toProto(proto); + proto.end(aToken); + } + proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem); + } } diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java index 51733114f8b9..5cb7fb7a6707 100644 --- a/core/java/android/app/NotificationChannelGroup.java +++ b/core/java/android/app/NotificationChannelGroup.java @@ -21,6 +21,7 @@ import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.proto.ProtoOutputStream; import org.json.JSONException; import org.json.JSONObject; @@ -295,4 +296,15 @@ public final class NotificationChannelGroup implements Parcelable { + ", mChannels=" + mChannels + '}'; } + + /** @hide */ + public void toProto(ProtoOutputStream proto) { + proto.write(NotificationChannelGroupProto.ID, mId); + proto.write(NotificationChannelGroupProto.NAME, mName.toString()); + proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription); + proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked); + for (NotificationChannel channel : mChannels) { + channel.toProto(proto); + } + } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index d9670ca7879c..3c5306331716 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -63,7 +63,9 @@ import android.telephony.TelephonyManager; import android.util.ArraySet; import android.util.Log; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; import com.android.org.conscrypt.TrustedCertificateStore; import java.io.ByteArrayInputStream; @@ -3142,6 +3144,7 @@ public class DevicePolicyManager { */ public static final int WIPE_EUICC = 0x0004; + /** * Ask that all user data be wiped. If called as a secondary user, the user will be removed and * other users will remain unaffected. Calling from the primary user will cause the device to @@ -3157,10 +3160,47 @@ public class DevicePolicyManager { * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} */ public void wipeData(int flags) { - throwIfParentInstance("wipeData"); + final String wipeReasonForUser = mContext.getString( + R.string.work_profile_deleted_description_dpm_wipe); + wipeDataInternal(flags, wipeReasonForUser); + } + + /** + * Ask that all user data be wiped. If called as a secondary user, the user will be removed and + * other users will remain unaffected, the provided reason for wiping data can be shown to + * user. Calling from the primary user will cause the device to reboot, erasing all device data + * - including all the secondary users and their data - while booting up. In this case, we don't + * show the reason to the user since the device would be factory reset. + * <p> + * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to + * be able to call this method; if it has not, a security exception will be thrown. + * + * @param flags Bit mask of additional options: currently supported flags are + * {@link #WIPE_EXTERNAL_STORAGE} and {@link #WIPE_RESET_PROTECTION_DATA}. + * @param reason a string that contains the reason for wiping data, which can be + * presented to the user. + * @throws SecurityException if the calling application does not own an active administrator + * that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} + * @throws IllegalArgumentException if the input reason string is null or empty. + */ + public void wipeDataWithReason(int flags, @NonNull CharSequence reason) { + Preconditions.checkNotNull(reason, "CharSequence is null"); + wipeDataInternal(flags, reason.toString()); + } + + /** + * Internal function for both {@link #wipeData(int)} and + * {@link #wipeDataWithReason(int, CharSequence)} to call. + * + * @see #wipeData(int) + * @see #wipeDataWithReason(int, CharSequence) + * @hide + */ + private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) { + throwIfParentInstance("wipeDataWithReason"); if (mService != null) { try { - mService.wipeData(flags); + mService.wipeDataWithReason(flags, wipeReasonForUser); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index acfb602834ef..8865a0525292 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -95,7 +95,7 @@ interface IDevicePolicyManager { void lockNow(int flags, boolean parent); - void wipeData(int flags); + void wipeDataWithReason(int flags, String wipeReasonForUser); ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList); ComponentName getGlobalProxyAdmin(int userHandle); diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index c208f1dbba8a..d9b7cd7e9e45 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -674,6 +674,7 @@ public class AssistStructure implements Parcelable { ViewNodeText mText; int mInputType; + String mWebScheme; String mWebDomain; Bundle mExtras; LocaleList mLocaleList; @@ -751,6 +752,7 @@ public class AssistStructure implements Parcelable { mInputType = in.readInt(); } if ((flags&FLAGS_HAS_URL) != 0) { + mWebScheme = in.readString(); mWebDomain = in.readString(); } if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) { @@ -813,7 +815,7 @@ public class AssistStructure implements Parcelable { if (mInputType != 0) { flags |= FLAGS_HAS_INPUT_TYPE; } - if (mWebDomain != null) { + if (mWebScheme != null || mWebDomain != null) { flags |= FLAGS_HAS_URL; } if (mLocaleList != null) { @@ -908,6 +910,7 @@ public class AssistStructure implements Parcelable { out.writeInt(mInputType); } if ((flags&FLAGS_HAS_URL) != 0) { + out.writeString(mWebScheme); out.writeString(mWebDomain); } if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) { @@ -1265,13 +1268,26 @@ public class AssistStructure implements Parcelable { * {@link android.service.autofill.AutofillService} for more details. * * @return domain-only part of the document. For example, if the full URL is - * {@code https://my.site/login?user=my_user}, it returns {@code my.site}. + * {@code https://example.com/login?user=my_user}, it returns {@code example.com}. */ @Nullable public String getWebDomain() { return mWebDomain; } /** + * Returns the scheme of the HTML document represented by this view. + * + * <p>Typically used when the view associated with the view is a container for an HTML + * document. + * + * @return scheme-only part of the document. For example, if the full URL is + * {@code https://example.com/login?user=my_user}, it returns {@code https}. + */ + @Nullable public String getWebScheme() { + return mWebScheme; + } + + /** * Returns the HTML properties associated with this view. * * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes, @@ -1767,10 +1783,13 @@ public class AssistStructure implements Parcelable { @Override public void setWebDomain(@Nullable String domain) { if (domain == null) { + mNode.mWebScheme = null; mNode.mWebDomain = null; return; } - mNode.mWebDomain = Uri.parse(domain).getHost(); + Uri uri = Uri.parse(domain); + mNode.mWebScheme = uri.getScheme(); + mNode.mWebDomain = uri.getHost(); } @Override diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index 87e516cabba5..1434c9baadf3 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -317,7 +317,8 @@ public class JobInfo implements Parcelable { } /** - * Whether this job needs the device to be plugged in. + * Whether this job requires that the device be charging (or be a non-battery-powered + * device connected to permanent power, such as Android TV devices). */ public boolean isRequireCharging() { return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0; @@ -331,7 +332,10 @@ public class JobInfo implements Parcelable { } /** - * Whether this job needs the device to be in an Idle maintenance window. + * Whether this job requires that the user <em>not</em> be interacting with the device. + * + * <p class="note">This is <em>not</em> the same as "doze" or "device idle"; + * it is purely about the user's direct interactions.</p> */ public boolean isRequireDeviceIdle() { return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0; @@ -918,9 +922,19 @@ public class JobInfo implements Parcelable { } /** - * Specify that to run this job, the device needs to be plugged in. This defaults to - * false. - * @param requiresCharging Whether or not the device is plugged in. + * Specify that to run this job, the device must be charging (or be a + * non-battery-powered device connected to permanent power, such as Android TV + * devices). This defaults to {@code false}. + * + * <p class="note">For purposes of running jobs, a battery-powered device + * "charging" is not quite the same as simply being connected to power. If the + * device is so busy that the battery is draining despite a power connection, jobs + * with this constraint will <em>not</em> run. This can happen during some + * common use cases such as video chat, particularly if the device is plugged in + * to USB rather than to wall power. + * + * @param requiresCharging Pass {@code true} to require that the device be + * charging in order to run the job. */ public Builder setRequiresCharging(boolean requiresCharging) { mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING) @@ -942,14 +956,22 @@ public class JobInfo implements Parcelable { } /** - * Specify that to run, the job needs the device to be in idle mode. This defaults to - * false. - * <p>Idle mode is a loose definition provided by the system, which means that the device - * is not in use, and has not been in use for some time. As such, it is a good time to - * perform resource heavy jobs. Bear in mind that battery usage will still be attributed - * to your application, and surfaced to the user in battery stats.</p> - * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance - * window. + * When set {@code true}, ensure that this job will not run if the device is in active use. + * The default state is {@code false}: that is, the for the job to be runnable even when + * someone is interacting with the device. + * + * <p>This state is a loose definition provided by the system. In general, it means that + * the device is not currently being used interactively, and has not been in use for some + * time. As such, it is a good time to perform resource heavy jobs. Bear in mind that + * battery usage will still be attributed to your application, and surfaced to the user in + * battery stats.</p> + * + * <p class="note">Despite the similar naming, this job constraint is <em>not</em> + * related to the system's "device idle" or "doze" states. This constraint only + * determines whether a job is allowed to run while the device is directly in use. + * + * @param requiresDeviceIdle Pass {@code true} to prevent the job from running + * while the device is being used interactively. */ public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) { mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE) diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java index 19f5198ca71a..a1a9347df690 100644 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.SdkConstant; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -53,35 +54,32 @@ public class BluetoothPbap { private static final boolean DBG = true; private static final boolean VDBG = false; - /** int extra for PBAP_STATE_CHANGED_ACTION */ - public static final String PBAP_STATE = - "android.bluetooth.pbap.intent.PBAP_STATE"; - /** int extra for PBAP_STATE_CHANGED_ACTION */ - public static final String PBAP_PREVIOUS_STATE = - "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE"; - /** - * Indicates the state of a pbap connection state has changed. - * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and - * BluetoothIntent.ADDRESS extras. + * Intent used to broadcast the change in connection state of the PBAP + * profile. + * + * <p>This intent will have 3 extras: + * <ul> + * <li> {@link BluetoothProfile#EXTRA_STATE} - The current state of the profile. </li> + * <li> {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * </ul> + * <p>{@link BluetoothProfile#EXTRA_STATE} or {@link BluetoothProfile#EXTRA_PREVIOUS_STATE} + * can be any of {@link BluetoothProfile#STATE_DISCONNECTED}, + * {@link BluetoothProfile#STATE_CONNECTING}, {@link BluetoothProfile#STATE_CONNECTED}, + * {@link BluetoothProfile#STATE_DISCONNECTING}. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. */ - public static final String PBAP_STATE_CHANGED_ACTION = - "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED"; + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; private volatile IBluetoothPbap mService; private final Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; - /** There was an error trying to obtain the state */ - public static final int STATE_ERROR = -1; - /** No client currently connected */ - public static final int STATE_DISCONNECTED = 0; - /** Connection attempt in progress */ - public static final int STATE_CONNECTING = 1; - /** Client is currently connected */ - public static final int STATE_CONNECTED = 2; - public static final int RESULT_FAILURE = 0; public static final int RESULT_SUCCESS = 1; /** Connection canceled before completion. */ @@ -209,8 +207,8 @@ public class BluetoothPbap { /** * Get the current state of the BluetoothPbap service. * - * @return One of the STATE_ return codes, or STATE_ERROR if this proxy object is currently not - * connected to the Pbap service. + * @return One of the STATE_ return codes, or {@link BluetoothProfile#STATE_DISCONNECTED} + * if this proxy object is currently not connected to the Pbap service. */ public int getState() { if (VDBG) log("getState()"); @@ -225,7 +223,7 @@ public class BluetoothPbap { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); } - return BluetoothPbap.STATE_ERROR; + return BluetoothProfile.STATE_DISCONNECTED; } /** diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java index 00a15f3f7087..01b3f6e0e841 100644 --- a/core/java/android/bluetooth/BluetoothPbapClient.java +++ b/core/java/android/bluetooth/BluetoothPbapClient.java @@ -40,7 +40,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { private static final boolean VDBG = false; public static final String ACTION_CONNECTION_STATE_CHANGED = - "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; + "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; private volatile IBluetoothPbapClient mService; private final Context mContext; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index cd9c6a30c33c..20fbf046a577 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -64,6 +64,7 @@ import android.view.DisplayAdjustments; import android.view.View; import android.view.ViewDebug; import android.view.WindowManager; +import android.view.autofill.AutofillManager.AutofillClient; import android.view.textclassifier.TextClassificationManager; import java.io.File; @@ -4787,6 +4788,19 @@ public abstract class Context { } /** + * @hide + */ + public AutofillClient getAutofillClient() { + return null; + } + + /** + * @hide + */ + public void setAutofillClient(AutofillClient client) { + } + + /** * Throws an exception if the Context is using system resources, * which are non-runtime-overlay-themable and may show inconsistent UI. * @hide diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index a9fd58bc950c..85acdc6b8101 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -37,6 +37,7 @@ import android.os.Looper; import android.os.UserHandle; import android.view.Display; import android.view.DisplayAdjustments; +import android.view.autofill.AutofillManager.AutofillClient; import java.io.File; import java.io.FileInputStream; @@ -967,7 +968,24 @@ public class ContextWrapper extends Context { /** * @hide */ + @Override public int getNextAutofillId() { return mBase.getNextAutofillId(); } + + /** + * @hide + */ + @Override + public AutofillClient getAutofillClient() { + return mBase.getAutofillClient(); + } + + /** + * @hide + */ + @Override + public void setAutofillClient(AutofillClient client) { + mBase.setAutofillClient(client); + } } diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java index ead406c20c93..79310e295a27 100644 --- a/core/java/android/net/IpSecAlgorithm.java +++ b/core/java/android/net/IpSecAlgorithm.java @@ -24,6 +24,7 @@ import com.android.internal.util.HexDump; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; /** * IpSecAlgorithm specifies a single algorithm that can be applied to an IpSec Transform. Refer to @@ -75,13 +76,7 @@ public final class IpSecAlgorithm implements Parcelable { public static final String AUTH_HMAC_SHA512 = "hmac(sha512)"; /** @hide */ - @StringDef({ - CRYPT_AES_CBC, - AUTH_HMAC_MD5, - AUTH_HMAC_SHA1, - AUTH_HMAC_SHA256, - AUTH_HMAC_SHA512 - }) + @StringDef({CRYPT_AES_CBC, AUTH_HMAC_MD5, AUTH_HMAC_SHA1, AUTH_HMAC_SHA256, AUTH_HMAC_SHA512}) @Retention(RetentionPolicy.SOURCE) public @interface AlgorithmName {} @@ -197,4 +192,12 @@ public final class IpSecAlgorithm implements Parcelable { .append("}") .toString(); } + + /** package */ + static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) { + if (lhs == null || rhs == null) return (lhs == rhs); + return (lhs.mName.equals(rhs.mName) + && Arrays.equals(lhs.mKey, rhs.mKey) + && lhs.mTruncLenBits == rhs.mTruncLenBits); + } }; diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java index 5a5c740c7aa8..ceccc07c3c43 100644 --- a/core/java/android/net/IpSecConfig.java +++ b/core/java/android/net/IpSecConfig.java @@ -17,105 +17,170 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; -import android.util.Log; -import java.net.InetAddress; -import java.net.UnknownHostException; + +import com.android.internal.annotations.VisibleForTesting; /** @hide */ public final class IpSecConfig implements Parcelable { private static final String TAG = "IpSecConfig"; - //MODE_TRANSPORT or MODE_TUNNEL - int mode; + // MODE_TRANSPORT or MODE_TUNNEL + private int mMode = IpSecTransform.MODE_TRANSPORT; - // For tunnel mode - InetAddress localAddress; + // Needs to be valid only for tunnel mode + // Preventing this from being null simplifies Java->Native binder + private String mLocalAddress = ""; - InetAddress remoteAddress; + // Preventing this from being null simplifies Java->Native binder + private String mRemoteAddress = ""; - // Limit selection by network interface - Network network; + // The underlying network interface that represents the "gateway" Network + // for outbound packets. It may also be used to select packets. + private Network mNetwork; public static class Flow { // Minimum requirements for identifying a transform // SPI identifying the IPsec flow in packet processing // and a remote IP address - int spiResourceId; + private int mSpiResourceId = IpSecManager.INVALID_RESOURCE_ID; // Encryption Algorithm - IpSecAlgorithm encryption; + private IpSecAlgorithm mEncryption; // Authentication Algorithm - IpSecAlgorithm authentication; + private IpSecAlgorithm mAuthentication; @Override public String toString() { return new StringBuilder() - .append("{spiResourceId=") - .append(spiResourceId) - .append(", encryption=") - .append(encryption) - .append(", authentication=") - .append(authentication) + .append("{mSpiResourceId=") + .append(mSpiResourceId) + .append(", mEncryption=") + .append(mEncryption) + .append(", mAuthentication=") + .append(mAuthentication) .append("}") .toString(); } + + static boolean equals(IpSecConfig.Flow lhs, IpSecConfig.Flow rhs) { + if (lhs == null || rhs == null) return (lhs == rhs); + return (lhs.mSpiResourceId == rhs.mSpiResourceId + && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption) + && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication)); + } } - final Flow[] flow = new Flow[] {new Flow(), new Flow()}; + private final Flow[] mFlow = new Flow[] {new Flow(), new Flow()}; // For tunnel mode IPv4 UDP Encapsulation // IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE - int encapType; - int encapLocalPortResourceId; - int encapRemotePort; + private int mEncapType = IpSecTransform.ENCAP_NONE; + private int mEncapSocketResourceId = IpSecManager.INVALID_RESOURCE_ID; + private int mEncapRemotePort; // An interval, in seconds between the NattKeepalive packets - int nattKeepaliveInterval; + private int mNattKeepaliveInterval; + + /** Set the mode for this IPsec transform */ + public void setMode(int mode) { + mMode = mode; + } + + /** Set the local IP address for Tunnel mode */ + public void setLocalAddress(String localAddress) { + if (localAddress == null) { + throw new IllegalArgumentException("localAddress may not be null!"); + } + mLocalAddress = localAddress; + } + + /** Set the remote IP address for this IPsec transform */ + public void setRemoteAddress(String remoteAddress) { + if (remoteAddress == null) { + throw new IllegalArgumentException("remoteAddress may not be null!"); + } + mRemoteAddress = remoteAddress; + } + + /** Set the SPI for a given direction by resource ID */ + public void setSpiResourceId(int direction, int resourceId) { + mFlow[direction].mSpiResourceId = resourceId; + } + + /** Set the encryption algorithm for a given direction */ + public void setEncryption(int direction, IpSecAlgorithm encryption) { + mFlow[direction].mEncryption = encryption; + } + + /** Set the authentication algorithm for a given direction */ + public void setAuthentication(int direction, IpSecAlgorithm authentication) { + mFlow[direction].mAuthentication = authentication; + } + + public void setNetwork(Network network) { + mNetwork = network; + } + + public void setEncapType(int encapType) { + mEncapType = encapType; + } + + public void setEncapSocketResourceId(int resourceId) { + mEncapSocketResourceId = resourceId; + } + + public void setEncapRemotePort(int port) { + mEncapRemotePort = port; + } + + public void setNattKeepaliveInterval(int interval) { + mNattKeepaliveInterval = interval; + } // Transport or Tunnel public int getMode() { - return mode; + return mMode; } - public InetAddress getLocalAddress() { - return localAddress; + public String getLocalAddress() { + return mLocalAddress; } public int getSpiResourceId(int direction) { - return flow[direction].spiResourceId; + return mFlow[direction].mSpiResourceId; } - public InetAddress getRemoteAddress() { - return remoteAddress; + public String getRemoteAddress() { + return mRemoteAddress; } public IpSecAlgorithm getEncryption(int direction) { - return flow[direction].encryption; + return mFlow[direction].mEncryption; } public IpSecAlgorithm getAuthentication(int direction) { - return flow[direction].authentication; + return mFlow[direction].mAuthentication; } public Network getNetwork() { - return network; + return mNetwork; } public int getEncapType() { - return encapType; + return mEncapType; } - public int getEncapLocalResourceId() { - return encapLocalPortResourceId; + public int getEncapSocketResourceId() { + return mEncapSocketResourceId; } public int getEncapRemotePort() { - return encapRemotePort; + return mEncapRemotePort; } public int getNattKeepaliveInterval() { - return nattKeepaliveInterval; + return mNattKeepaliveInterval; } // Parcelable Methods @@ -127,82 +192,70 @@ public final class IpSecConfig implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - // TODO: Use a byte array or other better method for storing IPs that can also include scope - out.writeString((localAddress != null) ? localAddress.getHostAddress() : null); - // TODO: Use a byte array or other better method for storing IPs that can also include scope - out.writeString((remoteAddress != null) ? remoteAddress.getHostAddress() : null); - out.writeParcelable(network, flags); - out.writeInt(flow[IpSecTransform.DIRECTION_IN].spiResourceId); - out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryption, flags); - out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authentication, flags); - out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spiResourceId); - out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryption, flags); - out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authentication, flags); - out.writeInt(encapType); - out.writeInt(encapLocalPortResourceId); - out.writeInt(encapRemotePort); - } - - // Package Private: Used by the IpSecTransform.Builder; - // there should be no public constructor for this object - IpSecConfig() {} - - private static InetAddress readInetAddressFromParcel(Parcel in) { - String addrString = in.readString(); - if (addrString == null) { - return null; - } - try { - return InetAddress.getByName(addrString); - } catch (UnknownHostException e) { - Log.wtf(TAG, "Invalid IpAddress " + addrString); - return null; - } + out.writeInt(mMode); + out.writeString(mLocalAddress); + out.writeString(mRemoteAddress); + out.writeParcelable(mNetwork, flags); + out.writeInt(mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId); + out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mEncryption, flags); + out.writeParcelable(mFlow[IpSecTransform.DIRECTION_IN].mAuthentication, flags); + out.writeInt(mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId); + out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mEncryption, flags); + out.writeParcelable(mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication, flags); + out.writeInt(mEncapType); + out.writeInt(mEncapSocketResourceId); + out.writeInt(mEncapRemotePort); + out.writeInt(mNattKeepaliveInterval); } + @VisibleForTesting + public IpSecConfig() {} + private IpSecConfig(Parcel in) { - localAddress = readInetAddressFromParcel(in); - remoteAddress = readInetAddressFromParcel(in); - network = (Network) in.readParcelable(Network.class.getClassLoader()); - flow[IpSecTransform.DIRECTION_IN].spiResourceId = in.readInt(); - flow[IpSecTransform.DIRECTION_IN].encryption = + mMode = in.readInt(); + mLocalAddress = in.readString(); + mRemoteAddress = in.readString(); + mNetwork = (Network) in.readParcelable(Network.class.getClassLoader()); + mFlow[IpSecTransform.DIRECTION_IN].mSpiResourceId = in.readInt(); + mFlow[IpSecTransform.DIRECTION_IN].mEncryption = (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); - flow[IpSecTransform.DIRECTION_IN].authentication = + mFlow[IpSecTransform.DIRECTION_IN].mAuthentication = (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); - flow[IpSecTransform.DIRECTION_OUT].spiResourceId = in.readInt(); - flow[IpSecTransform.DIRECTION_OUT].encryption = + mFlow[IpSecTransform.DIRECTION_OUT].mSpiResourceId = in.readInt(); + mFlow[IpSecTransform.DIRECTION_OUT].mEncryption = (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); - flow[IpSecTransform.DIRECTION_OUT].authentication = + mFlow[IpSecTransform.DIRECTION_OUT].mAuthentication = (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); - encapType = in.readInt(); - encapLocalPortResourceId = in.readInt(); - encapRemotePort = in.readInt(); + mEncapType = in.readInt(); + mEncapSocketResourceId = in.readInt(); + mEncapRemotePort = in.readInt(); + mNattKeepaliveInterval = in.readInt(); } @Override public String toString() { StringBuilder strBuilder = new StringBuilder(); strBuilder - .append("{mode=") - .append(mode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT") - .append(", localAddress=") - .append(localAddress) - .append(", remoteAddress=") - .append(remoteAddress) - .append(", network=") - .append(network) - .append(", encapType=") - .append(encapType) - .append(", encapLocalPortResourceId=") - .append(encapLocalPortResourceId) - .append(", encapRemotePort=") - .append(encapRemotePort) - .append(", nattKeepaliveInterval=") - .append(nattKeepaliveInterval) - .append(", flow[OUT]=") - .append(flow[IpSecTransform.DIRECTION_OUT]) - .append(", flow[IN]=") - .append(flow[IpSecTransform.DIRECTION_IN]) + .append("{mMode=") + .append(mMode == IpSecTransform.MODE_TUNNEL ? "TUNNEL" : "TRANSPORT") + .append(", mLocalAddress=") + .append(mLocalAddress) + .append(", mRemoteAddress=") + .append(mRemoteAddress) + .append(", mNetwork=") + .append(mNetwork) + .append(", mEncapType=") + .append(mEncapType) + .append(", mEncapSocketResourceId=") + .append(mEncapSocketResourceId) + .append(", mEncapRemotePort=") + .append(mEncapRemotePort) + .append(", mNattKeepaliveInterval=") + .append(mNattKeepaliveInterval) + .append(", mFlow[OUT]=") + .append(mFlow[IpSecTransform.DIRECTION_OUT]) + .append(", mFlow[IN]=") + .append(mFlow[IpSecTransform.DIRECTION_IN]) .append("}"); return strBuilder.toString(); @@ -218,4 +271,22 @@ public final class IpSecConfig implements Parcelable { return new IpSecConfig[size]; } }; + + @VisibleForTesting + public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) { + if (lhs == null || rhs == null) return (lhs == rhs); + return (lhs.mMode == rhs.mMode + && lhs.mLocalAddress.equals(rhs.mLocalAddress) + && lhs.mRemoteAddress.equals(rhs.mRemoteAddress) + && ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork)) + || (lhs.mNetwork == rhs.mNetwork)) + && lhs.mEncapType == rhs.mEncapType + && lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId + && lhs.mEncapRemotePort == rhs.mEncapRemotePort + && lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval + && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_OUT], + rhs.mFlow[IpSecTransform.DIRECTION_OUT]) + && IpSecConfig.Flow.equals(lhs.mFlow[IpSecTransform.DIRECTION_IN], + rhs.mFlow[IpSecTransform.DIRECTION_IN])); + } } diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index d7908c8ceef8..d7b325613fee 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -26,6 +26,8 @@ import android.os.RemoteException; import android.util.AndroidException; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import dalvik.system.CloseGuard; import java.io.FileDescriptor; @@ -188,7 +190,8 @@ public final class IpSecManager { } /** @hide */ - int getResourceId() { + @VisibleForTesting + public int getResourceId() { return mResourceId; } } @@ -489,7 +492,8 @@ public final class IpSecManager { } /** @hide */ - int getResourceId() { + @VisibleForTesting + public int getResourceId() { return mResourceId; } }; diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index 62fd65b9c176..529cf1a4956e 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -68,10 +68,10 @@ public final class IpSecTransform implements AutoCloseable { public @interface TransformDirection {} /** @hide */ - public static final int MODE_TUNNEL = 0; + public static final int MODE_TRANSPORT = 0; /** @hide */ - public static final int MODE_TRANSPORT = 1; + public static final int MODE_TUNNEL = 1; /** @hide */ public static final int ENCAP_NONE = 0; @@ -113,7 +113,11 @@ public final class IpSecTransform implements AutoCloseable { return IIpSecService.Stub.asInterface(b); } - private void checkResultStatusAndThrow(int status) + /** + * Checks the result status and throws an appropriate exception if + * the status is not Status.OK. + */ + private void checkResultStatus(int status) throws IOException, IpSecManager.ResourceUnavailableException, IpSecManager.SpiUnavailableException { switch (status) { @@ -141,7 +145,7 @@ public final class IpSecTransform implements AutoCloseable { IpSecTransformResponse result = svc.createTransportModeTransform(mConfig, new Binder()); int status = result.status; - checkResultStatusAndThrow(status); + checkResultStatus(status); mResourceId = result.resourceId; /* Keepalive will silently fail if not needed by the config; but, if needed and @@ -243,61 +247,20 @@ public final class IpSecTransform implements AutoCloseable { /* Package */ void startKeepalive(Context c) { - // FIXME: NO_KEEPALIVE needs to be a constant - if (mConfig.getNattKeepaliveInterval() == 0) { - return; - } - - ConnectivityManager cm = - (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE); - - if (mKeepalive != null) { - Log.wtf(TAG, "Keepalive already started for this IpSecTransform."); - return; - } - - synchronized (mKeepaliveSyncLock) { - mKeepalive = - cm.startNattKeepalive( - mConfig.getNetwork(), - mConfig.getNattKeepaliveInterval(), - mKeepaliveCallback, - mConfig.getLocalAddress(), - 0x1234, /* FIXME: get the real port number again, - which we need to retrieve from the provided - EncapsulationSocket, and which isn't currently - stashed in IpSecConfig */ - mConfig.getRemoteAddress()); - try { - // FIXME: this is still a horrible way to fudge the synchronous callback - mKeepaliveSyncLock.wait(2000); - } catch (InterruptedException e) { - } - } - if (mKeepaliveStatus != ConnectivityManager.PacketKeepalive.SUCCESS) { - throw new UnsupportedOperationException("Packet Keepalive cannot be started"); + if (mConfig.getNattKeepaliveInterval() != 0) { + Log.wtf(TAG, "Keepalive not yet supported."); } } - /* Package */ - int getResourceId() { + /** @hide */ + @VisibleForTesting + public int getResourceId() { return mResourceId; } /* Package */ void stopKeepalive() { - if (mKeepalive == null) { - return; - } - mKeepalive.stop(); - synchronized (mKeepaliveSyncLock) { - if (mKeepaliveStatus == ConnectivityManager.PacketKeepalive.SUCCESS) { - try { - mKeepaliveSyncLock.wait(2000); - } catch (InterruptedException e) { - } - } - } + return; } /** @@ -323,7 +286,7 @@ public final class IpSecTransform implements AutoCloseable { */ public IpSecTransform.Builder setEncryption( @TransformDirection int direction, IpSecAlgorithm algo) { - mConfig.flow[direction].encryption = algo; + mConfig.setEncryption(direction, algo); return this; } @@ -338,7 +301,7 @@ public final class IpSecTransform implements AutoCloseable { */ public IpSecTransform.Builder setAuthentication( @TransformDirection int direction, IpSecAlgorithm algo) { - mConfig.flow[direction].authentication = algo; + mConfig.setAuthentication(direction, algo); return this; } @@ -361,9 +324,7 @@ public final class IpSecTransform implements AutoCloseable { */ public IpSecTransform.Builder setSpi( @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) { - // TODO: convert to using the resource Id of the SPI. Then build() can validate - // the owner in the IpSecService - mConfig.flow[direction].spiResourceId = spi.getResourceId(); + mConfig.setSpiResourceId(direction, spi.getResourceId()); return this; } @@ -378,7 +339,7 @@ public final class IpSecTransform implements AutoCloseable { */ @SystemApi public IpSecTransform.Builder setUnderlyingNetwork(Network net) { - mConfig.network = net; + mConfig.setNetwork(net); return this; } @@ -395,10 +356,9 @@ public final class IpSecTransform implements AutoCloseable { */ public IpSecTransform.Builder setIpv4Encapsulation( IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) { - // TODO: check encap type is valid. - mConfig.encapType = ENCAP_ESPINUDP; - mConfig.encapLocalPortResourceId = localSocket.getResourceId(); - mConfig.encapRemotePort = remotePort; + mConfig.setEncapType(ENCAP_ESPINUDP); + mConfig.setEncapSocketResourceId(localSocket.getResourceId()); + mConfig.setEncapRemotePort(remotePort); return this; } @@ -416,7 +376,7 @@ public final class IpSecTransform implements AutoCloseable { */ @SystemApi public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) { - mConfig.nattKeepaliveInterval = intervalSeconds; + mConfig.setNattKeepaliveInterval(intervalSeconds); return this; } @@ -451,8 +411,8 @@ public final class IpSecTransform implements AutoCloseable { IpSecManager.SpiUnavailableException, IOException { //FIXME: argument validation here //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); - mConfig.mode = MODE_TRANSPORT; - mConfig.remoteAddress = remoteAddress; + mConfig.setMode(MODE_TRANSPORT); + mConfig.setRemoteAddress(remoteAddress.getHostAddress()); return new IpSecTransform(mContext, mConfig).activate(); } @@ -473,9 +433,9 @@ public final class IpSecTransform implements AutoCloseable { InetAddress localAddress, InetAddress remoteAddress) { //FIXME: argument validation here //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation"); - mConfig.localAddress = localAddress; - mConfig.remoteAddress = remoteAddress; - mConfig.mode = MODE_TUNNEL; + mConfig.setLocalAddress(localAddress.getHostAddress()); + mConfig.setRemoteAddress(remoteAddress.getHostAddress()); + mConfig.setMode(MODE_TUNNEL); return new IpSecTransform(mContext, mConfig); } @@ -489,14 +449,5 @@ public final class IpSecTransform implements AutoCloseable { mContext = context; mConfig = new IpSecConfig(); } - - /** - * Return an {@link IpSecConfig} object for testing purposes. - * @hide - */ - @VisibleForTesting - public IpSecConfig getIpSecConfig() { - return mConfig; - } } } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 3726f47093b9..7f4dee6ef2da 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1412,7 +1412,11 @@ public final class PowerManager { */ public void release(int flags) { synchronized (mToken) { - mInternalCount--; + if (mInternalCount > 0) { + // internal count must only be decreased if it is > 0 or state of + // the WakeLock object is broken. + mInternalCount--; + } if ((flags & RELEASE_FLAG_TIMEOUT) == 0) { mExternalCount--; } diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java index 045c83304097..1521e7e656a1 100644 --- a/core/java/android/service/autofill/AutofillService.java +++ b/core/java/android/service/autofill/AutofillService.java @@ -503,13 +503,19 @@ public abstract class AutofillService extends Service { @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback); /** - * Called when user requests service to save the fields of a screen. + * Called when the user requests the service to save the contents of a screen. * * <p>Service must call one of the {@link SaveCallback} methods (like * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)}) - * to notify the result of the request. + * to notify the Android System of the result of the request. + * + * <p>If the service could not handle the request right away—for example, because it must + * launch an activity asking the user to authenticate first or because the network is + * down—the service could keep the {@link SaveRequest request} and reuse it later, + * but the service must call {@link SaveCallback#onSuccess()} right away. * - * <p><b>Note:</b> To retrieve the actual value of the field, the service should call + * <p><b>Note:</b> To retrieve the actual value of fields input by the user, the service + * should call * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls * {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no * guarantee such method will return the most recent value of the field. diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java index 3a7013841729..7207f1df3ee5 100644 --- a/core/java/android/service/autofill/SaveCallback.java +++ b/core/java/android/service/autofill/SaveCallback.java @@ -34,9 +34,13 @@ public final class SaveCallback { /** * Notifies the Android System that an - * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully fulfilled + * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully handled * by the service. * + * <p>If the service could not handle the request right away—for example, because it must + * launch an activity asking the user to authenticate first or because the network is + * down—it should still call {@link #onSuccess()}. + * * @throws RuntimeException if an error occurred while calling the Android System. */ public void onSuccess() { @@ -51,9 +55,16 @@ public final class SaveCallback { /** * Notifies the Android System that an - * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} could not be fulfilled + * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} could not be handled * by the service. * + * <p>This method should only be called when the service could not handle the request right away + * and could not recover or retry it. If the service could retry or recover, it could keep + * the {@link SaveRequest} and call {@link #onSuccess()} instead. + * + * <p><b>Note:</b> The Android System displays an UI with the supplied error message; if + * you prefer to show your own message, call {@link #onSuccess()} instead. + * * @param message error message to be displayed to the user. * * @throws RuntimeException if an error occurred while calling the Android System. diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java index f27cc2eb0412..cfeb7fcead38 100644 --- a/core/java/android/service/settings/suggestions/Suggestion.java +++ b/core/java/android/service/settings/suggestions/Suggestion.java @@ -16,12 +16,17 @@ package android.service.settings.suggestions; +import android.annotation.IntDef; import android.annotation.SystemApi; import android.app.PendingIntent; +import android.graphics.drawable.Icon; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Data object that has information about a device suggestion. * @@ -30,9 +35,27 @@ import android.text.TextUtils; @SystemApi public final class Suggestion implements Parcelable { + /** + * @hide + */ + @IntDef(flag = true, value = { + FLAG_HAS_BUTTON, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Flags { + } + + /** + * Flag for suggestion type with a single button + */ + public static final int FLAG_HAS_BUTTON = 1 << 0; + private final String mId; private final CharSequence mTitle; private final CharSequence mSummary; + private final Icon mIcon; + @Flags + private final int mFlags; private final PendingIntent mPendingIntent; /** @@ -57,6 +80,22 @@ public final class Suggestion implements Parcelable { } /** + * Optional icon for this suggestion. + */ + public Icon getIcon() { + return mIcon; + } + + /** + * Optional flags for this suggestion. This will influence UI when rendering suggestion in + * different style. + */ + @Flags + public int getFlags() { + return mFlags; + } + + /** * The Intent to launch when the suggestion is activated. */ public PendingIntent getPendingIntent() { @@ -67,6 +106,8 @@ public final class Suggestion implements Parcelable { mId = builder.mId; mTitle = builder.mTitle; mSummary = builder.mSummary; + mIcon = builder.mIcon; + mFlags = builder.mFlags; mPendingIntent = builder.mPendingIntent; } @@ -74,6 +115,8 @@ public final class Suggestion implements Parcelable { mId = in.readString(); mTitle = in.readCharSequence(); mSummary = in.readCharSequence(); + mIcon = in.readParcelable(Icon.class.getClassLoader()); + mFlags = in.readInt(); mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader()); } @@ -99,6 +142,8 @@ public final class Suggestion implements Parcelable { dest.writeString(mId); dest.writeCharSequence(mTitle); dest.writeCharSequence(mSummary); + dest.writeParcelable(mIcon, flags); + dest.writeInt(mFlags); dest.writeParcelable(mPendingIntent, flags); } @@ -109,6 +154,9 @@ public final class Suggestion implements Parcelable { private final String mId; private CharSequence mTitle; private CharSequence mSummary; + private Icon mIcon; + @Flags + private int mFlags; private PendingIntent mPendingIntent; public Builder(String id) { @@ -135,6 +183,23 @@ public final class Suggestion implements Parcelable { } /** + * Sets icon for the suggestion. + */ + public Builder setIcon(Icon icon) { + mIcon = icon; + return this; + } + + /** + * Sets a UI type for this suggestion. This will influence UI when rendering suggestion in + * different style. + */ + public Builder setFlags(@Flags int flags) { + mFlags = flags; + return this; + } + + /** * Sets suggestion intent */ public Builder setPendingIntent(PendingIntent pendingIntent) { diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 5838f9590c9d..fc1d4873c9cb 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -50,6 +50,13 @@ public class FeatureFlagUtils { } /** + * Override feature flag to new state. + */ + public static void setEnabled(String feature, boolean enabled) { + SystemProperties.set(FFLAG_OVERRIDE_PREFIX + feature, enabled ? "true" : "false"); + } + + /** * Returns all feature flags in their raw form. */ public static Map<String, String> getAllFeatureFlags() { diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java index 9afa56dd57e4..49f8eea647b7 100644 --- a/core/java/android/util/proto/ProtoOutputStream.java +++ b/core/java/android/util/proto/ProtoOutputStream.java @@ -2375,6 +2375,9 @@ public final class ProtoOutputStream { if (countString == null) { countString = "fieldCount=" + fieldCount; } + if (countString.length() > 0) { + countString += " "; + } final long fieldType = fieldId & FIELD_TYPE_MASK; String typeString = getFieldTypeString(fieldType); @@ -2382,7 +2385,7 @@ public final class ProtoOutputStream { typeString = "fieldType=" + fieldType; } - return fieldCount + " " + typeString + " tag=" + ((int)fieldId) + return countString + typeString + " tag=" + ((int) fieldId) + " fieldId=0x" + Long.toHexString(fieldId); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d988d664027f..a3468638ba9a 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -366,7 +366,7 @@ public final class ViewRootImpl implements ViewParent, // These can be accessed by any thread, must be protected with a lock. // Surface can never be reassigned or cleared (use Surface.clear()). - final Surface mSurface = new Surface(); + public final Surface mSurface = new Surface(); boolean mAdded; boolean mAddedTouchMode; diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index 0ecd20da21c5..f671c34997d2 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -378,7 +378,7 @@ public abstract class ViewStructure { * * <p>Typically used when the view is a container for an HTML document. * - * @param domain URL representing the domain; only the host part will be used. + * @param domain RFC 2396-compliant URI representing the domain. */ public abstract void setWebDomain(@Nullable String domain); diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index e906a1fa7807..4fb2a99af575 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -937,10 +937,7 @@ public final class AutofillManager { } private AutofillClient getClientLocked() { - if (mContext instanceof AutofillClient) { - return (AutofillClient) mContext; - } - return null; + return mContext.getAutofillClient(); } /** @hide */ diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 36b24ffb8954..4773cda39b1d 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -492,10 +492,15 @@ public final class WebViewFactory { /** @hide */ public static IWebViewUpdateService getUpdateService() { if (isWebViewSupported()) { - return IWebViewUpdateService.Stub.asInterface( - ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME)); + return getUpdateServiceUnchecked(); } else { return null; } } + + /** @hide */ + static IWebViewUpdateService getUpdateServiceUnchecked() { + return IWebViewUpdateService.Stub.asInterface( + ServiceManager.getService(WEBVIEW_UPDATE_SERVICE_NAME)); + } } diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java index 6f9e8ece4b13..21316e7db9e4 100644 --- a/core/java/android/webkit/WebViewLibraryLoader.java +++ b/core/java/android/webkit/WebViewLibraryLoader.java @@ -80,7 +80,7 @@ class WebViewLibraryLoader { } finally { // We must do our best to always notify the update service, even if something fails. try { - WebViewFactory.getUpdateService().notifyRelroCreationCompleted(); + WebViewFactory.getUpdateServiceUnchecked().notifyRelroCreationCompleted(); } catch (RemoteException e) { Log.e(LOGTAG, "error notifying update service", e); } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 0f6172421fc8..afd11881e6d5 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -119,6 +119,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; import com.android.internal.util.Preconditions; import com.android.internal.widget.EditableInputConnection; +import com.android.internal.widget.Magnifier; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -138,6 +139,9 @@ import java.util.List; public class Editor { private static final String TAG = "Editor"; private static final boolean DEBUG_UNDO = false; + // Specifies whether to use or not the magnifier when pressing the insertion or selection + // handles. + private static final boolean FLAG_USE_MAGNIFIER = true; static final int BLINK = 500; private static final int DRAG_SHADOW_MAX_TEXT_LENGTH = 20; @@ -161,6 +165,17 @@ public class Editor { private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 11; private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100; + private static final float MAGNIFIER_ZOOM = 1.5f; + @IntDef({MagnifierHandleTrigger.SELECTION_START, + MagnifierHandleTrigger.SELECTION_END, + MagnifierHandleTrigger.INSERTION}) + @Retention(RetentionPolicy.SOURCE) + private @interface MagnifierHandleTrigger { + int INSERTION = 0; + int SELECTION_START = 1; + int SELECTION_END = 2; + } + // Each Editor manages its own undo stack. private final UndoManager mUndoManager = new UndoManager(); private UndoOwner mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this); @@ -179,6 +194,8 @@ public class Editor { private final boolean mHapticTextHandleEnabled; + private final Magnifier mMagnifier; + // Used to highlight a word when it is corrected by the IME private CorrectionHighlighter mCorrectionHighlighter; @@ -250,7 +267,7 @@ public class Editor { SuggestionRangeSpan mSuggestionRangeSpan; private Runnable mShowSuggestionRunnable; - Drawable mCursorDrawable = null; + Drawable mDrawableForCursor = null; private Drawable mSelectHandleLeft; private Drawable mSelectHandleRight; @@ -325,6 +342,8 @@ public class Editor { mProcessTextIntentActionsHandler = new ProcessTextIntentActionsHandler(this); mHapticTextHandleEnabled = mTextView.getContext().getResources().getBoolean( com.android.internal.R.bool.config_enableHapticTextHandle); + + mMagnifier = FLAG_USE_MAGNIFIER ? new Magnifier(mTextView) : null; } ParcelableParcel saveInstanceState() { @@ -1678,7 +1697,7 @@ public class Editor { mCorrectionHighlighter.draw(canvas, cursorOffsetVertical); } - if (highlight != null && selectionStart == selectionEnd && mCursorDrawable != null) { + if (highlight != null && selectionStart == selectionEnd && mDrawableForCursor != null) { drawCursor(canvas, cursorOffsetVertical); // Rely on the drawable entirely, do not draw the cursor line. // Has to be done after the IMM related code above which relies on the highlight. @@ -1873,8 +1892,8 @@ public class Editor { private void drawCursor(Canvas canvas, int cursorOffsetVertical) { final boolean translate = cursorOffsetVertical != 0; if (translate) canvas.translate(0, cursorOffsetVertical); - if (mCursorDrawable != null) { - mCursorDrawable.draw(canvas); + if (mDrawableForCursor != null) { + mDrawableForCursor.draw(canvas); } if (translate) canvas.translate(0, -cursorOffsetVertical); } @@ -1933,7 +1952,7 @@ public class Editor { void updateCursorPosition() { if (mTextView.mCursorDrawableRes == 0) { - mCursorDrawable = null; + mDrawableForCursor = null; return; } @@ -2314,17 +2333,17 @@ public class Editor { @VisibleForTesting @Nullable public Drawable getCursorDrawable() { - return mCursorDrawable; + return mDrawableForCursor; } private void updateCursorPosition(int top, int bottom, float horizontal) { - if (mCursorDrawable == null) { - mCursorDrawable = mTextView.getContext().getDrawable( + if (mDrawableForCursor == null) { + mDrawableForCursor = mTextView.getContext().getDrawable( mTextView.mCursorDrawableRes); } - final int left = clampHorizontalPosition(mCursorDrawable, horizontal); - final int width = mCursorDrawable.getIntrinsicWidth(); - mCursorDrawable.setBounds(left, top - mTempRect.top, left + width, + final int left = clampHorizontalPosition(mDrawableForCursor, horizontal); + final int width = mDrawableForCursor.getIntrinsicWidth(); + mDrawableForCursor.setBounds(left, top - mTempRect.top, left + width, bottom + mTempRect.bottom); } @@ -4353,6 +4372,9 @@ public class Editor { protected abstract void updatePosition(float x, float y, boolean fromTouchScreen); + @MagnifierHandleTrigger + protected abstract int getMagnifierHandleTrigger(); + protected boolean isAtRtlRun(@NonNull Layout layout, int offset) { return layout.isRtlCharAt(offset); } @@ -4490,6 +4512,53 @@ public class Editor { return 0; } + protected final void showMagnifier() { + if (mMagnifier == null) { + return; + } + + final int trigger = getMagnifierHandleTrigger(); + final int offset; + switch (trigger) { + case MagnifierHandleTrigger.INSERTION: // Fall through. + case MagnifierHandleTrigger.SELECTION_START: + offset = mTextView.getSelectionStart(); + break; + case MagnifierHandleTrigger.SELECTION_END: + offset = mTextView.getSelectionEnd(); + break; + default: + offset = -1; + break; + } + + if (offset == -1) { + dismissMagnifier(); + } + + final Layout layout = mTextView.getLayout(); + final int lineNumber = layout.getLineForOffset(offset); + // Horizontally snap to character offset. + final float xPosInView = getHorizontal(mTextView.getLayout(), offset); + // Vertically snap to middle of current line. + final float yPosInView = (mTextView.getLayout().getLineTop(lineNumber) + + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f; + final int[] coordinatesOnScreen = new int[2]; + mTextView.getLocationOnScreen(coordinatesOnScreen); + final float centerXOnScreen = xPosInView + mTextView.getTotalPaddingLeft() + - mTextView.getScrollX() + coordinatesOnScreen[0]; + final float centerYOnScreen = yPosInView + mTextView.getTotalPaddingTop() + - mTextView.getScrollY() + coordinatesOnScreen[1]; + + mMagnifier.show(centerXOnScreen, centerYOnScreen, MAGNIFIER_ZOOM); + } + + protected final void dismissMagnifier() { + if (mMagnifier != null) { + mMagnifier.dismiss(); + } + } + @Override public boolean onTouchEvent(MotionEvent ev) { updateFloatingToolbarVisibility(ev); @@ -4542,10 +4611,7 @@ public class Editor { case MotionEvent.ACTION_UP: filterOnTouchUp(ev.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)); - mIsDragging = false; - updateDrawable(); - break; - + // Fall through. case MotionEvent.ACTION_CANCEL: mIsDragging = false; updateDrawable(); @@ -4646,9 +4712,9 @@ public class Editor { @Override protected int getCursorOffset() { int offset = super.getCursorOffset(); - if (mCursorDrawable != null) { - mCursorDrawable.getPadding(mTempRect); - offset += (mCursorDrawable.getIntrinsicWidth() + if (mDrawableForCursor != null) { + mDrawableForCursor.getPadding(mTempRect); + offset += (mDrawableForCursor.getIntrinsicWidth() - mTempRect.left - mTempRect.right) / 2; } return offset; @@ -4656,9 +4722,9 @@ public class Editor { @Override int getCursorHorizontalPosition(Layout layout, int offset) { - if (mCursorDrawable != null) { + if (mDrawableForCursor != null) { final float horizontal = getHorizontal(layout, offset); - return clampHorizontalPosition(mCursorDrawable, horizontal) + mTempRect.left; + return clampHorizontalPosition(mDrawableForCursor, horizontal) + mTempRect.left; } return super.getCursorHorizontalPosition(layout, offset); } @@ -4671,6 +4737,11 @@ public class Editor { case MotionEvent.ACTION_DOWN: mDownPositionX = ev.getRawX(); mDownPositionY = ev.getRawY(); + showMagnifier(); + break; + + case MotionEvent.ACTION_MOVE: + showMagnifier(); break; case MotionEvent.ACTION_UP: @@ -4696,11 +4767,10 @@ public class Editor { mTextActionMode.invalidateContentRect(); } } - hideAfterDelay(); - break; - + // Fall through. case MotionEvent.ACTION_CANCEL: hideAfterDelay(); + dismissMagnifier(); break; default: @@ -4751,6 +4821,12 @@ public class Editor { super.onDetached(); removeHiderCallback(); } + + @Override + @MagnifierHandleTrigger + protected int getMagnifierHandleTrigger() { + return MagnifierHandleTrigger.INSERTION; + } } @Retention(RetentionPolicy.SOURCE) @@ -5009,12 +5085,26 @@ public class Editor { @Override public boolean onTouchEvent(MotionEvent event) { boolean superResult = super.onTouchEvent(event); - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - // Reset the touch word offset and x value when the user - // re-engages the handle. - mTouchWordDelta = 0.0f; - mPrevX = UNSET_X_VALUE; + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // Reset the touch word offset and x value when the user + // re-engages the handle. + mTouchWordDelta = 0.0f; + mPrevX = UNSET_X_VALUE; + showMagnifier(); + break; + + case MotionEvent.ACTION_MOVE: + showMagnifier(); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + dismissMagnifier(); + break; } + return superResult; } @@ -5110,6 +5200,13 @@ public class Editor { return isRtlChar == isRtlParagraph ? primaryOffset : secondaryOffset; } } + + @MagnifierHandleTrigger + protected int getMagnifierHandleTrigger() { + return isStartHandle() + ? MagnifierHandleTrigger.SELECTION_START + : MagnifierHandleTrigger.SELECTION_END; + } } private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 791a8fae418f..24ae03c37b11 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -6281,7 +6281,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int horizontalPadding = getCompoundPaddingLeft(); final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); - if (mEditor.mCursorDrawable == null) { + if (mEditor.mDrawableForCursor == null) { synchronized (TEMP_RECTF) { /* * The reason for this concern about the thickness of the @@ -6308,7 +6308,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener (int) Math.ceil(verticalPadding + TEMP_RECTF.bottom + thick)); } } else { - final Rect bounds = mEditor.mCursorDrawable.getBounds(); + final Rect bounds = mEditor.mDrawableForCursor.getBounds(); invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding, bounds.right + horizontalPadding, bounds.bottom + verticalPadding); } @@ -6360,8 +6360,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int bottom = mLayout.getLineBottom(lineEnd); // mEditor can be null in case selection is set programmatically. - if (invalidateCursor && mEditor != null && mEditor.mCursorDrawable != null) { - final Rect bounds = mEditor.mCursorDrawable.getBounds(); + if (invalidateCursor && mEditor != null && mEditor.mDrawableForCursor != null) { + final Rect bounds = mEditor.mDrawableForCursor.getBounds(); top = Math.min(top, bounds.top); bottom = Math.max(bottom, bounds.bottom); } diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java index 42c051d432b9..7519fce467d9 100644 --- a/core/java/com/android/internal/app/procstats/ProcessState.java +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -1181,7 +1181,6 @@ public final class ProcessState { proto.write(ProcessStatsProto.UID, uid); if (mNumExcessiveCpu > 0 || mNumCachedKill > 0 ) { final long killToken = proto.start(ProcessStatsProto.KILL); - proto.write(ProcessStatsProto.Kill.WAKES, mNumExcessiveWake); proto.write(ProcessStatsProto.Kill.CPU, mNumExcessiveCpu); proto.write(ProcessStatsProto.Kill.CACHED, mNumCachedKill); ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.Kill.CACHED_PSS, diff --git a/core/java/com/android/internal/widget/Magnifier.java b/core/java/com/android/internal/widget/Magnifier.java new file mode 100644 index 000000000000..86e7b38a8bc8 --- /dev/null +++ b/core/java/com/android/internal/widget/Magnifier.java @@ -0,0 +1,184 @@ +/* + * 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.internal.widget; + +import android.annotation.FloatRange; +import android.annotation.NonNull; +import android.annotation.UiThread; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Handler; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.PixelCopy; +import android.view.View; +import android.view.ViewRootImpl; +import android.widget.ImageView; +import android.widget.PopupWindow; + +import com.android.internal.R; +import com.android.internal.util.Preconditions; + +/** + * Android magnifier widget. Can be used by any view which is attached to window. + */ +public final class Magnifier { + private static final String LOG_TAG = "magnifier"; + // The view for which this magnifier is attached. + private final View mView; + // The window containing the magnifier. + private final PopupWindow mWindow; + // The center coordinates of the window containing the magnifier. + private final Point mWindowCoords = new Point(); + // The width of the window containing the magnifier. + private final int mWindowWidth; + // The height of the window containing the magnifier. + private final int mWindowHeight; + // The bitmap used to display the contents of the magnifier. + private final Bitmap mBitmap; + // The center coordinates of the content that is to be magnified. + private final Point mCenterZoomCoords = new Point(); + // The callback of the pixel copy request will be invoked on this Handler when + // the copy is finished. + private final Handler mPixelCopyHandler = Handler.getMain(); + + /** + * Initializes a magnifier. + * + * @param view the view for which this magnifier is attached + */ + @UiThread + public Magnifier(@NonNull View view) { + mView = Preconditions.checkNotNull(view); + final Context context = mView.getContext(); + final View content = LayoutInflater.from(context).inflate(R.layout.magnifier, null); + mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width); + mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height); + final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation); + + mWindow = new PopupWindow(context); + mWindow.setContentView(content); + mWindow.setWidth(mWindowWidth); + mWindow.setHeight(mWindowHeight); + mWindow.setElevation(elevation); + mWindow.setTouchable(false); + mWindow.setBackgroundDrawable(null); + + mBitmap = Bitmap.createBitmap(mWindowWidth, mWindowHeight, Bitmap.Config.ARGB_8888); + getImageView().setImageBitmap(mBitmap); + } + + /** + * Shows the magnifier on the screen. + * + * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source + * @param centerYOnScreen vertical coordinate of the center point of the magnifier source + * @param scale the scale at which the magnifier zooms on the source content + */ + public void show(@FloatRange(from=0) float centerXOnScreen, + @FloatRange(from=0) float centerYOnScreen, + @FloatRange(from=1, to=10) float scale) { + maybeResizeBitmap(scale); + configureCoordinates(centerXOnScreen, centerYOnScreen); + performPixelCopy(); + + if (mWindow.isShowing()) { + mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(), + mWindow.getHeight()); + } else { + mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY, + mWindowCoords.x, mWindowCoords.y); + } + } + + /** + * Dismisses the magnifier from the screen. + */ + public void dismiss() { + mWindow.dismiss(); + } + + /** + * @return the height of the magnifier window. + */ + public int getHeight() { + return mWindowHeight; + } + + /** + * @return the width of the magnifier window. + */ + public int getWidth() { + return mWindowWidth; + } + + private void maybeResizeBitmap(float scale) { + final int bitmapWidth = (int) (mWindowWidth / scale); + final int bitmapHeight = (int) (mWindowHeight / scale); + if (mBitmap.getWidth() != bitmapWidth || mBitmap.getHeight() != bitmapHeight) { + mBitmap.reconfigure(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); + getImageView().setImageBitmap(mBitmap); + } + } + + private void configureCoordinates(float posXOnScreen, float posYOnScreen) { + mCenterZoomCoords.x = (int) posXOnScreen; + mCenterZoomCoords.y = (int) posYOnScreen; + + final int verticalMagnifierOffset = mView.getContext().getResources().getDimensionPixelSize( + R.dimen.magnifier_offset); + final int availableTopSpace = (mCenterZoomCoords.y - mWindowHeight / 2) + - verticalMagnifierOffset - (mBitmap.getHeight() / 2); + + mWindowCoords.x = mCenterZoomCoords.x - mWindowWidth / 2; + mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 + + verticalMagnifierOffset * (availableTopSpace > 0 ? -1 : 1); + } + + private void performPixelCopy() { + int startX = mCenterZoomCoords.x - mBitmap.getWidth() / 2; + // Clamp startX value to avoid distorting the rendering of the magnifier content. + if (startX < 0) { + startX = 0; + } else if (startX + mBitmap.getWidth() > mView.getWidth()) { + startX = mView.getWidth() - mBitmap.getWidth(); + } + + final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2; + final ViewRootImpl viewRootImpl = mView.getViewRootImpl(); + + if (viewRootImpl != null && viewRootImpl.mSurface != null + && viewRootImpl.mSurface.isValid()) { + PixelCopy.request( + viewRootImpl.mSurface, + new Rect(startX, startY, startX + mBitmap.getWidth(), + startY + mBitmap.getHeight()), + mBitmap, + result -> getImageView().invalidate(), + mPixelCopyHandler); + } else { + Log.d(LOG_TAG, "Could not perform PixelCopy request"); + } + } + + private ImageView getImageView() { + return mWindow.getContentView().findViewById(R.id.magnifier_image); + } +} diff --git a/core/proto/android/app/notification_channel.proto b/core/proto/android/app/notification_channel.proto new file mode 100644 index 000000000000..bbc195644583 --- /dev/null +++ b/core/proto/android/app/notification_channel.proto @@ -0,0 +1,54 @@ +/* + * 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. + */ + +syntax = "proto3"; + +option java_package = "android.app"; +option java_multiple_files = true; + +package android.app; + +import "frameworks/base/core/proto/android/media/audioattributes.proto"; + +/** + * An android.app.NotificationChannel object. + */ +message NotificationChannelProto { + string id = 1; + string name = 2; + string description = 3; + int32 importance = 4; + bool can_bypass_dnd = 5; + // Default is VISIBILITY_NO_OVERRIDE (-1000). + int32 lockscreen_visibility = 6; + string sound = 7; + bool use_lights = 8; + // Default is 0. + int32 light_color = 9; + repeated int64 vibration = 10; + // Bitwise representation of fields that have been changed by the user, + // preventing the app from making changes to these fields. + int32 user_locked_fields = 11; + bool is_vibration_enabled = 12; + // Default is true. + bool show_badge = 13; + // Default is false. + bool is_deleted = 14; + string group = 15; + android.media.AudioAttributesProto audio_attributes = 16; + // If this is a blockable system notification channel. + bool is_blockable_system = 17; +} diff --git a/core/proto/android/app/notification_channel_group.proto b/core/proto/android/app/notification_channel_group.proto new file mode 100644 index 000000000000..9cb456f33947 --- /dev/null +++ b/core/proto/android/app/notification_channel_group.proto @@ -0,0 +1,35 @@ +/* + * 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. + */ + +syntax = "proto3"; + +option java_package = "android.app"; +option java_multiple_files = true; + +package android.app; + +import "frameworks/base/core/proto/android/app/notification_channel.proto"; + +/** + * An android.app.NotificationChannelGroup object. + */ +message NotificationChannelGroupProto { + string id = 1; + string name = 2; + string description = 3; + bool is_blocked = 4; + repeated android.app.NotificationChannelProto channels = 5; +} diff --git a/core/proto/android/media/audioattributes.proto b/core/proto/android/media/audioattributes.proto new file mode 100644 index 000000000000..3aa2792c39b3 --- /dev/null +++ b/core/proto/android/media/audioattributes.proto @@ -0,0 +1,100 @@ +/* + * 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. + */ + +syntax = "proto3"; + +option java_package = "android.media"; +option java_multiple_files = true; + +package android.media; + +/** + * An android.media.AudioAttributes object. + */ +message AudioAttributesProto { + Usage usage = 1; + ContentType content_type = 2; + // Bit representation of set flags. + int32 flags = 3; + repeated string tags = 4; +} + +enum ContentType { + // Content type value to use when the content type is unknown, or other than + // the ones defined. + CONTENT_TYPE_UNKNOWN = 0; + // Content type value to use when the content type is speech. + SPEECH = 1; + // Content type value to use when the content type is music. + MUSIC = 2; + // Content type value to use when the content type is a soundtrack, + // typically accompanying a movie or TV program. + MOVIE = 3; + // Content type value to use when the content type is a sound used to + // accompany a user action, such as a beep or sound effect expressing a key + // click, or event, such as the type of a sound for a bonus being received + // in a game. These sounds are mostly synthesized or short Foley sounds. + SONIFICATION = 4; +} + +enum Usage { + // Usage value to use when the usage is unknown. + USAGE_UNKNOWN = 0; + // Usage value to use when the usage is media, such as music, or movie + // soundtracks. + MEDIA = 1; + // Usage value to use when the usage is voice communications, such as + // telephony or VoIP. + VOICE_COMMUNICATION = 2; + // Usage value to use when the usage is in-call signalling, such as with a + // "busy" beep, or DTMF tones. + VOICE_COMMUNICATION_SIGNALLING = 3; + // Usage value to use when the usage is an alarm (e.g. wake-up alarm). + ALARM = 4; + // Usage value to use when the usage is notification. Other notification + // usages are for more specialized uses. + NOTIFICATION = 5; + // Usage value to use when the usage is telephony ringtone. + NOTIFICATION_RINGTONE = 6; + // Usage value to use when the usage is a request to enter/end a + // communication, such as a VoIP communication or video-conference. + NOTIFICATION_COMMUNICATION_REQUEST = 7; + // Usage value to use when the usage is notification for an "instant" + // communication such as a chat, or SMS. + NOTIFICATION_COMMUNICATION_INSTANT = 8; + // Usage value to use when the usage is notification for a non-immediate + // type of communication such as e-mail. + NOTIFICATION_COMMUNICATION_DELAYED = 9; + // Usage value to use when the usage is to attract the user's attention, + // such as a reminder or low battery warning. + NOTIFICATION_EVENT = 10; + // Usage value to use when the usage is for accessibility, such as with a + // screen reader. + ASSISTANCE_ACCESSIBILITY = 11; + // Usage value to use when the usage is driving or navigation directions. + ASSISTANCE_NAVIGATION_GUIDANCE = 12; + // Usage value to use when the usage is sonification, such as with user + // interface sounds. + ASSISTANCE_SONIFICATION = 13; + // Usage value to use when the usage is for game audio. + GAME = 14; + // Usage value to use when feeding audio to the platform and replacing + // "traditional" audio source, such as audio capture devices. + VIRTUAL_SOURCE = 15; + // Usage value to use for audio responses to user queries, audio + // instructions or help utterances. + ASSISTANT = 16; +} diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto index 05afe525c1eb..d8cb1a775595 100644 --- a/core/proto/android/service/notification.proto +++ b/core/proto/android/service/notification.proto @@ -21,6 +21,8 @@ package android.service.notification; option java_multiple_files = true; option java_outer_classname = "NotificationServiceProto"; +import "frameworks/base/core/proto/android/app/notification_channel.proto"; +import "frameworks/base/core/proto/android/app/notification_channel_group.proto"; import "frameworks/base/core/proto/android/app/notificationmanager.proto"; import "frameworks/base/core/proto/android/content/component_name.proto"; @@ -38,6 +40,8 @@ message NotificationServiceDumpProto { ManagedServicesProto notification_assistants = 6; ManagedServicesProto condition_providers = 7; + + RankingHelperProto ranking_config = 8; } message NotificationRecordProto { @@ -86,6 +90,28 @@ message ManagedServicesProto { repeated android.content.ComponentNameProto snoozed = 5; } +message RankingHelperProto { + repeated string notification_signal_extractors = 1; + + message RecordProto { + string package = 1; + // Default value is UNKNOWN_UID = USER_NULL = -10000. + int32 uid = 2; + // Default is IMPORTANCE_UNSPECIFIED (-1000). + int32 importance = 3; + // Default is PRIORITY_DEFAULT (0). + int32 priority = 4; + // Default is VISIBILITY_NO_OVERRIDE (-1000). + int32 visibility = 5; + // Default is true. + bool show_badge = 6; + repeated android.app.NotificationChannelProto channels = 7; + repeated android.app.NotificationChannelGroupProto channel_groups = 8; + } + repeated RecordProto records = 2; + repeated RecordProto records_restored_without_uid = 3; +} + enum State { ENQUEUED = 0; diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto index 885150e91f81..322b212cd5ba 100644 --- a/core/proto/android/service/procstats.proto +++ b/core/proto/android/service/procstats.proto @@ -88,17 +88,14 @@ message ProcessStatsProto { // Information about how often kills occurred message Kill { - // Count of excessive wakes kills - int32 wakes = 1; - // Count of excessive CPU kills - int32 cpu = 2; + int32 cpu = 1; // Count of kills when cached - int32 cached = 3; + int32 cached = 2; // PSS stats during cached kill - android.util.AggStats cached_pss = 4; + android.util.AggStats cached_pss = 3; } Kill kill = 3; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4159c21e837b..a9fda8ed4cd2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -204,8 +204,8 @@ android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" /> <protected-broadcast android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" /> - <protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" /> <protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" /> diff --git a/core/res/res/layout/magnifier.xml b/core/res/res/layout/magnifier.xml new file mode 100644 index 000000000000..181e5e54bb00 --- /dev/null +++ b/core/res/res/layout/magnifier.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="?android:attr/floatingToolbarPopupBackgroundDrawable"> + <ImageView + android:id="@+id/magnifier_image" + android:layout_width="match_parent" + android:layout_height="match_parent" /> +</LinearLayout> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 16c857894d05..b3d00535ecf8 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -520,6 +520,12 @@ <dimen name="floating_toolbar_vertical_margin">8dp</dimen> <dimen name="content_rect_bottom_clip_allowance">20dp</dimen> + <!-- Magnifier dimensions --> + <dimen name="magnifier_width">200dp</dimen> + <dimen name="magnifier_height">48dp</dimen> + <dimen name="magnifier_elevation">2dp</dimen> + <dimen name="magnifier_offset">42dp</dimen> + <dimen name="chooser_grid_padding">0dp</dimen> <!-- Spacing around the background change frome service to non-service --> <dimen name="chooser_service_spacing">8dp</dimen> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6807e1328f2e..085f8dd6488d 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -340,18 +340,19 @@ <!-- Work profile deleted notification--> <skip /> <!-- Shows up in the notification's title when the system deletes the work profile. [CHAR LIMIT=NONE] --> <string name="work_profile_deleted">Work profile deleted</string> - <!-- Content text for a notification. The Title of the notification is "work_profile_deleted", - i.e. "Work profile deleted". This says that the profile is deleted by the system as a result of - the current profile owner gone missing. [CHAR LIMIT=100]--> + <!-- Content text for a notification. The Title of the notification is "Work profile deleted". + This says that the profile is deleted by the system as a result of the current profile owner gone missing. [CHAR LIMIT=100]--> <string name="work_profile_deleted_description">Work profile deleted due to missing admin app</string> - <!-- Content text for an expanded notification. The Title of the notification is "work_profile_deleted", - i.e. "Work profile deleted". This further explains that the profile is deleted by the system - as a result of the current profile admin gone missing. [CHAR LIMIT=NONE]--> + <!-- Content text for an expanded notification. The Title of the notification is "Work profile deleted". + This further explains that the profile is deleted by the system as a result of the current profile admin gone missing. [CHAR LIMIT=NONE]--> <string name="work_profile_deleted_details">The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance.</string> - <!-- Content text for a notification. The Title of the notification is "work_profile_deleted", + <!-- Content text for a notification. The Title of the notification is "Work profile deleted", This indicates that a work profile has been deleted. [CHAR LIMIT=NONE]--> <string name="work_profile_deleted_description_dpm_wipe">Your work profile is no longer available on this device</string> + <!-- Content text for a notification. The Title of the notification is "Work profile deleted", + This indicates that a work profile has been deleted because the maximum failed password attempts as been reached. [CHAR LIMIT=NONE]--> + <string name="work_profile_deleted_reason_maximum_password_failure">Too many password attempts</string> <!-- Content title for a notification. This notification indicates that the device is managed and network logging was activated by a device owner. [CHAR LIMIT=NONE]--> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0732f0d0bcfd..a5dbe263003d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1155,6 +1155,7 @@ <java-symbol type="string" name="work_profile_deleted_description" /> <java-symbol type="string" name="work_profile_deleted_details" /> <java-symbol type="string" name="work_profile_deleted_description_dpm_wipe" /> + <java-symbol type="string" name="work_profile_deleted_reason_maximum_password_failure" /> <java-symbol type="string" name="network_logging_notification_title" /> <java-symbol type="string" name="network_logging_notification_text" /> <java-symbol type="string" name="factory_reset_warning" /> @@ -2474,6 +2475,14 @@ <java-symbol type="drawable" name="ft_avd_tooverflow_animation" /> <java-symbol type="attr" name="floatingToolbarDividerColor" /> + <!-- Magnifier --> + <java-symbol type="id" name="magnifier_image" /> + <java-symbol type="layout" name="magnifier" /> + <java-symbol type="dimen" name="magnifier_width" /> + <java-symbol type="dimen" name="magnifier_height" /> + <java-symbol type="dimen" name="magnifier_elevation" /> + <java-symbol type="dimen" name="magnifier_offset" /> + <java-symbol type="string" name="date_picker_prev_month_button" /> <java-symbol type="string" name="date_picker_next_month_button" /> <java-symbol type="layout" name="date_picker_month_item_material" /> diff --git a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java index 5548e48ffdda..b0ec55d946e2 100644 --- a/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java +++ b/core/tests/coretests/src/android/service/settings/suggestions/SuggestionTest.java @@ -21,6 +21,8 @@ import static com.google.common.truth.Truth.assertThat; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.drawable.Icon; import android.os.Parcel; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; @@ -36,6 +38,8 @@ public class SuggestionTest { private static final String TEST_ID = "id"; private static final String TEST_TITLE = "title"; private static final String TEST_SUMMARY = "summary"; + + private Icon mIcon; private PendingIntent mTestIntent; @@ -44,6 +48,7 @@ public class SuggestionTest { final Context context = InstrumentationRegistry.getContext(); mTestIntent = PendingIntent.getActivity(context, 0 /* requestCode */, new Intent(), 0 /* flags */); + mIcon = Icon.createWithBitmap(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)); } @Test @@ -51,12 +56,15 @@ public class SuggestionTest { final Suggestion suggestion = new Suggestion.Builder(TEST_ID) .setTitle(TEST_TITLE) .setSummary(TEST_SUMMARY) + .setIcon(mIcon) .setPendingIntent(mTestIntent) .build(); assertThat(suggestion.getId()).isEqualTo(TEST_ID); assertThat(suggestion.getTitle()).isEqualTo(TEST_TITLE); assertThat(suggestion.getSummary()).isEqualTo(TEST_SUMMARY); + assertThat(suggestion.getIcon()).isEqualTo(mIcon); + assertThat(suggestion.getFlags()).isEqualTo(0); assertThat(suggestion.getPendingIntent()).isEqualTo(mTestIntent); } @@ -66,6 +74,7 @@ public class SuggestionTest { .setTitle(TEST_TITLE) .setSummary(TEST_SUMMARY) .setPendingIntent(mTestIntent) + .setIcon(mIcon) .build(); } @@ -75,6 +84,8 @@ public class SuggestionTest { final Suggestion oldSuggestion = new Suggestion.Builder(TEST_ID) .setTitle(TEST_TITLE) .setSummary(TEST_SUMMARY) + .setIcon(mIcon) + .setFlags(Suggestion.FLAG_HAS_BUTTON) .setPendingIntent(mTestIntent) .build(); @@ -85,6 +96,9 @@ public class SuggestionTest { assertThat(newSuggestion.getId()).isEqualTo(TEST_ID); assertThat(newSuggestion.getTitle()).isEqualTo(TEST_TITLE); assertThat(newSuggestion.getSummary()).isEqualTo(TEST_SUMMARY); + assertThat(newSuggestion.getIcon().toString()).isEqualTo(mIcon.toString()); + assertThat(newSuggestion.getFlags()) + .isEqualTo(Suggestion.FLAG_HAS_BUTTON); assertThat(newSuggestion.getPendingIntent()).isEqualTo(mTestIntent); } } diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java index 8fee1d11a954..ebfa45c9bbc6 100644 --- a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java +++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java @@ -16,6 +16,7 @@ package android.util; +import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -65,6 +66,18 @@ public class FeatureFlagUtilsTest { } @Test + public void testSetEnabled_shouldSetOverrideFlag() { + assertFalse(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME)); + + FeatureFlagUtils.setEnabled(TEST_FEATURE_NAME, true); + + assertEquals(SystemProperties.get(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, null), + ""); + assertTrue(Boolean.parseBoolean(SystemProperties.get( + FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + TEST_FEATURE_NAME, ""))); + } + + @Test public void testGetFlag_notSet_shouldReturnFalse() { assertFalse(FeatureFlagUtils.isEnabled(TEST_FEATURE_NAME)); } diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java index 13c3ac468882..efaf2244e26c 100644 --- a/media/java/android/media/AmrInputStream.java +++ b/media/java/android/media/AmrInputStream.java @@ -30,7 +30,7 @@ import android.util.Log; */ public final class AmrInputStream extends InputStream { private final static String TAG = "AmrInputStream"; - + // frame is 20 msec at 8.000 khz private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000; @@ -140,19 +140,15 @@ public final class AmrInputStream extends InputStream { } } - // now read encoded data from the encoder (blocking, since we just filled up the - // encoder's input with data it should be able to output at least one buffer) - while (true) { - int index = mCodec.dequeueOutputBuffer(mInfo, -1); - if (index >= 0) { - mBufIn = mInfo.size; - ByteBuffer out = mCodec.getOutputBuffer(index); - out.get(mBuf, 0 /* offset */, mBufIn /* length */); - mCodec.releaseOutputBuffer(index, false /* render */); - if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { - mSawOutputEOS = true; - } - break; + // now read encoded data from the encoder + int index = mCodec.dequeueOutputBuffer(mInfo, 0); + if (index >= 0) { + mBufIn = mInfo.size; + ByteBuffer out = mCodec.getOutputBuffer(index); + out.get(mBuf, 0 /* offset */, mBufIn /* length */); + mCodec.releaseOutputBuffer(index, false /* render */); + if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { + mSawOutputEOS = true; } } } diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 3b9a5de00707..26ead3d1b54d 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -19,12 +19,14 @@ package android.media; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.media.AudioAttributesProto; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; +import android.util.proto.ProtoOutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -177,7 +179,7 @@ public final class AudioAttributes implements Parcelable { /** * IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES - * if applicable. + * if applicable, as well as audioattributes.proto. */ /** @@ -850,6 +852,21 @@ public final class AudioAttributes implements Parcelable { } /** @hide */ + public void toProto(ProtoOutputStream proto) { + proto.write(AudioAttributesProto.USAGE, mUsage); + proto.write(AudioAttributesProto.CONTENT_TYPE, mContentType); + proto.write(AudioAttributesProto.FLAGS, mFlags); + // mFormattedTags is never null due to assignment in Builder or unmarshalling. + for (String t : mFormattedTags.split(";")) { + t = t.trim(); + if (t != "") { + proto.write(AudioAttributesProto.TAGS, t); + } + } + // TODO: is the data in mBundle useful for debugging? + } + + /** @hide */ public String usageToString() { return usageToString(mUsage); } diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index 4808d7a5aa6a..09449a183d96 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -127,8 +127,9 @@ public abstract class PlayerBase { Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e); } synchronized (mLock) { + boolean attributesChanged = (mAttributes != attr); mAttributes = attr; - updateAppOpsPlayAudio_sync(); + updateAppOpsPlayAudio_sync(attributesChanged); } } @@ -200,16 +201,13 @@ public abstract class PlayerBase { } void baseSetVolume(float leftVolume, float rightVolume) { - final boolean hasAppOpsPlayAudio; + final boolean isRestricted; synchronized (mLock) { mLeftVolume = leftVolume; mRightVolume = rightVolume; - hasAppOpsPlayAudio = mHasAppOpsPlayAudio; - if (isRestricted_sync()) { - return; - } + isRestricted = isRestricted_sync(); } - playerSetVolume(!hasAppOpsPlayAudio/*muting*/, + playerSetVolume(isRestricted/*muting*/, leftVolume * mPanMultiplierL, rightVolume * mPanMultiplierR); } @@ -250,7 +248,7 @@ public abstract class PlayerBase { private void updateAppOpsPlayAudio() { synchronized (mLock) { - updateAppOpsPlayAudio_sync(); + updateAppOpsPlayAudio_sync(false); } } @@ -258,7 +256,7 @@ public abstract class PlayerBase { * To be called whenever a condition that might affect audibility of this player is updated. * Must be called synchronized on mLock. */ - void updateAppOpsPlayAudio_sync() { + void updateAppOpsPlayAudio_sync(boolean attributesChanged) { boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio; try { int mode = AppOpsManager.MODE_IGNORED; @@ -275,9 +273,10 @@ public abstract class PlayerBase { // AppsOps alters a player's volume; when the restriction changes, reflect it on the actual // volume used by the player try { - if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio) { + if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio || + attributesChanged) { getService().playerHasOpPlayAudio(mPlayerIId, mHasAppOpsPlayAudio); - if (mHasAppOpsPlayAudio) { + if (!isRestricted_sync()) { if (DEBUG_APP_OPS) { Log.v(TAG, "updateAppOpsPlayAudio: unmuting player, vol=" + mLeftVolume + "/" + mRightVolume); diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java new file mode 100644 index 000000000000..ff7536adb808 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java @@ -0,0 +1,64 @@ +/* + * 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.settingslib.deviceinfo; + +import android.content.Context; +import android.os.Build; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.text.TextUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settingslib.core.AbstractPreferenceController; + +/** + * Preference controller for displaying device serial number. Wraps {@link Build#getSerial()}. + */ +public class AbstractSerialNumberPreferenceController extends AbstractPreferenceController { + private static final String KEY_SERIAL_NUMBER = "serial_number"; + + private final String mSerialNumber; + + public AbstractSerialNumberPreferenceController(Context context) { + this(context, Build.getSerial()); + } + + @VisibleForTesting + AbstractSerialNumberPreferenceController(Context context, String serialNumber) { + super(context); + mSerialNumber = serialNumber; + } + + @Override + public boolean isAvailable() { + return !TextUtils.isEmpty(mSerialNumber); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + final Preference pref = screen.findPreference(KEY_SERIAL_NUMBER); + if (pref != null) { + pref.setSummary(mSerialNumber); + } + } + + @Override + public String getPreferenceKey() { + return KEY_SERIAL_NUMBER; + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java new file mode 100644 index 000000000000..4dbb957f2748 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java @@ -0,0 +1,106 @@ +/* + * 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.settingslib.deviceinfo; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Answers.RETURNS_DEEP_STUBS; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SerialNumberPreferenceControllerTest { + + @Mock(answer = RETURNS_DEEP_STUBS) + private Context mContext; + @Mock(answer = RETURNS_DEEP_STUBS) + private PreferenceScreen mScreen; + + private AbstractSerialNumberPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testIsAvaiable_noSerial_shouldReturnFalse() { + mController = new ConcreteSerialNumberPreferenceController(mContext, null); + + assertThat(mController.isAvailable()).isFalse(); + } + + @Test + public void testIsAvaiable_hasSerial_shouldReturnTrue() { + mController = new ConcreteSerialNumberPreferenceController(mContext, "123"); + + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void testDisplay_noSerial_shouldHidePreference() { + final Preference preference = mock(Preference.class); + when(mScreen.getPreferenceCount()).thenReturn(1); + when(mScreen.getPreference(0)).thenReturn(preference); + mController = new ConcreteSerialNumberPreferenceController(mContext, null); + when(preference.getKey()).thenReturn(mController.getPreferenceKey()); + + mController.displayPreference(mScreen); + + verify(mScreen).removePreference(any(Preference.class)); + } + + @Test + public void testDisplay_hasSerial_shouldSetSummary() { + final String serial = "123"; + final Preference preference = mock(Preference.class); + when(mScreen.findPreference(anyString())).thenReturn(preference); + + mController = new ConcreteSerialNumberPreferenceController(mContext, serial); + mController.displayPreference(mScreen); + + verify(mScreen, never()).removePreference(any(Preference.class)); + verify(preference).setSummary(serial); + } + + private static class ConcreteSerialNumberPreferenceController + extends AbstractSerialNumberPreferenceController { + + ConcreteSerialNumberPreferenceController(Context context, String serialNumber) { + super(context, serialNumber); + } + } +} diff --git a/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml b/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml index db2eb3a27455..bff97f6f8fe4 100644 --- a/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml +++ b/packages/SystemUI/res/drawable/recents_low_ram_stack_button_background.xml @@ -17,6 +17,6 @@ <corners android:radius="@dimen/borderless_button_radius" /> - <solid android:color="#CC000000" /> + <solid android:color="?attr/clearAllBackgroundColor" /> </shape> diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 16c74e3777e2..c7edb9aeb646 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -203,10 +203,6 @@ public class RecentsView extends FrameLayout { mStackButtonShadowDistance.x, mStackButtonShadowDistance.y, mStackButtonShadowColor); } - if (Recents.getConfiguration().isLowRamDevice) { - int bgColor = Utils.getColorAttr(mContext, R.attr.clearAllBackgroundColor); - mStackActionButton.setBackgroundColor(bgColor); - } } // Let's also require dark status and nav bars if the text is dark diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java index 54e9ed9e3b76..492ab44d499b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java @@ -18,8 +18,11 @@ package com.android.systemui.statusbar; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; @@ -30,6 +33,7 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Typeface; +import android.metrics.LogMaker; import android.os.Bundle; import android.provider.Settings; import android.service.notification.SnoozeCriterion; @@ -63,6 +67,15 @@ public class NotificationSnooze extends LinearLayout private static final int MAX_ASSISTANT_SUGGESTIONS = 1; private static final String KEY_DEFAULT_SNOOZE = "default"; private static final String KEY_OPTIONS = "options_array"; + private static final LogMaker OPTIONS_OPEN_LOG = + new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS) + .setType(MetricsEvent.TYPE_OPEN); + private static final LogMaker OPTIONS_CLOSE_LOG = + new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS) + .setType(MetricsEvent.TYPE_CLOSE); + private static final LogMaker UNDO_LOG = + new LogMaker(MetricsEvent.NOTIFICATION_UNDO_SNOOZE) + .setType(MetricsEvent.TYPE_ACTION); private NotificationGuts mGutsContainer; private NotificationSwipeActionHelper mSnoozeListener; private StatusBarNotification mSbn; @@ -88,6 +101,8 @@ public class NotificationSnooze extends LinearLayout R.id.action_snooze_longer, }; + private MetricsLogger mMetricsLogger = new MetricsLogger(); + public NotificationSnooze(Context context, AttributeSet attrs) { super(context, attrs); mParser = new KeyValueListParser(','); @@ -123,7 +138,13 @@ public class NotificationSnooze extends LinearLayout mSnoozeOptions = getDefaultSnoozeOptions(); createOptionViews(); - setSelected(mDefaultOption); + setSelected(mDefaultOption, false); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption); } @Override @@ -163,7 +184,7 @@ public class NotificationSnooze extends LinearLayout SnoozeOption so = mSnoozeOptions.get(i); if (so.getAccessibilityAction() != null && so.getAccessibilityAction().getId() == action) { - setSelected(so); + setSelected(so, true); return true; } } @@ -327,12 +348,24 @@ public class NotificationSnooze extends LinearLayout mExpandAnimation.start(); } - private void setSelected(SnoozeOption option) { + private void setSelected(SnoozeOption option, boolean userAction) { mSelectedOption = option; mSelectedOptionText.setText(option.getConfirmation()); showSnoozeOptions(false); hideSelectedOption(); sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + if (userAction) { + logOptionSelection(MetricsEvent.NOTIFICATION_SELECT_SNOOZE, option); + } + } + + private void logOptionSelection(int category, SnoozeOption option) { + int index = mSnoozeOptions.indexOf(option); + long duration = TimeUnit.MINUTES.toMillis(option.getMinutesToSnoozeFor()); + mMetricsLogger.write(new LogMaker(category) + .setType(MetricsEvent.TYPE_ACTION) + .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_INDEX, index) + .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, duration)); } @Override @@ -343,13 +376,15 @@ public class NotificationSnooze extends LinearLayout final int id = v.getId(); final SnoozeOption tag = (SnoozeOption) v.getTag(); if (tag != null) { - setSelected(tag); + setSelected(tag, true); } else if (id == R.id.notification_snooze) { // Toggle snooze options showSnoozeOptions(!mExpanded); + mMetricsLogger.write(!mExpanded ? OPTIONS_OPEN_LOG : OPTIONS_CLOSE_LOG); } else { // Undo snooze was selected undoSnooze(v); + mMetricsLogger.write(UNDO_LOG); } } @@ -380,7 +415,7 @@ public class NotificationSnooze extends LinearLayout @Override public View getContentView() { // Reset the view before use - setSelected(mDefaultOption); + setSelected(mDefaultOption, false); return this; } @@ -402,7 +437,7 @@ public class NotificationSnooze extends LinearLayout return true; } else { // The view should actually be closed - setSelected(mSnoozeOptions.get(0)); + setSelected(mSnoozeOptions.get(0), false); return false; // Return false here so that guts handles closing the view } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index a2e5a3b07b07..54b4e3518740 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1437,6 +1437,11 @@ public class StatusBar extends SystemUI implements DemoMode, animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); }; + if (hideAnimatedList.isEmpty()) { + animationFinishAction.run(); + return; + } + // let's disable our normal animations mStackScroller.setDismissAllInProgress(true); diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index abe8110c00be..e17a6a6aefa9 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4538,6 +4538,30 @@ message MetricsEvent { // OS: O MR AUTOFILL_UI_LATENCY = 1136; + // Action: the snooze leave-behind was shown after the user clicked the snooze icon + // OS: O MR + NOTIFICATION_SNOOZE_CLICKED = 1137; + + // Action: user selected a notification snooze duration from the drop down + // OS: O MR + NOTIFICATION_SELECT_SNOOZE = 1138; + + // attached to NOTIFICATION_SNOOZED and NOTIFICATION_SELECT_SNOOZE events + // OS: O MR + FIELD_NOTIFICATION_SNOOZE_DURATION_MS = 1139; + + // attached to NOTIFICATION_SELECT_SNOOZE events to indicate the option selected + // OS: O MR + FIELD_NOTIFICATION_SNOOZE_INDEX = 1140; + + // Action: user tapped undo on the notification snooze leave-behind + // OS: O MR + NOTIFICATION_UNDO_SNOOZE = 1141; + + // Action: user togged the visibility of the notification snooze options drop down + // OS: O MR + NOTIFICATION_SNOOZE_OPTIONS = 1142; + // ---- End O-MR1 Constants, all O-MR1 constants go above this line ---- // OPEN: Settings > Network & Internet > Mobile network diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 305683139ffd..555874c0e144 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -33,6 +33,7 @@ import android.net.IpSecSpiResponse; import android.net.IpSecTransform; import android.net.IpSecTransformResponse; import android.net.IpSecUdpEncapResponse; +import android.net.NetworkUtils; import android.net.util.NetdService; import android.os.Binder; import android.os.IBinder; @@ -42,11 +43,14 @@ import android.os.ServiceSpecificException; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; +import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.util.SparseArray; + import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; + import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; @@ -54,6 +58,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.concurrent.atomic.AtomicInteger; + import libcore.io.IoUtils; /** @hide */ @@ -252,7 +257,11 @@ public class IpSecService extends IIpSecService.Stub { return (mReferenceCount.get() > 0); } - public void checkOwnerOrSystemAndThrow() { + /** + * Ensures that the caller is either the owner of this resource or has the system UID and + * throws a SecurityException otherwise. + */ + public void checkOwnerOrSystem() { if (uid != Binder.getCallingUid() && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) { throw new SecurityException("Only the owner may access managed resources!"); @@ -340,7 +349,7 @@ public class IpSecService extends IIpSecService.Stub { // The value should never be null unless the resource doesn't exist // (since we do not allow null resources to be added). if (val != null) { - val.checkOwnerOrSystemAndThrow(); + val.checkOwnerOrSystem(); } return val; } @@ -405,12 +414,8 @@ public class IpSecService extends IIpSecService.Stub { .ipSecDeleteSecurityAssociation( mResourceId, direction, - (mConfig.getLocalAddress() != null) - ? mConfig.getLocalAddress().getHostAddress() - : "", - (mConfig.getRemoteAddress() != null) - ? mConfig.getRemoteAddress().getHostAddress() - : "", + mConfig.getLocalAddress(), + mConfig.getRemoteAddress(), spi); } catch (ServiceSpecificException e) { // FIXME: get the error code and throw is at an IOException from Errno Exception @@ -638,11 +643,45 @@ public class IpSecService extends IIpSecService.Stub { } } + /** + * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be + * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1. + */ + private static void checkInetAddress(String inetAddress) { + if (TextUtils.isEmpty(inetAddress)) { + throw new IllegalArgumentException("Unspecified address"); + } + + InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress); + + if (checkAddr.isAnyLocalAddress()) { + throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress); + } + } + + /** + * Checks the user-provided direction field and throws an IllegalArgumentException if it is not + * DIRECTION_IN or DIRECTION_OUT + */ + private static void checkDirection(int direction) { + switch (direction) { + case IpSecTransform.DIRECTION_OUT: + case IpSecTransform.DIRECTION_IN: + return; + } + throw new IllegalArgumentException("Invalid Direction: " + direction); + } + @Override /** Get a new SPI and maintain the reservation in the system server */ public synchronized IpSecSpiResponse reserveSecurityParameterIndex( int direction, String remoteAddress, int requestedSpi, IBinder binder) throws RemoteException { + checkDirection(direction); + checkInetAddress(remoteAddress); + /* requestedSpi can be anything in the int range, so no check is needed. */ + checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex"); + int resourceId = mNextResourceId.getAndIncrement(); int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; @@ -651,9 +690,7 @@ public class IpSecService extends IIpSecService.Stub { try { if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) { return new IpSecSpiResponse( - IpSecManager.Status.RESOURCE_UNAVAILABLE, - INVALID_RESOURCE_ID, - spi); + IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi); } spi = mSrvConfig @@ -751,6 +788,8 @@ public class IpSecService extends IIpSecService.Stub { throw new IllegalArgumentException( "Specified port number must be a valid non-reserved UDP port"); } + checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket"); + int resourceId = mNextResourceId.getAndIncrement(); FileDescriptor sockFd = null; try { @@ -792,6 +831,67 @@ public class IpSecService extends IIpSecService.Stub { } /** + * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an + * IllegalArgumentException if they are not. + */ + private void checkIpSecConfig(IpSecConfig config) { + if (config.getLocalAddress() == null) { + throw new IllegalArgumentException("Invalid null Local InetAddress"); + } + + if (config.getRemoteAddress() == null) { + throw new IllegalArgumentException("Invalid null Remote InetAddress"); + } + + switch (config.getMode()) { + case IpSecTransform.MODE_TRANSPORT: + if (!config.getLocalAddress().isEmpty()) { + throw new IllegalArgumentException("Non-empty Local Address"); + } + // Must be valid, and not a wildcard + checkInetAddress(config.getRemoteAddress()); + break; + case IpSecTransform.MODE_TUNNEL: + break; + default: + throw new IllegalArgumentException( + "Invalid IpSecTransform.mode: " + config.getMode()); + } + + switch (config.getEncapType()) { + case IpSecTransform.ENCAP_NONE: + break; + case IpSecTransform.ENCAP_ESPINUDP: + case IpSecTransform.ENCAP_ESPINUDP_NON_IKE: + if (mUdpSocketRecords.get(config.getEncapSocketResourceId()) == null) { + throw new IllegalStateException( + "No Encapsulation socket for Resource Id: " + + config.getEncapSocketResourceId()); + } + + int port = config.getEncapRemotePort(); + if (port <= 0 || port > 0xFFFF) { + throw new IllegalArgumentException("Invalid remote UDP port: " + port); + } + break; + default: + throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType()); + } + + for (int direction : DIRECTIONS) { + IpSecAlgorithm crypt = config.getEncryption(direction); + IpSecAlgorithm auth = config.getAuthentication(direction); + if (crypt == null && auth == null) { + throw new IllegalArgumentException("Encryption and Authentication are both null"); + } + + if (mSpiRecords.get(config.getSpiResourceId(direction)) == null) { + throw new IllegalStateException("No SPI for specified Resource Id"); + } + } + } + + /** * Create a transport mode transform, which represent two security associations (one in each * direction) in the kernel. The transform will be cached by the system server and must be freed * when no longer needed. It is possible to free one, deleting the SA from underneath sockets @@ -801,17 +901,19 @@ public class IpSecService extends IIpSecService.Stub { @Override public synchronized IpSecTransformResponse createTransportModeTransform( IpSecConfig c, IBinder binder) throws RemoteException { + checkIpSecConfig(c); + checkNotNull(binder, "Null Binder passed to createTransportModeTransform"); int resourceId = mNextResourceId.getAndIncrement(); if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) { return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); } SpiRecord[] spis = new SpiRecord[DIRECTIONS.length]; - // TODO: Basic input validation here since it's coming over the Binder + int encapType, encapLocalPort = 0, encapRemotePort = 0; UdpSocketRecord socketRecord = null; encapType = c.getEncapType(); if (encapType != IpSecTransform.ENCAP_NONE) { - socketRecord = mUdpSocketRecords.get(c.getEncapLocalResourceId()); + socketRecord = mUdpSocketRecords.get(c.getEncapSocketResourceId()); encapLocalPort = socketRecord.getPort(); encapRemotePort = c.getEncapRemotePort(); } @@ -823,20 +925,15 @@ public class IpSecService extends IIpSecService.Stub { spis[direction] = mSpiRecords.get(c.getSpiResourceId(direction)); int spi = spis[direction].getSpi(); try { - mSrvConfig.getNetdInstance() + mSrvConfig + .getNetdInstance() .ipSecAddSecurityAssociation( resourceId, c.getMode(), direction, - (c.getLocalAddress() != null) - ? c.getLocalAddress().getHostAddress() - : "", - (c.getRemoteAddress() != null) - ? c.getRemoteAddress().getHostAddress() - : "", - (c.getNetwork() != null) - ? c.getNetwork().getNetworkHandle() - : 0, + c.getLocalAddress(), + c.getRemoteAddress(), + (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0, spi, (auth != null) ? auth.getName() : "", (auth != null) ? auth.getKey() : null, @@ -899,12 +996,8 @@ public class IpSecService extends IIpSecService.Stub { socket.getFileDescriptor(), resourceId, direction, - (c.getLocalAddress() != null) - ? c.getLocalAddress().getHostAddress() - : "", - (c.getRemoteAddress() != null) - ? c.getRemoteAddress().getHostAddress() - : "", + c.getLocalAddress(), + c.getRemoteAddress(), info.getSpiRecord(direction).getSpi()); } } catch (ServiceSpecificException e) { diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 8adc2598ea2d..ba3afc316a3d 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -1980,8 +1980,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub final String[] domainStrs = domains == null ? new String[0] : domains.split(" "); final int[] params = { sampleValidity, successThreshold, minSamples, maxSamples }; + final boolean useTls = false; + final String tlsHostname = ""; + final String[] tlsFingerprints = new String[0]; try { - mNetdService.setResolverConfiguration(netId, servers, domainStrs, params); + mNetdService.setResolverConfiguration(netId, servers, domainStrs, params, + useTls, tlsHostname, tlsFingerprints); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 027dc0862e9d..ac85e6b132bf 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -517,11 +517,14 @@ final class ServiceRecord extends Binder { } catch (PackageManager.NameNotFoundException e) { } } - if (localForegroundNoti.getSmallIcon() == null) { + if (localForegroundNoti.getSmallIcon() == null + || nm.getNotificationChannel(localPackageName, appUid, + localForegroundNoti.getChannelId()) == null) { // Notifications whose icon is 0 are defined to not show // a notification, silently ignoring it. We don't want to // just ignore it, we want to prevent the service from // being foreground. + // Also every notification needs a channel. throw new RuntimeException("invalid service notification: " + foregroundNoti); } diff --git a/services/core/java/com/android/server/audio/AudioEventLogger.java b/services/core/java/com/android/server/audio/AudioEventLogger.java index c96138fff8e6..a2c0f76bd841 100644 --- a/services/core/java/com/android/server/audio/AudioEventLogger.java +++ b/services/core/java/com/android/server/audio/AudioEventLogger.java @@ -16,6 +16,8 @@ package com.android.server.audio; +import android.util.Log; + import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; @@ -47,6 +49,22 @@ public class AudioEventLogger { } /** + * Causes the string message for the event to appear in the verbose logcat. + * Here is an example of how to create a new event (a StringEvent), adding it to the logger + * (an instance of AudioEventLogger) while also making it show in the verbose logcat: + * <pre> + * myLogger.log( + * (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) ); + * </pre> + * @param tag the tag for the android.util.Log.v + * @return the same instance of the event + */ + public Event printLog(String tag) { + Log.v(tag, eventToString()); + return this; + } + + /** * Convert event to String. * This method is only called when the logger history is about to the dumped, * so this method is where expensive String conversions should be made, not when the Event diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index c17583ff370c..5eb2a8d2066f 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -751,6 +751,9 @@ public class AudioService extends IAudioService.Stub // relies on audio policy having correct ranges for volume indexes. mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex(); + mPlaybackMonitor = + new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]); + mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor); mRecordMonitor = new RecordingActivityMonitor(mContext); @@ -6977,7 +6980,7 @@ public class AudioService extends IAudioService.Stub //====================== // Audio playback notification //====================== - private final PlaybackActivityMonitor mPlaybackMonitor = new PlaybackActivityMonitor(); + private final PlaybackActivityMonitor mPlaybackMonitor; public void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb) { final boolean isPrivileged = diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index d1a37af5e7a8..6506cf7fa189 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -17,6 +17,8 @@ package com.android.server.audio; import android.annotation.NonNull; +import android.content.Context; +import android.content.pm.PackageManager; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; @@ -90,7 +92,14 @@ public final class PlaybackActivityMonitor private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers = new HashMap<Integer, AudioPlaybackConfiguration>(); - PlaybackActivityMonitor() { + private final Context mContext; + private int mSavedAlarmVolume = -1; + private final int mMaxAlarmVolume; + private int mPrivilegedAlarmActiveCount = 0; + + PlaybackActivityMonitor(Context context, int maxAlarmVolume) { + mContext = context; + mMaxAlarmVolume = maxAlarmVolume; PlayMonitorClient.sListenerDeathMonitor = this; AudioPlaybackConfiguration.sPlayerDeathMonitor = this; } @@ -105,7 +114,7 @@ public final class PlaybackActivityMonitor if (index >= 0) { if (!disable) { if (DEBUG) { // hidden behind DEBUG, too noisy otherwise - mEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid)); + sEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid)); } mBannedUids.remove(index); // nothing else to do, future playback requests from this uid are ok @@ -116,7 +125,7 @@ public final class PlaybackActivityMonitor checkBanPlayer(apc, uid); } if (DEBUG) { // hidden behind DEBUG, too noisy otherwise - mEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid)); + sEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid)); } mBannedUids.add(new Integer(uid)); } // no else to handle, uid already not in list, so enabling again is no-op @@ -151,7 +160,7 @@ public final class PlaybackActivityMonitor new AudioPlaybackConfiguration(pic, newPiid, Binder.getCallingUid(), Binder.getCallingPid()); apc.init(); - mEventLogger.log(new NewPlayerEvent(apc)); + sEventLogger.log(new NewPlayerEvent(apc)); synchronized(mPlayerLock) { mPlayers.put(newPiid, apc); } @@ -163,7 +172,7 @@ public final class PlaybackActivityMonitor synchronized(mPlayerLock) { final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); if (checkConfigurationCaller(piid, apc, binderUid)) { - mEventLogger.log(new AudioAttrEvent(piid, attr)); + sEventLogger.log(new AudioAttrEvent(piid, attr)); change = apc.handleAudioAttributesEvent(attr); } else { Log.e(TAG, "Error updating audio attributes"); @@ -175,6 +184,38 @@ public final class PlaybackActivityMonitor } } + private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) { + if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED || + apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + if ((apc.getAudioAttributes().getAllFlags() & + AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0 && + apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM && + mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, + apc.getClientPid(), apc.getClientUid()) == + PackageManager.PERMISSION_GRANTED) { + if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED && + apc.getPlayerState() != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + if (mPrivilegedAlarmActiveCount++ == 0) { + mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex( + AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER); + AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM, + mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); + } + } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED && + apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + if (--mPrivilegedAlarmActiveCount == 0) { + if (AudioSystem.getStreamVolumeIndex( + AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) == + mMaxAlarmVolume) { + AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM, + mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); + } + } + } + } + } + } + public void playerEvent(int piid, int event, int binderUid) { if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); } final boolean change; @@ -183,12 +224,12 @@ public final class PlaybackActivityMonitor if (apc == null) { return; } - mEventLogger.log(new PlayerEvent(piid, event)); + sEventLogger.log(new PlayerEvent(piid, event)); if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { for (Integer uidInteger: mBannedUids) { if (checkBanPlayer(apc, uidInteger.intValue())) { // player was banned, do not update its state - mEventLogger.log(new AudioEventLogger.StringEvent( + sEventLogger.log(new AudioEventLogger.StringEvent( "not starting piid:" + piid + " ,is banned")); return; } @@ -200,6 +241,7 @@ public final class PlaybackActivityMonitor } if (checkConfigurationCaller(piid, apc, binderUid)) { //TODO add generation counter to only update to the latest state + checkVolumeForPrivilegedAlarm(apc, event); change = apc.handleStateEvent(event); } else { Log.e(TAG, "Error handling event " + event); @@ -216,7 +258,7 @@ public final class PlaybackActivityMonitor public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid) { // no check on UID yet because this is only for logging at the moment - mEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid)); + sEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid)); } public void releasePlayer(int piid, int binderUid) { @@ -224,10 +266,11 @@ public final class PlaybackActivityMonitor synchronized(mPlayerLock) { final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); if (checkConfigurationCaller(piid, apc, binderUid)) { - mEventLogger.log(new AudioEventLogger.StringEvent( + sEventLogger.log(new AudioEventLogger.StringEvent( "releasing player piid:" + piid)); mPlayers.remove(new Integer(piid)); mDuckingManager.removeReleased(apc); + checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); } } @@ -278,7 +321,7 @@ public final class PlaybackActivityMonitor } pw.println("\n"); // log - mEventLogger.dump(pw); + sEventLogger.dump(pw); } } @@ -456,7 +499,8 @@ public final class PlaybackActivityMonitor } if (mute) { try { - Log.v(TAG, "call: muting player" + piid + " uid:" + apc.getClientUid()); + sEventLogger.log((new AudioEventLogger.StringEvent("call: muting piid:" + + piid + " uid:" + apc.getClientUid())).printLog(TAG)); apc.getPlayerProxy().setVolume(0.0f); mMutedPlayers.add(new Integer(piid)); } catch (Exception e) { @@ -480,7 +524,8 @@ public final class PlaybackActivityMonitor final AudioPlaybackConfiguration apc = mPlayers.get(piid); if (apc != null) { try { - Log.v(TAG, "call: unmuting player" + piid + " uid:" + apc.getClientUid()); + sEventLogger.log(new AudioEventLogger.StringEvent("call: unmuting piid:" + + piid).printLog(TAG)); apc.getPlayerProxy().setVolume(1.0f); } catch (Exception e) { Log.e(TAG, "call: error unmuting player " + piid + " uid:" @@ -669,8 +714,7 @@ public final class PlaybackActivityMonitor return; } try { - Log.v(TAG, "ducking (skipRamp=" + skipRamp + ") player piid:" - + apc.getPlayerInterfaceId() + " uid:" + mUid); + sEventLogger.log((new DuckEvent(apc, skipRamp)).printLog(TAG)); apc.getPlayerProxy().applyVolumeShaper( DUCK_VSHAPE, skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED); @@ -685,7 +729,8 @@ public final class PlaybackActivityMonitor final AudioPlaybackConfiguration apc = players.get(piid); if (apc != null) { try { - Log.v(TAG, "unducking player " + piid + " uid:" + mUid); + sEventLogger.log((new AudioEventLogger.StringEvent("unducking piid:" + + piid)).printLog(TAG)); apc.getPlayerProxy().applyVolumeShaper( DUCK_ID, VolumeShaper.Operation.REVERSE); @@ -772,7 +817,28 @@ public final class PlaybackActivityMonitor } } - private final static class AudioAttrEvent extends AudioEventLogger.Event { + private static final class DuckEvent extends AudioEventLogger.Event { + private final int mPlayerIId; + private final boolean mSkipRamp; + private final int mClientUid; + private final int mClientPid; + + DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) { + mPlayerIId = apc.getPlayerInterfaceId(); + mSkipRamp = skipRamp; + mClientUid = apc.getClientUid(); + mClientPid = apc.getClientPid(); + } + + @Override + public String eventToString() { + return new StringBuilder("ducking player piid:").append(mPlayerIId) + .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid) + .append(" skip ramp:").append(mSkipRamp).toString(); + } + } + + private static final class AudioAttrEvent extends AudioEventLogger.Event { private final int mPlayerIId; private final AudioAttributes mPlayerAttr; @@ -787,6 +853,6 @@ public final class PlaybackActivityMonitor } } - private final AudioEventLogger mEventLogger = new AudioEventLogger(100, + private static final AudioEventLogger sEventLogger = new AudioEventLogger(100, "playback activity as reported through PlayerBase"); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 90dab2c38725..b4056b333fe5 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -331,7 +331,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_UPDATE_INTERFACE_QUOTA = 10; private static final int MSG_REMOVE_INTERFACE_QUOTA = 11; private static final int MSG_POLICIES_CHANGED = 13; - private static final int MSG_SET_FIREWALL_RULES = 14; private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15; private static final int UID_MSG_STATE_CHANGED = 100; @@ -3138,9 +3137,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW); } } - setUidFirewallRulesAsync(chain, uidRules, CHAIN_TOGGLE_ENABLE); + setUidFirewallRulesUL(chain, uidRules, CHAIN_TOGGLE_ENABLE); } else { - setUidFirewallRulesAsync(chain, null, CHAIN_TOGGLE_DISABLE); + setUidFirewallRulesUL(chain, null, CHAIN_TOGGLE_DISABLE); } } @@ -3207,7 +3206,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - setUidFirewallRulesAsync(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE); + setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } @@ -3906,18 +3905,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { removeInterfaceQuota((String) msg.obj); return true; } - case MSG_SET_FIREWALL_RULES: { - final int chain = msg.arg1; - final int toggle = msg.arg2; - final SparseIntArray uidRules = (SparseIntArray) msg.obj; - if (uidRules != null) { - setUidFirewallRules(chain, uidRules); - } - if (toggle != CHAIN_TOGGLE_NONE) { - enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE); - } - return true; - } case MSG_RESET_FIREWALL_RULES_BY_UID: { resetUidFirewallRules(msg.arg1); return true; @@ -4063,15 +4050,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** * Calls {@link #setUidFirewallRules(int, SparseIntArray)} and - * {@link #enableFirewallChainUL(int, boolean)} asynchronously. + * {@link #enableFirewallChainUL(int, boolean)} synchronously. * * @param chain firewall chain. * @param uidRules new UID rules; if {@code null}, only toggles chain state. * @param toggle whether the chain should be enabled, disabled, or not changed. */ - private void setUidFirewallRulesAsync(int chain, @Nullable SparseIntArray uidRules, + private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules, @ChainToggleType int toggle) { - mHandler.obtainMessage(MSG_SET_FIREWALL_RULES, chain, toggle, uidRules).sendToTarget(); + if (uidRules != null) { + setUidFirewallRulesUL(chain, uidRules); + } + if (toggle != CHAIN_TOGGLE_NONE) { + enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE); + } } /** @@ -4079,7 +4071,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * here to netd. It will clean up dead rules and make sure the target chain only contains rules * specified here. */ - private void setUidFirewallRules(int chain, SparseIntArray uidRules) { + private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) { try { int size = uidRules.size(); int[] uids = new int[size]; diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index 4923b06e9595..f1476b34388b 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -17,8 +17,10 @@ package com.android.server.notification; import android.app.Notification; +import android.app.NotificationChannel; public interface NotificationManagerInternal { + NotificationChannel getNotificationChannel(String pkg, int uid, String channelId); void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid, String tag, int id, Notification notification, int userId); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6e84dd211692..4cb5d0fdf502 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3286,6 +3286,10 @@ public class NotificationManagerService extends SystemService { long conditionsToken = proto.start(NotificationServiceDumpProto.CONDITION_PROVIDERS); mConditionProviders.dump(proto, filter); proto.end(conditionsToken); + + long rankingToken = proto.start(NotificationServiceDumpProto.RANKING_CONFIG); + mRankingHelper.dump(proto, filter); + proto.end(rankingToken); } proto.flush(); @@ -3441,6 +3445,12 @@ public class NotificationManagerService extends SystemService { */ private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { @Override + public NotificationChannel getNotificationChannel(String pkg, int uid, String + channelId) { + return mRankingHelper.getNotificationChannel(pkg, uid, channelId, false); + } + + @Override public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, String tag, int id, Notification notification, int userId) { enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, @@ -3807,6 +3817,8 @@ public class NotificationManagerService extends SystemService { MetricsLogger.action(r.getLogMaker() .setCategory(MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsEvent.TYPE_CLOSE) + .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, + mDuration) .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA, mSnoozeCriterionId == null ? 0 : 1)); boolean wasPosted = removeFromNotificationListsLocked(r); @@ -5828,10 +5840,9 @@ public class NotificationManagerService extends SystemService { final DumpFilter filter = new DumpFilter(); for (int ai = 0; ai < args.length; ai++) { final String a = args[ai]; - if ("--proto".equals(args[0])) { + if ("--proto".equals(a)) { filter.proto = true; - } - if ("--noredact".equals(a) || "--reveal".equals(a)) { + } else if ("--noredact".equals(a) || "--reveal".equals(a)) { filter.redact = false; } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) { if (ai < args.length-1) { diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index fea2464ba5b8..8783f4704f5b 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -36,10 +36,13 @@ import android.os.Build; import android.os.UserHandle; import android.provider.Settings.Secure; import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.RankingHelperProto; +import android.service.notification.RankingHelperProto.RecordProto; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Slog; import android.util.SparseBooleanArray; +import android.util.proto.ProtoOutputStream; import org.json.JSONArray; import org.json.JSONException; @@ -915,8 +918,7 @@ public class RankingHelper implements RankingConfig { pw.print(" "); pw.println(mSignalExtractors[i]); } - } - if (filter == null) { + pw.print(prefix); pw.println("per-package config:"); } @@ -928,6 +930,52 @@ public class RankingHelper implements RankingConfig { dumpRecords(pw, prefix, filter, mRestoredWithoutUids); } + public void dump(ProtoOutputStream proto, NotificationManagerService.DumpFilter filter) { + final int N = mSignalExtractors.length; + for (int i = 0; i < N; i++) { + proto.write(RankingHelperProto.NOTIFICATION_SIGNAL_EXTRACTORS, + mSignalExtractors[i].getClass().getSimpleName()); + } + synchronized (mRecords) { + dumpRecords(proto, RankingHelperProto.RECORDS, filter, mRecords); + } + dumpRecords(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter, + mRestoredWithoutUids); + } + + private static void dumpRecords(ProtoOutputStream proto, long fieldId, + NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) { + final int N = records.size(); + long fToken; + for (int i = 0; i < N; i++) { + final Record r = records.valueAt(i); + if (filter == null || filter.matches(r.pkg)) { + fToken = proto.start(fieldId); + + proto.write(RecordProto.PACKAGE, r.pkg); + proto.write(RecordProto.UID, r.uid); + proto.write(RecordProto.IMPORTANCE, r.importance); + proto.write(RecordProto.PRIORITY, r.priority); + proto.write(RecordProto.VISIBILITY, r.visibility); + proto.write(RecordProto.SHOW_BADGE, r.showBadge); + + long token; + for (NotificationChannel channel : r.channels.values()) { + token = proto.start(RecordProto.CHANNELS); + channel.toProto(proto); + proto.end(token); + } + for (NotificationChannelGroup group : r.groups.values()) { + token = proto.start(RecordProto.CHANNEL_GROUPS); + group.toProto(proto); + proto.end(token); + } + + proto.end(fToken); + } + } + } + private static void dumpRecords(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) { final int N = records.size(); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 91cce3175b0a..bdf46c76d73a 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6c859f76c906..7830623b7604 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5350,7 +5350,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void forceWipeUser(int userId) { + private void forceWipeUser(int userId, String wipeReasonForUser) { try { IActivityManager am = mInjector.getIActivityManager(); if (am.getCurrentUser().id == userId) { @@ -5361,7 +5361,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!userRemoved) { Slog.w(LOG_TAG, "Couldn't remove user " + userId); } else if (isManagedProfile(userId)) { - sendWipeProfileNotification(); + sendWipeProfileNotification(wipeReasonForUser); } } catch (RemoteException re) { // Shouldn't happen @@ -5369,23 +5369,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public void wipeData(int flags) { + public void wipeDataWithReason(int flags, String wipeReasonForUser) { if (!mHasFeature) { return; } + Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty"); enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId()); final ActiveAdmin admin; synchronized (this) { admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); } - String reason = "DevicePolicyManager.wipeData() from " + String internalReason = "DevicePolicyManager.wipeDataWithReason() from " + admin.info.getComponent().flattenToShortString(); wipeDataNoLock( - admin.info.getComponent(), flags, reason, admin.getUserHandle().getIdentifier()); + admin.info.getComponent(), flags, internalReason, wipeReasonForUser, + admin.getUserHandle().getIdentifier()); } - private void wipeDataNoLock(ComponentName admin, int flags, String reason, int userId) { + private void wipeDataNoLock(ComponentName admin, int flags, String internalReason, + String wipeReasonForUser, int userId) { wtfIfInLock(); long ident = mInjector.binderClearCallingIdentity(); @@ -5420,25 +5423,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // (rather than system), we should probably trigger factory reset. Current code just // removes that user (but still clears FRP...) if (userId == UserHandle.USER_SYSTEM) { - forceWipeDeviceNoLock(/*wipeExtRequested=*/ (flags & WIPE_EXTERNAL_STORAGE) != 0, - reason, /*wipeEuicc=*/ (flags & WIPE_EUICC) != 0); + forceWipeDeviceNoLock(/*wipeExtRequested=*/ ( + flags & WIPE_EXTERNAL_STORAGE) != 0, + internalReason, + /*wipeEuicc=*/ (flags & WIPE_EUICC) != 0); } else { - forceWipeUser(userId); + forceWipeUser(userId, wipeReasonForUser); } } finally { mInjector.binderRestoreCallingIdentity(ident); } } - private void sendWipeProfileNotification() { - String contentText = mContext.getString(R.string.work_profile_deleted_description_dpm_wipe); + private void sendWipeProfileNotification(String wipeReasonForUser) { Notification notification = new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN) .setSmallIcon(android.R.drawable.stat_sys_warning) .setContentTitle(mContext.getString(R.string.work_profile_deleted)) - .setContentText(contentText) + .setContentText(wipeReasonForUser) .setColor(mContext.getColor(R.color.system_notification_accent_color)) - .setStyle(new Notification.BigTextStyle().bigText(contentText)) + .setStyle(new Notification.BigTextStyle().bigText(wipeReasonForUser)) .build(); mInjector.getNotificationManager().notify(SystemMessage.NOTE_PROFILE_WIPED, notification); } @@ -5610,9 +5614,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // able to do so). // IMPORTANT: Call without holding the lock to prevent deadlock. try { + String wipeReasonForUser = mContext.getString( + R.string.work_profile_deleted_reason_maximum_password_failure); wipeDataNoLock(strictestAdmin.info.getComponent(), /*flags=*/ 0, /*reason=*/ "reportFailedPasswordAttempt()", + wipeReasonForUser, userId); } catch (SecurityException e) { Slog.w(LOG_TAG, "Failed to wipe user " + userId @@ -5621,7 +5628,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (mInjector.securityLogIsLoggingEnabled()) { - SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0, + SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, + /*result*/ 0, /*method strength*/ 1); } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 87b0db80103a..a8bf8f18b09a 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -3421,6 +3421,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Even if the caller is the managed profile, the current user is the user 0 when(getServices().iactivityManager.getCurrentUser()) .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); + // Get mock reason string since we throw an IAE with empty string input. + when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)). + thenReturn("Just a test string."); dpm.wipeData(0); verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed( @@ -3440,6 +3443,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.of(MANAGED_PROFILE_USER_ID))) .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM); + when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)). + thenReturn("Just a test string."); + mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; // The PO is not allowed to remove the profile if the user restriction was set on the // profile by the system @@ -3453,6 +3459,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER); + when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)). + thenReturn("Just a test string."); dpm.wipeData(0); verify(getServices().recoverySystem).rebootWipeUserData( @@ -3466,6 +3474,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER); + when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)). + thenReturn("Just a test string."); dpm.wipeData(WIPE_EUICC); verify(getServices().recoverySystem).rebootWipeUserData( @@ -3479,6 +3489,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM)) .thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM); + when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)). + thenReturn("Just a test string."); // The DO is not allowed to wipe the device if the user restriction was set // by the system assertExpectException(SecurityException.class, /* messageRegex= */ null, diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java index ebac0419785c..764b7b22f3d2 100644 --- a/telephony/java/android/telephony/MbmsDownloadSession.java +++ b/telephony/java/android/telephony/MbmsDownloadSession.java @@ -522,8 +522,7 @@ public class MbmsDownloadSession implements AutoCloseable { * @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on. */ public void registerStateCallback(@NonNull DownloadRequest request, - @NonNull DownloadStateCallback callback, - @NonNull Handler handler) { + @NonNull DownloadStateCallback callback, @NonNull Handler handler) { IMbmsDownloadService downloadService = mService.get(); if (downloadService == null) { throw new IllegalStateException("Middleware not yet bound"); @@ -533,7 +532,8 @@ public class MbmsDownloadSession implements AutoCloseable { new InternalDownloadStateCallback(callback, handler); try { - int result = downloadService.registerStateCallback(request, internalCallback); + int result = downloadService.registerStateCallback(request, internalCallback, + callback.getCallbackFilterFlags()); if (result != MbmsErrors.SUCCESS) { if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) { throw new IllegalArgumentException("Unknown download request."); diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java index 86920bd3b205..892fbf078ea1 100644 --- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java +++ b/telephony/java/android/telephony/mbms/DownloadStateCallback.java @@ -16,8 +16,12 @@ package android.telephony.mbms; +import android.annotation.IntDef; import android.telephony.MbmsDownloadSession; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A optional listener class used by download clients to track progress. Apps should extend this * class and pass an instance into @@ -29,6 +33,71 @@ import android.telephony.MbmsDownloadSession; public class DownloadStateCallback { /** + * Bitmask flags used for filtering out callback methods. Used when constructing the + * DownloadStateCallback as an optional parameter. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES}) + public @interface FilterFlag {} + + /** + * Receive all callbacks. + * Default value. + */ + public static final int ALL_UPDATES = 0x00; + /** + * Receive callbacks for {@link #onProgressUpdated}. + */ + public static final int PROGRESS_UPDATES = 0x01; + /** + * Receive callbacks for {@link #onStateUpdated}. + */ + public static final int STATE_UPDATES = 0x02; + + private final int mCallbackFilterFlags; + + /** + * Creates a DownloadStateCallback that will receive all callbacks. + */ + public DownloadStateCallback() { + mCallbackFilterFlags = ALL_UPDATES; + } + + /** + * Creates a DownloadStateCallback that will only receive callbacks for the methods specified + * via the filterFlags parameter. + * @param filterFlags A bitmask of filter flags that will specify which callback this instance + * is interested in. + */ + public DownloadStateCallback(int filterFlags) { + mCallbackFilterFlags = filterFlags; + } + + /** + * Return the currently set filter flags. + * @return An integer containing the bitmask of flags that this instance is interested in. + * @hide + */ + public int getCallbackFilterFlags() { + return mCallbackFilterFlags; + } + + /** + * Returns true if a filter flag is set for a particular callback method. If the flag is set, + * the callback will be delivered to the listening process. + * @param flag A filter flag specifying whether or not a callback method is registered to + * receive callbacks. + * @return true if registered to receive callbacks in the listening process, false if not. + */ + public final boolean isFilterFlagSet(@FilterFlag int flag) { + if (mCallbackFilterFlags == ALL_UPDATES) { + return true; + } + return (mCallbackFilterFlags & flag) > 0; + } + + /** * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}. * * @param request a {@link DownloadRequest}, indicating which download is being referenced. diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl index ed5e8268fc77..cb93542ff168 100755 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl @@ -36,7 +36,8 @@ interface IMbmsDownloadService int download(in DownloadRequest downloadRequest); - int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener); + int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener, + int flags); int unregisterStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener); diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java index d845a57b946a..2f85a1df8a22 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java @@ -46,6 +46,47 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>(); private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>(); + + // Filters the DownloadStateCallbacks by its configuration from the app. + private abstract static class FilteredDownloadStateCallback extends DownloadStateCallback { + + private final IDownloadStateCallback mCallback; + public FilteredDownloadStateCallback(IDownloadStateCallback callback, int callbackFlags) { + super(callbackFlags); + mCallback = callback; + } + + @Override + public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, + int currentDownloadSize, int fullDownloadSize, int currentDecodedSize, + int fullDecodedSize) { + if (!isFilterFlagSet(PROGRESS_UPDATES)) { + return; + } + try { + mCallback.onProgressUpdated(request, fileInfo, currentDownloadSize, + fullDownloadSize, currentDecodedSize, fullDecodedSize); + } catch (RemoteException e) { + onRemoteException(e); + } + } + + @Override + public void onStateUpdated(DownloadRequest request, FileInfo fileInfo, + @MbmsDownloadSession.DownloadStatus int state) { + if (!isFilterFlagSet(STATE_UPDATES)) { + return; + } + try { + mCallback.onStateUpdated(request, fileInfo, state); + } catch (RemoteException e) { + onRemoteException(e); + } + } + + protected abstract void onRemoteException(RemoteException e); + } + /** * Initialize the download service for this app and subId, registering the listener. * @@ -196,9 +237,8 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { * @hide */ @Override - public final int registerStateCallback( - final DownloadRequest downloadRequest, final IDownloadStateCallback callback) - throws RemoteException { + public final int registerStateCallback(final DownloadRequest downloadRequest, + final IDownloadStateCallback callback, int flags) throws RemoteException { final int uid = Binder.getCallingUid(); DeathRecipient deathRecipient = new DeathRecipient() { @Override @@ -211,28 +251,10 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient); callback.asBinder().linkToDeath(deathRecipient, 0); - DownloadStateCallback exposedCallback = new DownloadStateCallback() { + DownloadStateCallback exposedCallback = new FilteredDownloadStateCallback(callback, flags) { @Override - public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int - currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int - fullDecodedSize) { - try { - callback.onProgressUpdated(request, fileInfo, currentDownloadSize, - fullDownloadSize, - currentDecodedSize, fullDecodedSize); - } catch (RemoteException e) { - onAppCallbackDied(uid, downloadRequest.getSubscriptionId()); - } - } - - @Override - public void onStateUpdated(DownloadRequest request, FileInfo fileInfo, - @MbmsDownloadSession.DownloadStatus int state) { - try { - callback.onStateUpdated(request, fileInfo, state); - } catch (RemoteException e) { - onAppCallbackDied(uid, downloadRequest.getSubscriptionId()); - } + protected void onRemoteException(RemoteException e) { + onAppCallbackDied(uid, downloadRequest.getSubscriptionId()); } }; diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java new file mode 100644 index 000000000000..1b4bef5d2f43 --- /dev/null +++ b/tests/net/java/android/net/IpSecConfigTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; +import android.support.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link IpSecConfig}. */ +@SmallTest +@RunWith(JUnit4.class) +public class IpSecConfigTest { + + @Test + public void testDefaults() throws Exception { + IpSecConfig c = new IpSecConfig(); + assertEquals(IpSecTransform.MODE_TRANSPORT, c.getMode()); + assertEquals("", c.getLocalAddress()); + assertEquals("", c.getRemoteAddress()); + assertNull(c.getNetwork()); + assertEquals(IpSecTransform.ENCAP_NONE, c.getEncapType()); + assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getEncapSocketResourceId()); + assertEquals(0, c.getEncapRemotePort()); + assertEquals(0, c.getNattKeepaliveInterval()); + for (int direction : + new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}) { + assertNull(c.getEncryption(direction)); + assertNull(c.getAuthentication(direction)); + assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId(direction)); + } + } + + @Test + public void testParcelUnparcel() throws Exception { + assertParcelingIsLossless(new IpSecConfig()); + + IpSecConfig c = new IpSecConfig(); + c.setMode(IpSecTransform.MODE_TUNNEL); + c.setLocalAddress("0.0.0.0"); + c.setRemoteAddress("1.2.3.4"); + c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP); + c.setEncapSocketResourceId(7); + c.setEncapRemotePort(22); + c.setNattKeepaliveInterval(42); + c.setEncryption( + IpSecTransform.DIRECTION_OUT, + new IpSecAlgorithm( + IpSecAlgorithm.CRYPT_AES_CBC, + new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF})); + c.setAuthentication( + IpSecTransform.DIRECTION_OUT, + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_SHA1, + new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0})); + c.setSpiResourceId(IpSecTransform.DIRECTION_OUT, 1984); + c.setEncryption( + IpSecTransform.DIRECTION_IN, + new IpSecAlgorithm( + IpSecAlgorithm.CRYPT_AES_CBC, + new byte[] {2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF})); + c.setAuthentication( + IpSecTransform.DIRECTION_IN, + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_SHA1, + new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 1})); + c.setSpiResourceId(IpSecTransform.DIRECTION_IN, 99); + assertParcelingIsLossless(c); + } + + private void assertParcelingIsLossless(IpSecConfig ci) throws Exception { + Parcel p = Parcel.obtain(); + ci.writeToParcel(p, 0); + p.setDataPosition(0); + IpSecConfig co = IpSecConfig.CREATOR.createFromParcel(p); + assertTrue(IpSecConfig.equals(co, ci)); + } +} diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java new file mode 100644 index 000000000000..9057a108dec4 --- /dev/null +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -0,0 +1,276 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.net.INetd; +import android.net.IpSecAlgorithm; +import android.net.IpSecConfig; +import android.net.IpSecManager; +import android.net.IpSecSpiResponse; +import android.net.IpSecTransform; +import android.net.IpSecTransformResponse; +import android.net.NetworkUtils; +import android.os.Binder; +import android.os.ParcelFileDescriptor; +import android.support.test.filters.SmallTest; +import android.system.OsConstants; + +import java.net.Socket; +import java.util.Arrays; +import java.util.Collection; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/** Unit tests for {@link IpSecService}. */ +@SmallTest +@RunWith(Parameterized.class) +public class IpSecServiceParameterizedTest { + + private static final int DROID_SPI = 0xD1201D; + private static final int DROID_SPI2 = DROID_SPI + 1; + + private final String mRemoteAddr; + + @Parameterized.Parameters + public static Collection ipSecConfigs() { + return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}}); + } + + private static final byte[] CRYPT_KEY = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + }; + private static final byte[] AUTH_KEY = { + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F + }; + + Context mMockContext; + INetd mMockNetd; + IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; + IpSecService mIpSecService; + + public IpSecServiceParameterizedTest(String remoteAddr) { + mRemoteAddr = remoteAddr; + } + + @Before + public void setUp() throws Exception { + mMockContext = mock(Context.class); + mMockNetd = mock(INetd.class); + mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); + mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); + + // Injecting mock netd + when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); + } + + @Test + public void testIpSecServiceReserveSpi() throws Exception { + when(mMockNetd.ipSecAllocateSpi( + anyInt(), + eq(IpSecTransform.DIRECTION_OUT), + anyString(), + eq(mRemoteAddr), + eq(DROID_SPI))) + .thenReturn(DROID_SPI); + + IpSecSpiResponse spiResp = + mIpSecService.reserveSecurityParameterIndex( + IpSecTransform.DIRECTION_OUT, mRemoteAddr, DROID_SPI, new Binder()); + assertEquals(IpSecManager.Status.OK, spiResp.status); + assertEquals(DROID_SPI, spiResp.spi); + } + + @Test + public void testReleaseSecurityParameterIndex() throws Exception { + when(mMockNetd.ipSecAllocateSpi( + anyInt(), + eq(IpSecTransform.DIRECTION_OUT), + anyString(), + eq(mRemoteAddr), + eq(DROID_SPI))) + .thenReturn(DROID_SPI); + + IpSecSpiResponse spiResp = + mIpSecService.reserveSecurityParameterIndex( + IpSecTransform.DIRECTION_OUT, mRemoteAddr, DROID_SPI, new Binder()); + + mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId); + + verify(mMockNetd) + .ipSecDeleteSecurityAssociation( + eq(spiResp.resourceId), anyInt(), anyString(), anyString(), eq(DROID_SPI)); + } + + IpSecConfig buildIpSecConfig() throws Exception { + IpSecManager ipSecManager = new IpSecManager(mIpSecService); + + // Mocking the netd to allocate SPI + when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt())) + .thenReturn(DROID_SPI) + .thenReturn(DROID_SPI2); + + IpSecAlgorithm encryptAlgo = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY); + IpSecAlgorithm authAlgo = + new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 8); + + /** Allocate and add SPI records in the IpSecService through IpSecManager interface. */ + IpSecManager.SecurityParameterIndex outSpi = + ipSecManager.reserveSecurityParameterIndex( + IpSecTransform.DIRECTION_OUT, + NetworkUtils.numericToInetAddress(mRemoteAddr)); + IpSecManager.SecurityParameterIndex inSpi = + ipSecManager.reserveSecurityParameterIndex( + IpSecTransform.DIRECTION_IN, + NetworkUtils.numericToInetAddress(mRemoteAddr)); + + IpSecConfig config = new IpSecConfig(); + config.setSpiResourceId(IpSecTransform.DIRECTION_IN, inSpi.getResourceId()); + config.setSpiResourceId(IpSecTransform.DIRECTION_OUT, outSpi.getResourceId()); + config.setEncryption(IpSecTransform.DIRECTION_OUT, encryptAlgo); + config.setAuthentication(IpSecTransform.DIRECTION_OUT, authAlgo); + config.setEncryption(IpSecTransform.DIRECTION_IN, encryptAlgo); + config.setAuthentication(IpSecTransform.DIRECTION_IN, authAlgo); + config.setRemoteAddress(mRemoteAddr); + return config; + } + + @Test + public void testCreateTransportModeTransform() throws Exception { + IpSecConfig ipSecConfig = buildIpSecConfig(); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + assertEquals(IpSecManager.Status.OK, createTransformResp.status); + + verify(mMockNetd) + .ipSecAddSecurityAssociation( + eq(createTransformResp.resourceId), + anyInt(), + eq(IpSecTransform.DIRECTION_OUT), + anyString(), + anyString(), + anyLong(), + eq(DROID_SPI), + eq(IpSecAlgorithm.AUTH_HMAC_SHA256), + eq(AUTH_KEY), + anyInt(), + eq(IpSecAlgorithm.CRYPT_AES_CBC), + eq(CRYPT_KEY), + anyInt(), + anyInt(), + anyInt(), + anyInt()); + verify(mMockNetd) + .ipSecAddSecurityAssociation( + eq(createTransformResp.resourceId), + anyInt(), + eq(IpSecTransform.DIRECTION_IN), + anyString(), + anyString(), + anyLong(), + eq(DROID_SPI2), + eq(IpSecAlgorithm.AUTH_HMAC_SHA256), + eq(AUTH_KEY), + anyInt(), + eq(IpSecAlgorithm.CRYPT_AES_CBC), + eq(CRYPT_KEY), + anyInt(), + anyInt(), + anyInt(), + anyInt()); + } + + @Test + public void testDeleteTransportModeTransform() throws Exception { + IpSecConfig ipSecConfig = buildIpSecConfig(); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId); + + verify(mMockNetd) + .ipSecDeleteSecurityAssociation( + eq(createTransformResp.resourceId), + eq(IpSecTransform.DIRECTION_OUT), + anyString(), + anyString(), + eq(DROID_SPI)); + verify(mMockNetd) + .ipSecDeleteSecurityAssociation( + eq(createTransformResp.resourceId), + eq(IpSecTransform.DIRECTION_IN), + anyString(), + anyString(), + eq(DROID_SPI2)); + } + + @Test + public void testApplyTransportModeTransform() throws Exception { + IpSecConfig ipSecConfig = buildIpSecConfig(); + + IpSecTransformResponse createTransformResp = + mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); + + int resourceId = createTransformResp.resourceId; + mIpSecService.applyTransportModeTransform(pfd, resourceId); + + verify(mMockNetd) + .ipSecApplyTransportModeTransform( + eq(pfd.getFileDescriptor()), + eq(resourceId), + eq(IpSecTransform.DIRECTION_OUT), + anyString(), + anyString(), + eq(DROID_SPI)); + verify(mMockNetd) + .ipSecApplyTransportModeTransform( + eq(pfd.getFileDescriptor()), + eq(resourceId), + eq(IpSecTransform.DIRECTION_IN), + anyString(), + anyString(), + eq(DROID_SPI2)); + } + + @Test + public void testRemoveTransportModeTransform() throws Exception { + ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); + mIpSecService.removeTransportModeTransform(pfd, 1); + + verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor()); + } +} diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index 23fee286b8e7..efc58ccf7346 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -23,34 +23,28 @@ import static android.system.OsConstants.SOCK_DGRAM; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.net.INetd; -import android.net.IpSecAlgorithm; -import android.net.IpSecConfig; import android.net.IpSecManager; import android.net.IpSecSpiResponse; import android.net.IpSecTransform; -import android.net.IpSecTransformResponse; import android.net.IpSecUdpEncapResponse; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.support.test.filters.SmallTest; import android.system.ErrnoException; import android.system.Os; + import java.io.FileDescriptor; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -62,13 +56,8 @@ import org.junit.runners.JUnit4; public class IpSecServiceTest { private static final int DROID_SPI = 0xD1201D; - private static final int DROID_SPI2 = DROID_SPI + 1; private static final int TEST_UDP_ENCAP_INVALID_PORT = 100; private static final int TEST_UDP_ENCAP_PORT_OUT_RANGE = 100000; - private static final int TEST_UDP_ENCAP_PORT = 34567; - - private static final String IPV4_LOOPBACK = "127.0.0.1"; - private static final String IPV4_ADDR = "192.168.0.2"; private static final InetAddress INADDR_ANY; @@ -80,21 +69,6 @@ public class IpSecServiceTest { } } - private static final int[] DIRECTIONS = - new int[] {IpSecTransform.DIRECTION_OUT, IpSecTransform.DIRECTION_IN}; - private static final byte[] CRYPT_KEY = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - }; - private static final byte[] AUTH_KEY = { - 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, - 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F - }; - Context mMockContext; INetd mMockNetd; IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; @@ -118,44 +92,6 @@ public class IpSecServiceTest { } @Test - public void testIpSecServiceReserveSpi() throws Exception { - when(mMockNetd.ipSecAllocateSpi( - anyInt(), - eq(IpSecTransform.DIRECTION_OUT), - anyString(), - eq(IPV4_LOOPBACK), - eq(DROID_SPI))) - .thenReturn(DROID_SPI); - - IpSecSpiResponse spiResp = - mIpSecService.reserveSecurityParameterIndex( - IpSecTransform.DIRECTION_OUT, IPV4_LOOPBACK, DROID_SPI, new Binder()); - assertEquals(IpSecManager.Status.OK, spiResp.status); - assertEquals(DROID_SPI, spiResp.spi); - } - - @Test - public void testReleaseSecurityParameterIndex() throws Exception { - when(mMockNetd.ipSecAllocateSpi( - anyInt(), - eq(IpSecTransform.DIRECTION_OUT), - anyString(), - eq(IPV4_LOOPBACK), - eq(DROID_SPI))) - .thenReturn(DROID_SPI); - - IpSecSpiResponse spiResp = - mIpSecService.reserveSecurityParameterIndex( - IpSecTransform.DIRECTION_OUT, IPV4_LOOPBACK, DROID_SPI, new Binder()); - - mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId); - - verify(mMockNetd) - .ipSecDeleteSecurityAssociation( - eq(spiResp.resourceId), anyInt(), anyString(), anyString(), eq(DROID_SPI)); - } - - @Test public void testReleaseInvalidSecurityParameterIndex() throws Exception { try { mIpSecService.releaseSecurityParameterIndex(1); @@ -285,108 +221,6 @@ public class IpSecServiceTest { } } - IpSecConfig buildIpSecConfig() throws Exception { - IpSecManager ipSecManager = new IpSecManager(mIpSecService); - - // Mocking the netd to allocate SPI - when(mMockNetd.ipSecAllocateSpi(anyInt(), anyInt(), anyString(), anyString(), anyInt())) - .thenReturn(DROID_SPI) - .thenReturn(DROID_SPI2); - - IpSecAlgorithm encryptAlgo = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY); - IpSecAlgorithm authAlgo = - new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 8); - - InetAddress localAddr = InetAddress.getByAddress(new byte[] {127, 0, 0, 1}); - - /** Allocate and add SPI records in the IpSecService through IpSecManager interface. */ - IpSecManager.SecurityParameterIndex outSpi = - ipSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, localAddr); - IpSecManager.SecurityParameterIndex inSpi = - ipSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_IN, localAddr); - - IpSecConfig ipSecConfig = - new IpSecTransform.Builder(mMockContext) - .setSpi(IpSecTransform.DIRECTION_OUT, outSpi) - .setSpi(IpSecTransform.DIRECTION_IN, inSpi) - .setEncryption(IpSecTransform.DIRECTION_OUT, encryptAlgo) - .setAuthentication(IpSecTransform.DIRECTION_OUT, authAlgo) - .setEncryption(IpSecTransform.DIRECTION_IN, encryptAlgo) - .setAuthentication(IpSecTransform.DIRECTION_IN, authAlgo) - .getIpSecConfig(); - return ipSecConfig; - } - - @Test - public void testCreateTransportModeTransform() throws Exception { - IpSecConfig ipSecConfig = buildIpSecConfig(); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); - assertEquals(IpSecManager.Status.OK, createTransformResp.status); - - verify(mMockNetd) - .ipSecAddSecurityAssociation( - eq(createTransformResp.resourceId), - anyInt(), - eq(IpSecTransform.DIRECTION_OUT), - anyString(), - anyString(), - anyLong(), - eq(DROID_SPI), - eq(IpSecAlgorithm.AUTH_HMAC_SHA256), - eq(AUTH_KEY), - anyInt(), - eq(IpSecAlgorithm.CRYPT_AES_CBC), - eq(CRYPT_KEY), - anyInt(), - anyInt(), - anyInt(), - anyInt()); - verify(mMockNetd) - .ipSecAddSecurityAssociation( - eq(createTransformResp.resourceId), - anyInt(), - eq(IpSecTransform.DIRECTION_IN), - anyString(), - anyString(), - anyLong(), - eq(DROID_SPI2), - eq(IpSecAlgorithm.AUTH_HMAC_SHA256), - eq(AUTH_KEY), - anyInt(), - eq(IpSecAlgorithm.CRYPT_AES_CBC), - eq(CRYPT_KEY), - anyInt(), - anyInt(), - anyInt(), - anyInt()); - } - - @Test - public void testDeleteTransportModeTransform() throws Exception { - IpSecConfig ipSecConfig = buildIpSecConfig(); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); - mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId); - - verify(mMockNetd) - .ipSecDeleteSecurityAssociation( - eq(createTransformResp.resourceId), - eq(IpSecTransform.DIRECTION_OUT), - anyString(), - anyString(), - eq(DROID_SPI)); - verify(mMockNetd) - .ipSecDeleteSecurityAssociation( - eq(createTransformResp.resourceId), - eq(IpSecTransform.DIRECTION_IN), - anyString(), - anyString(), - eq(DROID_SPI2)); - } - @Test public void testDeleteInvalidTransportModeTransform() throws Exception { try { @@ -397,39 +231,31 @@ public class IpSecServiceTest { } @Test - public void testApplyTransportModeTransform() throws Exception { - IpSecConfig ipSecConfig = buildIpSecConfig(); - - IpSecTransformResponse createTransformResp = - mIpSecService.createTransportModeTransform(ipSecConfig, new Binder()); - ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); - - int resourceId = createTransformResp.resourceId; - mIpSecService.applyTransportModeTransform(pfd, resourceId); - - verify(mMockNetd) - .ipSecApplyTransportModeTransform( - eq(pfd.getFileDescriptor()), - eq(resourceId), - eq(IpSecTransform.DIRECTION_OUT), - anyString(), - anyString(), - eq(DROID_SPI)); - verify(mMockNetd) - .ipSecApplyTransportModeTransform( - eq(pfd.getFileDescriptor()), - eq(resourceId), - eq(IpSecTransform.DIRECTION_IN), - anyString(), - anyString(), - eq(DROID_SPI2)); - } - - @Test public void testRemoveTransportModeTransform() throws Exception { ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket()); mIpSecService.removeTransportModeTransform(pfd, 1); verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor()); } + + @Test + public void testValidateIpAddresses() throws Exception { + String[] invalidAddresses = + new String[] {"www.google.com", "::", "2001::/64", "0.0.0.0", ""}; + for (String address : invalidAddresses) { + try { + IpSecSpiResponse spiResp = + mIpSecService.reserveSecurityParameterIndex( + IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder()); + fail("Invalid address was passed through IpSecService validation: " + address); + } catch (IllegalArgumentException e) { + } catch (Exception e) { + fail( + "Invalid InetAddress was not caught in validation: " + + address + + ", Exception: " + + e); + } + } + } } diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp index 10a45870e556..6b97ae67c363 100644 --- a/tools/aapt2/xml/XmlDom_test.cpp +++ b/tools/aapt2/xml/XmlDom_test.cpp @@ -18,6 +18,7 @@ #include <string> +#include "flatten/XmlFlattener.h" #include "io/StringInputStream.h" #include "test/Test.h" @@ -51,6 +52,36 @@ TEST(XmlDomTest, Inflate) { EXPECT_THAT(el->namespace_decls[0].prefix, StrEq("android")); } +TEST(XmlDomTest, BinaryInflate) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<XmlResource> doc = util::make_unique<XmlResource>(); + doc->root = util::make_unique<Element>(); + doc->root->name = "Layout"; + doc->root->line_number = 2u; + + NamespaceDecl decl; + decl.uri = kSchemaAndroid; + decl.prefix = "android"; + decl.line_number = 2u; + doc->root->namespace_decls.push_back(decl); + + BigBuffer buffer(4096); + XmlFlattener flattener(&buffer, {}); + ASSERT_TRUE(flattener.Consume(context.get(), doc.get())); + + auto block = util::Copy(buffer); + std::unique_ptr<XmlResource> new_doc = + Inflate(block.get(), buffer.size(), context->GetDiagnostics(), Source("test.xml")); + ASSERT_THAT(new_doc, NotNull()); + + EXPECT_THAT(new_doc->root->name, StrEq("Layout")); + EXPECT_THAT(new_doc->root->line_number, Eq(2u)); + ASSERT_THAT(new_doc->root->namespace_decls, SizeIs(1u)); + EXPECT_THAT(new_doc->root->namespace_decls[0].uri, StrEq(kSchemaAndroid)); + EXPECT_THAT(new_doc->root->namespace_decls[0].prefix, StrEq("android")); + EXPECT_THAT(new_doc->root->namespace_decls[0].line_number, Eq(2u)); +} + // Escaping is handled after parsing of the values for resource-specific values. TEST(XmlDomTest, ForwardEscapes) { std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"( |