summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/Android.bp30
-rw-r--r--core/java/android/accounts/OWNERS10
-rw-r--r--core/java/android/annotation/OWNERS1
-rw-r--r--core/java/android/annotation/RequiresFeature.java45
-rw-r--r--core/java/android/annotation/SystemService.java9
-rw-r--r--core/java/android/app/Activity.java41
-rw-r--r--core/java/android/app/ActivityManager.java25
-rw-r--r--core/java/android/app/ActivityManagerInternal.java13
-rw-r--r--core/java/android/app/ActivityOptions.java5
-rw-r--r--core/java/android/app/ActivityThread.java296
-rw-r--r--core/java/android/app/AppOpsManager.java1
-rw-r--r--core/java/android/app/ClientTransactionHandler.java36
-rw-r--r--core/java/android/app/ContextImpl.java3
-rw-r--r--core/java/android/app/Dialog.java25
-rw-r--r--core/java/android/app/IActivityManager.aidl32
-rw-r--r--core/java/android/app/IAlarmManager.aidl1
-rw-r--r--core/java/android/app/IApplicationThread.aidl3
-rw-r--r--core/java/android/app/LocalActivityManager.java4
-rw-r--r--core/java/android/app/Notification.java52
-rw-r--r--core/java/android/app/NotificationManager.java12
-rw-r--r--core/java/android/app/ProcessMemoryState.java88
-rw-r--r--core/java/android/app/StatsManager.java115
-rw-r--r--core/java/android/app/SystemServiceRegistry.java17
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java57
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java40
-rw-r--r--core/java/android/app/admin/FreezeInterval.java6
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/app/admin/SecurityLog.java33
-rw-r--r--core/java/android/app/admin/SecurityLogTags.logtags5
-rw-r--r--core/java/android/app/admin/SystemUpdatePolicy.java175
-rw-r--r--core/java/android/app/assist/AssistStructure.java42
-rw-r--r--core/java/android/app/backup/BackupAgent.java10
-rw-r--r--core/java/android/app/backup/OWNERS7
-rw-r--r--core/java/android/app/job/JobInfo.java4
-rw-r--r--core/java/android/app/servertransaction/ActivityLifecycleItem.java5
-rw-r--r--core/java/android/app/servertransaction/ActivityRelaunchItem.java176
-rw-r--r--core/java/android/app/servertransaction/DestroyActivityItem.java3
-rw-r--r--core/java/android/app/servertransaction/PauseActivityItem.java1
-rw-r--r--core/java/android/app/servertransaction/PendingTransactionActions.java19
-rw-r--r--core/java/android/app/servertransaction/ResumeActivityItem.java1
-rw-r--r--core/java/android/app/servertransaction/StopActivityItem.java1
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java4
-rw-r--r--core/java/android/app/timezone/RulesManager.java17
-rw-r--r--core/java/android/app/usage/UsageEvents.java14
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java3
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java6
-rw-r--r--core/java/android/bluetooth/BluetoothHearingAid.java693
-rw-r--r--core/java/android/bluetooth/BluetoothManager.java3
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java9
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java3
-rw-r--r--core/java/android/content/Context.java26
-rw-r--r--core/java/android/content/ContextWrapper.java8
-rw-r--r--core/java/android/content/Intent.java23
-rw-r--r--core/java/android/content/om/IOverlayManager.aidl25
-rw-r--r--core/java/android/content/om/OverlayInfo.java27
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java2
-rw-r--r--core/java/android/content/pm/InstantAppRequest.java10
-rw-r--r--core/java/android/content/pm/InstantAppResolveInfo.java50
-rw-r--r--core/java/android/content/pm/PackageInfo.java32
-rw-r--r--core/java/android/content/pm/PackageManager.java12
-rw-r--r--core/java/android/content/pm/PackageParser.java54
-rw-r--r--core/java/android/content/pm/VerifierDeviceIdentity.java3
-rw-r--r--core/java/android/content/pm/permission/RuntimePermissionPresenter.java1
-rw-r--r--core/java/android/content/res/AssetManager.java6
-rw-r--r--core/java/android/content/res/Resources.java17
-rw-r--r--core/java/android/content/res/ResourcesImpl.java52
-rw-r--r--core/java/android/content/res/XmlResourceParser.java2
-rw-r--r--core/java/android/database/sqlite/SQLiteConnectionPool.java12
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java1
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java29
-rw-r--r--core/java/android/hardware/Camera.java11
-rw-r--r--core/java/android/hardware/ConsumerIrManager.java3
-rw-r--r--core/java/android/hardware/OWNERS7
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java11
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java67
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java58
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java4
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java4
-rw-r--r--core/java/android/hardware/camera2/OWNERS6
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java8
-rw-r--r--core/java/android/hardware/display/AmbientBrightnessDayStats.java31
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java3
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java3
-rw-r--r--core/java/android/hardware/radio/RadioManager.java3
-rw-r--r--core/java/android/hardware/usb/UsbDeviceConnection.java14
-rw-r--r--core/java/android/hardware/usb/UsbManager.java10
-rw-r--r--core/java/android/hardware/usb/UsbRequest.java26
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java6
-rw-r--r--core/java/android/net/ConnectivityManager.java48
-rw-r--r--core/java/android/net/IpSecConfig.java19
-rw-r--r--core/java/android/net/IpSecManager.java3
-rw-r--r--core/java/android/net/IpSecTransform.java28
-rw-r--r--core/java/android/net/MacAddress.java29
-rw-r--r--core/java/android/net/NetworkCapabilities.java30
-rw-r--r--core/java/android/net/OWNERS2
-rw-r--r--core/java/android/net/Uri.java19
-rw-r--r--core/java/android/os/BatteryManager.java12
-rw-r--r--core/java/android/os/BatteryStats.java13
-rw-r--r--core/java/android/os/BestClock.java58
-rw-r--r--core/java/android/os/Binder.java26
-rw-r--r--core/java/android/os/ChildZygoteProcess.java44
-rw-r--r--core/java/android/os/HwBinder.java28
-rw-r--r--core/java/android/os/IHwBinder.java6
-rw-r--r--core/java/android/os/IStatsCompanionService.aidl5
-rw-r--r--core/java/android/os/IStatsManager.aidl20
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/SimpleClock.java53
-rw-r--r--core/java/android/os/SystemClock.java107
-rw-r--r--core/java/android/os/UpdateEngine.java18
-rw-r--r--core/java/android/os/UserManager.java26
-rw-r--r--core/java/android/os/VibrationEffect.java103
-rw-r--r--core/java/android/os/VintfObject.java7
-rw-r--r--core/java/android/os/ZygoteProcess.java91
-rw-r--r--core/java/android/os/storage/StorageResultCode.java75
-rw-r--r--core/java/android/preference/OWNERS2
-rw-r--r--core/java/android/print/PrintManager.java3
-rw-r--r--core/java/android/provider/Settings.java150
-rw-r--r--core/java/android/provider/SettingsSlicesContract.java4
-rw-r--r--core/java/android/security/IConfirmationPromptCallback.aidl27
-rw-r--r--core/java/android/security/IKeystoreService.aidl87
-rw-r--r--core/java/android/security/KeystoreArguments.aidl20
-rw-r--r--core/java/android/security/keymaster/ExportResult.aidl20
-rw-r--r--core/java/android/security/keymaster/KeyCharacteristics.aidl20
-rw-r--r--core/java/android/security/keymaster/KeymasterArguments.aidl20
-rw-r--r--core/java/android/security/keymaster/KeymasterBlob.aidl20
-rw-r--r--core/java/android/security/keymaster/KeymasterCertificateChain.aidl20
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java3
-rw-r--r--core/java/android/security/keymaster/OperationResult.aidl20
-rw-r--r--core/java/android/security/keystore/OWNERS4
-rw-r--r--core/java/android/service/autofill/AutofillServiceInfo.java45
-rw-r--r--core/java/android/service/autofill/DateTransformation.java2
-rw-r--r--core/java/android/service/autofill/DateValueSanitizer.java2
-rw-r--r--core/java/android/service/autofill/FieldClassification.java18
-rw-r--r--core/java/android/service/autofill/FillContext.java24
-rw-r--r--core/java/android/service/autofill/UserData.java155
-rw-r--r--core/java/android/service/euicc/EuiccProfileInfo.java273
-rw-r--r--core/java/android/service/euicc/EuiccService.java67
-rw-r--r--core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java38
-rw-r--r--core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java34
-rw-r--r--core/java/android/service/euicc/GetEuiccProfileInfoListResult.java53
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java1
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java4
-rw-r--r--core/java/android/speech/tts/SynthesisPlaybackQueueItem.java22
-rw-r--r--core/java/android/text/OWNERS3
-rw-r--r--core/java/android/text/style/TypefaceSpan.java109
-rw-r--r--core/java/android/text/util/Linkify.java14
-rw-r--r--core/java/android/transition/ArcMotion.java10
-rw-r--r--core/java/android/transition/TransitionUtils.java25
-rw-r--r--core/java/android/util/AttributeSet.java30
-rw-r--r--core/java/android/util/ByteStringUtils.java92
-rw-r--r--core/java/android/util/ExceptionUtils.java14
-rw-r--r--core/java/android/util/FeatureFlagUtils.java4
-rw-r--r--core/java/android/util/MemoryIntArray.java51
-rw-r--r--core/java/android/util/OWNERS2
-rw-r--r--core/java/android/util/StatsLog.java7
-rw-r--r--core/java/android/util/StatsManager.java239
-rw-r--r--core/java/android/util/XmlPullAttributes.java4
-rw-r--r--core/java/android/view/DisplayCutout.java10
-rw-r--r--core/java/android/view/DisplayInfo.java2
-rw-r--r--core/java/android/view/IRecentsAnimationRunner.aidl18
-rw-r--r--core/java/android/view/RemoteAnimationTarget.java12
-rw-r--r--core/java/android/view/RenderNodeAnimator.java2
-rw-r--r--core/java/android/view/ThreadedRenderer.java9
-rw-r--r--core/java/android/view/View.java86
-rw-r--r--core/java/android/view/ViewDebug.java21
-rw-r--r--core/java/android/view/ViewGroup.java35
-rw-r--r--core/java/android/view/ViewStructure.java8
-rw-r--r--core/java/android/view/WindowInfo.java7
-rw-r--r--core/java/android/view/WindowManager.java7
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java5
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java15
-rw-r--r--core/java/android/view/animation/AnimationUtils.java34
-rw-r--r--core/java/android/view/autofill/AutofillId.java1
-rw-r--r--core/java/android/view/autofill/AutofillManager.java171
-rw-r--r--core/java/android/view/autofill/AutofillPopupWindow.java19
-rw-r--r--core/java/android/view/autofill/IAutoFillManager.aidl4
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java3
-rw-r--r--core/java/android/view/textclassifier/SmartSelection.java4
-rw-r--r--core/java/android/view/textclassifier/SystemTextClassifier.java9
-rw-r--r--core/java/android/view/textclassifier/TextClassification.java6
-rw-r--r--core/java/android/view/textclassifier/TextClassifier.java27
-rw-r--r--core/java/android/view/textclassifier/TextClassifierConstants.java36
-rw-r--r--core/java/android/view/textclassifier/TextClassifierImpl.java301
-rw-r--r--core/java/android/view/textclassifier/TextLinks.java7
-rw-r--r--core/java/android/view/textclassifier/logging/DefaultLogger.java49
-rw-r--r--core/java/android/view/textclassifier/logging/Logger.java21
-rw-r--r--core/java/android/view/textclassifier/logging/SelectionEvent.java21
-rw-r--r--core/java/android/webkit/TracingConfig.java259
-rw-r--r--core/java/android/webkit/TracingController.java78
-rw-r--r--core/java/android/webkit/TracingFileOutputStream.java63
-rw-r--r--core/java/android/widget/Editor.java14
-rw-r--r--core/java/android/widget/GridLayout.java6
-rw-r--r--core/java/android/widget/LinearLayout.java4
-rw-r--r--core/java/android/widget/Magnifier.java44
-rw-r--r--core/java/android/widget/OWNERS5
-rw-r--r--core/java/android/widget/PopupWindow.java2
-rw-r--r--core/java/android/widget/RadioGroup.java10
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java21
-rw-r--r--core/java/android/widget/TextInputTimePickerView.java7
-rw-r--r--core/java/android/widget/TextView.java24
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java307
-rw-r--r--core/java/com/android/internal/app/ResolverListController.java75
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java20
-rw-r--r--core/java/com/android/internal/backup/LocalTransportParameters.java77
-rw-r--r--core/java/com/android/internal/backup/LocalTransportService.java10
-rw-r--r--core/java/com/android/internal/car/ICarServiceHelper.aidl24
-rw-r--r--core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java7
-rw-r--r--core/java/com/android/internal/colorextraction/types/Tonal.java13
-rw-r--r--core/java/com/android/internal/notification/SystemNotificationChannels.java6
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java347
-rw-r--r--core/java/com/android/internal/os/FuseAppLoop.java3
-rw-r--r--core/java/com/android/internal/os/KernelSingleUidTimeReader.java38
-rw-r--r--core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java2
-rw-r--r--core/java/com/android/internal/os/KernelUidCpuTimeReader.java12
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java11
-rw-r--r--core/java/com/android/internal/os/WebViewZygoteInit.java2
-rw-r--r--core/java/com/android/internal/os/Zygote.java26
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java43
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java25
-rw-r--r--core/java/com/android/internal/os/ZygoteServer.java41
-rw-r--r--core/java/com/android/internal/print/DumpUtils.java3
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl2
-rw-r--r--core/java/com/android/internal/util/OWNERS24
-rw-r--r--core/java/com/android/internal/view/menu/MenuBuilder.java1
-rw-r--r--core/java/com/android/internal/widget/LocalImageResolver.java76
-rw-r--r--core/java/com/android/internal/widget/MessagingGroup.java161
-rw-r--r--core/java/com/android/internal/widget/MessagingImageMessage.java273
-rw-r--r--core/java/com/android/internal/widget/MessagingLayout.java8
-rw-r--r--core/java/com/android/internal/widget/MessagingLinearLayout.java9
-rw-r--r--core/java/com/android/internal/widget/MessagingMessage.java187
-rw-r--r--core/java/com/android/internal/widget/MessagingMessageState.java81
-rw-r--r--core/java/com/android/internal/widget/MessagingTextMessage.java145
-rw-r--r--core/java/com/android/internal/widget/VerifyCredentialResponse.java2
-rw-r--r--core/java/com/android/internal/widget/ViewPager.java20
-rw-r--r--core/java/com/android/server/BootReceiver.java20
235 files changed, 6214 insertions, 2878 deletions
diff --git a/core/java/Android.bp b/core/java/Android.bp
index f7c5c57a07e4..fb27f74211fb 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -7,33 +7,3 @@ filegroup {
name: "IDropBoxManagerService.aidl",
srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
}
-
-// only used by key_store_service
-cc_library_shared {
- name: "libkeystore_aidl",
- srcs: ["android/security/IKeystoreService.aidl",
- "android/security/IConfirmationPromptCallback.aidl"],
- aidl: {
- export_aidl_headers: true,
- include_dirs: [
- "frameworks/base/core/java/",
- "system/security/keystore/",
- ],
- },
- shared_libs: [
- "libbinder",
- "libcutils",
- "libhardware",
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "liblog",
- "libkeystore_parcelables",
- "libselinux",
- "libutils",
- ],
- export_shared_lib_headers: [
- "libbinder",
- "libkeystore_parcelables",
- ],
-}
diff --git a/core/java/android/accounts/OWNERS b/core/java/android/accounts/OWNERS
new file mode 100644
index 000000000000..ea5fd36702f9
--- /dev/null
+++ b/core/java/android/accounts/OWNERS
@@ -0,0 +1,10 @@
+carlosvaldivia@google.com
+dementyev@google.com
+sandrakwan@google.com
+hackbod@google.com
+svetoslavganov@google.com
+moltmann@google.com
+fkupolov@google.com
+yamasani@google.com
+omakoto@google.com
+
diff --git a/core/java/android/annotation/OWNERS b/core/java/android/annotation/OWNERS
new file mode 100644
index 000000000000..d6bb71b50e34
--- /dev/null
+++ b/core/java/android/annotation/OWNERS
@@ -0,0 +1 @@
+tnorbye@google.com
diff --git a/core/java/android/annotation/RequiresFeature.java b/core/java/android/annotation/RequiresFeature.java
new file mode 100644
index 000000000000..fc93f03d76cf
--- /dev/null
+++ b/core/java/android/annotation/RequiresFeature.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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 static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.content.pm.PackageManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated element requires one or more device features. This
+ * is used to auto-generate documentation.
+ *
+ * @see PackageManager#hasSystemFeature(String)
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({TYPE,FIELD,METHOD,CONSTRUCTOR})
+public @interface RequiresFeature {
+ /**
+ * The name of the device feature that is required.
+ *
+ * @see PackageManager#hasSystemFeature(String)
+ */
+ String value();
+}
diff --git a/core/java/android/annotation/SystemService.java b/core/java/android/annotation/SystemService.java
index ba5002a4f1b5..0c5d15e178a3 100644
--- a/core/java/android/annotation/SystemService.java
+++ b/core/java/android/annotation/SystemService.java
@@ -26,12 +26,19 @@ import java.lang.annotation.Target;
/**
* Description of a system service available through
- * {@link Context#getSystemService(Class)}.
+ * {@link Context#getSystemService(Class)}. This is used to auto-generate
+ * documentation explaining how to obtain a reference to the service.
*
* @hide
*/
@Retention(SOURCE)
@Target(TYPE)
public @interface SystemService {
+ /**
+ * The string name of the system service that can be passed to
+ * {@link Context#getSystemService(String)}.
+ *
+ * @see Context#getSystemServiceName(Class)
+ */
String value();
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 0bc510a13ba6..83fe4dd0038a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -17,6 +17,7 @@
package android.app;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+
import static java.lang.Character.MIN_VALUE;
import android.annotation.CallSuper;
@@ -135,6 +136,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -1733,7 +1735,7 @@ public class Activity extends ContextThemeWrapper
*
* <p>This callback and {@link #onUserInteraction} are intended to help
* activities manage status bar notifications intelligently; specifically,
- * for helping activities determine the proper time to cancel a notfication.
+ * for helping activities determine the proper time to cancel a notification.
*
* @see #onUserInteraction()
*/
@@ -1741,32 +1743,16 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Generate a new thumbnail for this activity. This method is called before
- * pausing the activity, and should draw into <var>outBitmap</var> the
- * imagery for the desired thumbnail in the dimensions of that bitmap. It
- * can use the given <var>canvas</var>, which is configured to draw into the
- * bitmap, for rendering if desired.
- *
- * <p>The default implementation returns fails and does not draw a thumbnail;
- * this will result in the platform creating its own thumbnail if needed.
- *
- * @param outBitmap The bitmap to contain the thumbnail.
- * @param canvas Can be used to render into the bitmap.
- *
- * @return Return true if you have drawn into the bitmap; otherwise after
- * you return it will be filled with a default thumbnail.
- *
- * @see #onCreateDescription
- * @see #onSaveInstanceState
- * @see #onPause
+ * @deprecated Method doesn't do anything and will be removed in the future.
*/
+ @Deprecated
public boolean onCreateThumbnail(Bitmap outBitmap, Canvas canvas) {
return false;
}
/**
* Generate a new description for this activity. This method is called
- * before pausing the activity and can, if desired, return some textual
+ * before stopping the activity and can, if desired, return some textual
* description of its current state to be displayed to the user.
*
* <p>The default implementation returns null, which will cause you to
@@ -1777,9 +1763,8 @@ public class Activity extends ContextThemeWrapper
* @return A description of what the user is doing. It should be short and
* sweet (only a few words).
*
- * @see #onCreateThumbnail
* @see #onSaveInstanceState
- * @see #onPause
+ * @see #onStop
*/
@Nullable
public CharSequence onCreateDescription() {
@@ -1915,7 +1900,7 @@ public class Activity extends ContextThemeWrapper
if (isFinishing()) {
if (mAutoFillResetNeeded) {
- getAutofillManager().onActivityFinished();
+ getAutofillManager().onActivityFinishing();
} else if (mIntent != null
&& mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
// Activity was launched when user tapped a link in the Autofill Save UI - since
@@ -6358,6 +6343,8 @@ public class Activity extends ContextThemeWrapper
final AutofillManager afm = getAutofillManager();
if (afm != null) {
+ writer.print(prefix); writer.print("Autofill Compat Mode: ");
+ writer.println(isAutofillCompatibilityEnabled());
afm.dump(prefix, writer);
} else {
writer.print(prefix); writer.println("No AutofillManager");
@@ -7179,8 +7166,8 @@ public class Activity extends ContextThemeWrapper
String appName = getApplicationInfo().loadLabel(getPackageManager())
.toString();
- String warning = "Detected problems with API compatiblity\n"
- + "(please consult log for detail)";
+ String warning = "Detected problems with API compatibility\n"
+ + "(visit g.co/dev/appcompat for more info)";
if (isAppDebuggable) {
new AlertDialog.Builder(this)
.setTitle(appName)
@@ -7706,6 +7693,9 @@ public class Activity extends ContextThemeWrapper
}
}
}
+ if (android.view.autofill.Helper.sVerbose) {
+ Log.v(TAG, "autofillClientGetViewVisibility(): " + Arrays.toString(visible));
+ }
return visible;
}
@@ -7768,7 +7758,6 @@ public class Activity extends ContextThemeWrapper
* @param disable {@code true} to disable preview screenshots; {@code false} otherwise.
* @hide
*/
- @SystemApi
public void setDisablePreviewScreenshots(boolean disable) {
try {
ActivityManager.getService().setDisablePreviewScreenshots(mToken, disable);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ae47a684f6ab..03faeeeb91a1 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -44,6 +44,7 @@ import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
@@ -2750,6 +2751,30 @@ public class ActivityManager {
}
/**
+ * Updates (grants or revokes) a persitable URI permission.
+ *
+ * @param uri URI to be granted or revoked.
+ * @param prefix if {@code false}, permission apply to this specific URI; if {@code true}, it
+ * applies to all URIs that are prefixed by this URI.
+ * @param packageName target package.
+ * @param grant if {@code true} a new permission will be granted, otherwise an existing
+ * permission will be revoked.
+ *
+ * @return whether or not the requested succeeded.
+ *
+ * @hide
+ */
+ public boolean updatePersistableUriPermission(Uri uri, boolean prefix, String packageName,
+ boolean grant) {
+ try {
+ return getService().updatePersistableUriPermission(uri, prefix, packageName, grant,
+ UserHandle.myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Information you can retrieve about any processes that are in an error condition.
*/
public static class ProcessErrorStateInfo implements Parcelable {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 5ee7edee9db7..0c98267cfd68 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -219,6 +219,9 @@ public abstract class ActivityManagerInternal {
/**
* Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
*
+ * - DO NOT call it with the calling UID cleared.
+ * - All the necessary caller permission checks must be done at callsites.
+ *
* @return error codes used by {@link IActivityManager#startActivity} and its siblings.
*/
public abstract int startActivitiesAsPackage(String packageName,
@@ -348,4 +351,14 @@ public abstract class ActivityManagerInternal {
* Returns is the caller has the same uid as the Recents component
*/
public abstract boolean isCallerRecents(int callingUid);
+
+ /**
+ * Whether an UID is active or idle.
+ */
+ public abstract boolean isUidActive(int uid);
+
+ /**
+ * Returns a list that contains the memory stats for currently running processes.
+ */
+ public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index fee58274a5fc..d5430f05d65b 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1106,6 +1106,11 @@ public class ActivityOptions {
}
/** @hide */
+ public void setRemoteAnimationAdapter(RemoteAnimationAdapter remoteAnimationAdapter) {
+ mRemoteAnimationAdapter = remoteAnimationAdapter;
+ }
+
+ /** @hide */
public static ActivityOptions fromBundle(Bundle bOptions) {
return bOptions != null ? new ActivityOptions(bOptions) : null;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 45c57ca67c87..a96f484ccf11 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -113,6 +113,7 @@ import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.LogPrinter;
+import android.util.MergedConfiguration;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
@@ -166,9 +167,9 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.text.DateFormat;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -204,7 +205,7 @@ public final class ActivityThread extends ClientTransactionHandler {
private static final boolean DEBUG_SERVICE = false;
public static final boolean DEBUG_MEMORY_TRIM = false;
private static final boolean DEBUG_PROVIDER = false;
- private static final boolean DEBUG_ORDER = false;
+ public static final boolean DEBUG_ORDER = false;
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
private static final int LOG_AM_ON_PAUSE_CALLED = 30021;
@@ -222,7 +223,7 @@ public final class ActivityThread extends ClientTransactionHandler {
private static final boolean REPORT_TO_ACTIVITY = true;
// Maximum number of recent tokens to maintain for debugging purposes
- private static final int MAX_RECENT_TOKENS = 10;
+ private static final int MAX_DESTROYED_ACTIVITIES = 10;
/**
* Denotes an invalid sequence number corresponding to a process state change.
@@ -256,7 +257,7 @@ public final class ActivityThread extends ClientTransactionHandler {
final H mH = new H();
final Executor mExecutor = new HandlerExecutor(mH);
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
- final ArrayDeque<Integer> mRecentTokens = new ArrayDeque<>();
+ final ArrayList<DestroyedActivityInfo> mRecentDestroyedActivities = new ArrayList<>();
// List of new activities (via ActivityRecord.nextIdle) that should
// be reported when next we idle.
@@ -339,6 +340,26 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
+ /**
+ * TODO(b/71506345): Remove this once bug is resolved.
+ */
+ private static final class DestroyedActivityInfo {
+ private final Integer mToken;
+ private final String mReason;
+ private final long mTime;
+
+ DestroyedActivityInfo(Integer token, String reason) {
+ mToken = token;
+ mReason = reason;
+ mTime = System.currentTimeMillis();
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "[token:" + mToken + " | time:" + mTime + " | reason:" + mReason
+ + "]");
+ }
+ }
+
// The lock of mProviderMap protects the following variables.
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
= new ArrayMap<ProviderKey, ProviderClientRecord>();
@@ -398,7 +419,6 @@ public final class ActivityThread extends ClientTransactionHandler {
boolean startsNotResumed;
public final boolean isForward;
int pendingConfigChanges;
- boolean onlyLocalRequest;
Window mPendingRemoveWindow;
WindowManager mPendingRemoveWindowManager;
@@ -520,7 +540,6 @@ public final class ActivityThread extends ClientTransactionHandler {
sb.append(", startsNotResumed=").append(startsNotResumed);
sb.append(", isForward=").append(isForward);
sb.append(", pendingConfigChanges=").append(pendingConfigChanges);
- sb.append(", onlyLocalRequest=").append(onlyLocalRequest);
sb.append(", preserveWindow=").append(mPreserveWindow);
if (activity != null) {
sb.append(", Activity{");
@@ -765,15 +784,6 @@ public final class ActivityThread extends ClientTransactionHandler {
sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
}
- @Override
- public final void scheduleRelaunchActivity(IBinder token,
- List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig, boolean preserveWindow) {
- requestRelaunchActivity(token, pendingResults, pendingNewIntents,
- configChanges, notResumed, config, overrideConfig, true, preserveWindow);
- }
-
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
@@ -1531,7 +1541,6 @@ public final class ActivityThread extends ClientTransactionHandler {
public static final int UNBIND_SERVICE = 122;
public static final int DUMP_SERVICE = 123;
public static final int LOW_MEMORY = 124;
- public static final int RELAUNCH_ACTIVITY = 126;
public static final int PROFILER_CONTROL = 127;
public static final int CREATE_BACKUP_AGENT = 128;
public static final int DESTROY_BACKUP_AGENT = 129;
@@ -1577,7 +1586,6 @@ public final class ActivityThread extends ClientTransactionHandler {
case UNBIND_SERVICE: return "UNBIND_SERVICE";
case DUMP_SERVICE: return "DUMP_SERVICE";
case LOW_MEMORY: return "LOW_MEMORY";
- case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
case PROFILER_CONTROL: return "PROFILER_CONTROL";
case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
@@ -1611,12 +1619,6 @@ public final class ActivityThread extends ClientTransactionHandler {
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
- case RELAUNCH_ACTIVITY: {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
- ActivityClientRecord r = (ActivityClientRecord)msg.obj;
- handleRelaunchActivity(r);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } break;
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
@@ -2182,14 +2184,28 @@ public final class ActivityThread extends ClientTransactionHandler {
@Override
public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "mActivities:");
+ pw.println(prefix + "Activities:");
- for (ArrayMap.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
- pw.println(prefix + " [token:" + entry.getKey().hashCode() + " record:"
- + entry.getValue().toString() + "]");
+ if (!mActivities.isEmpty()) {
+ final Iterator<Map.Entry<IBinder, ActivityClientRecord>> activitiesIterator =
+ mActivities.entrySet().iterator();
+
+ while (activitiesIterator.hasNext()) {
+ final ArrayMap.Entry<IBinder, ActivityClientRecord> entry =
+ activitiesIterator.next();
+ pw.println(prefix + " [token:" + entry.getKey().hashCode() + " record:"
+ + entry.getValue().toString() + "]");
+ }
}
- pw.println(prefix + "mRecentTokens:" + mRecentTokens);
+ if (!mRecentDestroyedActivities.isEmpty()) {
+ pw.println(prefix + "Recent destroyed activities:");
+ for (int i = 0, size = mRecentDestroyedActivities.size(); i < size; i++) {
+ final DestroyedActivityInfo info = mRecentDestroyedActivities.get(i);
+ pw.print(prefix);
+ info.dump(pw, " ");
+ }
+ }
}
public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
@@ -2876,11 +2892,6 @@ public final class ActivityThread extends ClientTransactionHandler {
r.setState(ON_CREATE);
mActivities.put(r.token, r);
- mRecentTokens.push(r.token.hashCode());
-
- if (mRecentTokens.size() > MAX_RECENT_TOKENS) {
- mRecentTokens.removeLast();
- }
} catch (SuperNotCalledException e) {
throw e;
@@ -3726,20 +3737,6 @@ public final class ActivityThread extends ClientTransactionHandler {
}
r.activity.performResume(r.startsNotResumed);
- synchronized (mResourcesManager) {
- // If there is a pending local relaunch that was requested when the activity was
- // paused, it will put the activity into paused state when it finally happens.
- // Since the activity resumed before being relaunched, we don't want that to
- // happen, so we need to clear the request to relaunch paused.
- for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) {
- final ActivityClientRecord relaunching = mRelaunchingActivities.get(i);
- if (relaunching.token == r.token
- && relaunching.onlyLocalRequest && relaunching.startsNotResumed) {
- relaunching.startsNotResumed = false;
- }
- }
- }
-
EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED, UserHandle.myUserId(),
r.activity.getComponentName().getClassName(), reason);
@@ -3888,14 +3885,12 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
- if (!r.onlyLocalRequest) {
- r.nextIdle = mNewActivities;
- mNewActivities = r;
- if (localLOGV) Slog.v(
- TAG, "Scheduling idle handler for " + r);
- Looper.myQueue().addIdleHandler(new Idler());
+ r.nextIdle = mNewActivities;
+ mNewActivities = r;
+ if (localLOGV) {
+ Slog.v(TAG, "Scheduling idle handler for " + r);
}
- r.onlyLocalRequest = false;
+ Looper.myQueue().addIdleHandler(new Idler());
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
@@ -3909,62 +3904,6 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
- private int mThumbnailWidth = -1;
- private int mThumbnailHeight = -1;
- private Bitmap mAvailThumbnailBitmap = null;
- private Canvas mThumbnailCanvas = null;
-
- private Bitmap createThumbnailBitmap(ActivityClientRecord r) {
- Bitmap thumbnail = mAvailThumbnailBitmap;
- try {
- if (thumbnail == null) {
- int w = mThumbnailWidth;
- int h;
- if (w < 0) {
- Resources res = r.activity.getResources();
- int wId = com.android.internal.R.dimen.thumbnail_width;
- int hId = com.android.internal.R.dimen.thumbnail_height;
- mThumbnailWidth = w = res.getDimensionPixelSize(wId);
- mThumbnailHeight = h = res.getDimensionPixelSize(hId);
- } else {
- h = mThumbnailHeight;
- }
-
- // On platforms where we don't want thumbnails, set dims to (0,0)
- if ((w > 0) && (h > 0)) {
- thumbnail = Bitmap.createBitmap(r.activity.getResources().getDisplayMetrics(),
- w, h, THUMBNAIL_FORMAT);
- thumbnail.eraseColor(0);
- }
- }
-
- if (thumbnail != null) {
- Canvas cv = mThumbnailCanvas;
- if (cv == null) {
- mThumbnailCanvas = cv = new Canvas();
- }
-
- cv.setBitmap(thumbnail);
- if (!r.activity.onCreateThumbnail(thumbnail, cv)) {
- mAvailThumbnailBitmap = thumbnail;
- thumbnail = null;
- }
- cv.setBitmap(null);
- }
-
- } catch (Exception e) {
- if (!mInstrumentation.onException(r.activity, e)) {
- throw new RuntimeException(
- "Unable to create thumbnail of "
- + r.intent.getComponent().toShortString()
- + ": " + e.toString(), e);
- }
- thumbnail = null;
- }
-
- return thumbnail;
- }
-
@Override
public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
int configChanges, boolean dontReport, PendingTransactionActions pendingActions) {
@@ -4453,7 +4392,7 @@ public final class ActivityThread extends ClientTransactionHandler {
/** Core implementation of activity destroy call. */
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
- int configChanges, boolean getNonConfigInstance) {
+ int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
@@ -4505,6 +4444,12 @@ public final class ActivityThread extends ClientTransactionHandler {
r.setState(ON_DESTROY);
}
mActivities.remove(token);
+ mRecentDestroyedActivities.add(0, new DestroyedActivityInfo(token.hashCode(), reason));
+
+ final int recentDestroyedActivitiesSize = mRecentDestroyedActivities.size();
+ if (recentDestroyedActivitiesSize > MAX_DESTROYED_ACTIVITIES) {
+ mRecentDestroyedActivities.remove(recentDestroyedActivitiesSize - 1);
+ }
StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}
@@ -4516,9 +4461,9 @@ public final class ActivityThread extends ClientTransactionHandler {
@Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
- boolean getNonConfigInstance) {
+ boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = performDestroyActivity(token, finishing,
- configChanges, getNonConfigInstance);
+ configChanges, getNonConfigInstance, reason);
if (r != null) {
cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
@@ -4586,15 +4531,12 @@ public final class ActivityThread extends ClientTransactionHandler {
mSomeActivitiesChanged = true;
}
- /**
- * @param preserveWindow Whether the activity should try to reuse the window it created,
- * including the decor view after the relaunch.
- */
- public final void requestRelaunchActivity(IBinder token,
+ @Override
+ public ActivityClientRecord prepareRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig, boolean fromServer, boolean preserveWindow) {
+ int configChanges, MergedConfiguration config, boolean preserveWindow) {
ActivityClientRecord target = null;
+ boolean scheduleRelaunch = false;
synchronized (mResourcesManager) {
for (int i=0; i<mRelaunchingActivities.size(); i++) {
@@ -4616,57 +4558,31 @@ public final class ActivityThread extends ClientTransactionHandler {
r.pendingIntents = pendingNewIntents;
}
}
-
- // For each relaunch request, activity manager expects an answer
- if (!r.onlyLocalRequest && fromServer) {
- try {
- ActivityManager.getService().activityRelaunched(token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
break;
}
}
if (target == null) {
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null, fromServer:"
- + fromServer);
+ if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null");
target = new ActivityClientRecord();
target.token = token;
target.pendingResults = pendingResults;
target.pendingIntents = pendingNewIntents;
target.mPreserveWindow = preserveWindow;
- if (!fromServer) {
- final ActivityClientRecord existing = mActivities.get(token);
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + existing);
- if (existing != null) {
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: paused= "
- + existing.paused);;
- target.startsNotResumed = existing.paused;
- target.overrideConfig = existing.overrideConfig;
- }
- target.onlyLocalRequest = true;
- }
mRelaunchingActivities.add(target);
- sendMessage(H.RELAUNCH_ACTIVITY, target);
- }
-
- if (fromServer) {
- target.startsNotResumed = notResumed;
- target.onlyLocalRequest = false;
- }
- if (config != null) {
- target.createdConfig = config;
- }
- if (overrideConfig != null) {
- target.overrideConfig = overrideConfig;
+ scheduleRelaunch = true;
}
+ target.createdConfig = config.getGlobalConfiguration();
+ target.overrideConfig = config.getOverrideConfiguration();
target.pendingConfigChanges |= configChanges;
}
+
+ return scheduleRelaunch ? target : null;
}
- private void handleRelaunchActivity(ActivityClientRecord tmp) {
+ @Override
+ public void handleRelaunchActivity(ActivityClientRecord tmp,
+ PendingTransactionActions pendingActions) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
@@ -4735,18 +4651,10 @@ public final class ActivityThread extends ClientTransactionHandler {
ActivityClientRecord r = mActivities.get(tmp.token);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handling relaunch of " + r);
if (r == null) {
- if (!tmp.onlyLocalRequest) {
- try {
- ActivityManager.getService().activityRelaunched(tmp.token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
return;
}
r.activity.mConfigChangeFlags |= configChanges;
- r.onlyLocalRequest = tmp.onlyLocalRequest;
r.mPreserveWindow = tmp.mPreserveWindow;
r.activity.mChangingConfigurations = true;
@@ -4763,9 +4671,9 @@ public final class ActivityThread extends ClientTransactionHandler {
// preserved by the server, so we want to notify it that we are preparing to replace
// everything
try {
- if (r.mPreserveWindow || r.onlyLocalRequest) {
+ if (r.mPreserveWindow) {
WindowManagerGlobal.getWindowSession().prepareToReplaceWindows(
- r.token, !r.onlyLocalRequest);
+ r.token, true /* childrenOnly */);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -4780,7 +4688,7 @@ public final class ActivityThread extends ClientTransactionHandler {
callActivityOnStop(r, true /* saveState */, "handleRelaunchActivity");
}
- handleDestroyActivity(r.token, false, configChanges, true);
+ handleDestroyActivity(r.token, false, configChanges, true, "handleRelaunchActivity");
r.activity = null;
r.window = null;
@@ -4804,24 +4712,22 @@ public final class ActivityThread extends ClientTransactionHandler {
r.startsNotResumed = tmp.startsNotResumed;
r.overrideConfig = tmp.overrideConfig;
- // TODO(lifecycler): Move relaunch to lifecycler.
- PendingTransactionActions pendingActions = new PendingTransactionActions();
handleLaunchActivity(r, pendingActions);
- handleStartActivity(r, pendingActions);
- handleResumeActivity(r.token, false /* clearHide */, r.isForward, "relaunch");
- if (r.startsNotResumed) {
- performPauseActivity(r, false /* finished */, "relaunch", pendingActions);
- }
+ // Only report a successful relaunch to WindowManager.
+ pendingActions.setReportRelaunchToWindowManager(true);
+ }
- if (!tmp.onlyLocalRequest) {
- try {
- ActivityManager.getService().activityRelaunched(r.token);
- if (r.window != null) {
- r.window.reportActivityRelaunched();
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ @Override
+ public void reportRelaunch(IBinder token, PendingTransactionActions pendingActions) {
+ try {
+ ActivityManager.getService().activityRelaunched(token);
+ final ActivityClientRecord r = mActivities.get(token);
+ if (pendingActions.shouldReportRelaunchToWindowManager() && r != null
+ && r.window != null) {
+ r.window.reportActivityRelaunched();
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -5897,21 +5803,23 @@ public final class ActivityThread extends ClientTransactionHandler {
// Preload fonts resources
FontsContract.setApplicationContextForResources(appContext);
- try {
- final ApplicationInfo info =
- getPackageManager().getApplicationInfo(
- data.appInfo.packageName,
- PackageManager.GET_META_DATA /*flags*/,
- UserHandle.myUserId());
- if (info.metaData != null) {
- final int preloadedFontsResource = info.metaData.getInt(
- ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
- if (preloadedFontsResource != 0) {
- data.info.getResources().preloadFonts(preloadedFontsResource);
+ if (!Process.isIsolated()) {
+ try {
+ final ApplicationInfo info =
+ getPackageManager().getApplicationInfo(
+ data.appInfo.packageName,
+ PackageManager.GET_META_DATA /*flags*/,
+ UserHandle.myUserId());
+ if (info.metaData != null) {
+ final int preloadedFontsResource = info.metaData.getInt(
+ ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
+ if (preloadedFontsResource != 0) {
+ data.info.getResources().preloadFonts(preloadedFontsResource);
+ }
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4c9fb74c9c80..1026550b9b6b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -543,7 +543,6 @@ public class AppOpsManager {
OP_CAMERA,
// Body sensors
OP_BODY_SENSORS,
- OP_REQUEST_DELETE_PACKAGES,
// APPOP PERMISSIONS
OP_ACCESS_NOTIFICATIONS,
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 5b61fdf8677f..310965e475b8 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -21,6 +21,7 @@ import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
+import android.util.MergedConfiguration;
import com.android.internal.content.ReferrerIntent;
@@ -60,7 +61,7 @@ public abstract class ClientTransactionHandler {
/** Destroy the activity. */
public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
- boolean getNonConfigInstance);
+ boolean getNonConfigInstance, String reason);
/** Pause the activity. */
public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
@@ -124,6 +125,39 @@ public abstract class ClientTransactionHandler {
public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token);
/**
+ * Prepare activity relaunch to update internal bookkeeping. This is used to track multiple
+ * relaunch and config update requests.
+ * @param token Activity token.
+ * @param pendingResults Activity results to be delivered.
+ * @param pendingNewIntents New intent messages to be delivered.
+ * @param configChanges Mask of configuration changes that have occurred.
+ * @param config New configuration applied to the activity.
+ * @param preserveWindow Whether the activity should try to reuse the window it created,
+ * including the decor view after the relaunch.
+ * @return An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during
+ * relaunch, or {@code null} if relaunch cancelled.
+ */
+ public abstract ActivityThread.ActivityClientRecord prepareRelaunchActivity(IBinder token,
+ List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+ int configChanges, MergedConfiguration config, boolean preserveWindow);
+
+ /**
+ * Perform activity relaunch.
+ * @param r Activity client record prepared for relaunch.
+ * @param pendingActions Pending actions to be used on later stages of activity transaction.
+ * */
+ public abstract void handleRelaunchActivity(ActivityThread.ActivityClientRecord r,
+ PendingTransactionActions pendingActions);
+
+ /**
+ * Report that relaunch request was handled.
+ * @param token Target activity token.
+ * @param pendingActions Pending actions initialized on earlier stages of activity transaction.
+ * Used to check if we should report relaunch to WM.
+ * */
+ public abstract void reportRelaunch(IBinder token, PendingTransactionActions pendingActions);
+
+ /**
* Debugging output.
* @param pw {@link PrintWriter} to write logs to.
* @param prefix Prefix to prepend to output.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4a9b2bcb16ed..99fb465f5c41 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -18,6 +18,7 @@ package android.app;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -409,6 +410,7 @@ class ContextImpl extends Context {
return sp;
}
+ @GuardedBy("ContextImpl.class")
private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
if (sSharedPrefsCache == null) {
sSharedPrefsCache = new ArrayMap<>();
@@ -2263,6 +2265,7 @@ class ContextImpl extends Context {
}
/** @hide */
+ @TestApi
@Override
public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
mIsAutofillCompatEnabled = autofillCompatEnabled;
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 2b648ea6937d..eb260265c15f 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -609,18 +609,19 @@ public class Dialog implements DialogInterface, Window.Callback,
/**
* A key was pressed down.
+ * <p>
+ * If the focused view didn't want this event, this method is called.
+ * <p>
+ * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+ * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+ * KEYCODE_ESCAPE} to later handle them in {@link #onKeyUp}.
*
- * <p>If the focused view didn't want this event, this method is called.
- *
- * <p>The default implementation consumed the KEYCODE_BACK to later
- * handle it in {@link #onKeyUp}.
- *
* @see #onKeyUp
* @see android.view.KeyEvent
*/
@Override
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
event.startTracking();
return true;
}
@@ -640,16 +641,18 @@ public class Dialog implements DialogInterface, Window.Callback,
/**
* A key was released.
- *
- * <p>The default implementation handles KEYCODE_BACK to close the
- * dialog.
+ * <p>
+ * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+ * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+ * KEYCODE_ESCAPE} to close the dialog.
*
* @see #onKeyDown
- * @see KeyEvent
+ * @see android.view.KeyEvent
*/
@Override
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+ if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
+ && event.isTracking()
&& !event.isCanceled()) {
onBackPressed();
return true;
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 02be00268a45..54fd0c45a811 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -68,6 +68,7 @@ import android.os.WorkSource;
import android.service.voice.IVoiceInteractionSession;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationAdapter;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -423,6 +424,8 @@ interface IActivityManager {
void restart();
void performIdleMaintenance();
void takePersistableUriPermission(in Uri uri, int modeFlags, int userId);
+ boolean updatePersistableUriPermission(in Uri uri, boolean prefix, String packageName,
+ boolean grant, int userId);
void releasePersistableUriPermission(in Uri uri, int modeFlags, int userId);
ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming);
void appNotRespondingViaProvider(in IBinder connection);
@@ -683,17 +686,24 @@ interface IActivityManager {
// If a transaction which will also be used on the native side is being inserted, add it
// alongside with other transactions of this kind at the top of this file.
- void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
- void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
+ void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
+ void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
- /**
- * Similar to {@link #startUserInBackground(int userId), but with a listener to report
- * user unlock progress.
- */
- boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener);
+ /**
+ * Similar to {@link #startUserInBackground(int userId), but with a listener to report
+ * user unlock progress.
+ */
+ boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener);
- /**
- * Registers remote animations for a specific activity.
- */
- void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
+ /**
+ * Registers remote animations for a specific activity.
+ */
+ void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
+
+ /**
+ * Registers a remote animation to be run for all activity starts from a certain package during
+ * a short predefined amount of time.
+ */
+ void registerRemoteAnimationForNextActivityStart(in String packageName,
+ in RemoteAnimationAdapter adapter);
}
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index 7b05b4918103..ded4c4954956 100644
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -37,4 +37,5 @@ interface IAlarmManager {
void remove(in PendingIntent operation, in IAlarmListener listener);
long getNextWakeFromIdleTime();
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
+ long currentNetworkTimeMillis();
}
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 9e99a78ccfbd..ae9b83ec0122 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -83,9 +83,6 @@ oneway interface IApplicationThread {
int resultCode, in String data, in Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState);
void scheduleLowMemory();
- void scheduleRelaunchActivity(IBinder token, in List<ResultInfo> pendingResults,
- in List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
- in Configuration config, in Configuration overrideConfig, boolean preserveWindow);
void scheduleSleeping(IBinder token, boolean sleeping);
void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType);
void setSchedulingGroup(int group);
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 1d3459534f0c..e297719f9e4c 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -380,7 +380,7 @@ public class LocalActivityManager {
}
if (localLOGV) Log.v(TAG, r.id + ": destroying");
mActivityThread.performDestroyActivity(r, finish, 0 /* configChanges */,
- false /* getNonConfigInstance */);
+ false /* getNonConfigInstance */, "LocalActivityManager::performDestroy");
r.activity = null;
r.window = null;
if (finish) {
@@ -645,7 +645,7 @@ public class LocalActivityManager {
LocalActivityRecord r = mActivityArray.get(i);
if (localLOGV) Log.v(TAG, r.id + ": destroying");
mActivityThread.performDestroyActivity(r, finishing, 0 /* configChanges */,
- false /* getNonConfigInstance */);
+ false /* getNonConfigInstance */, "LocalActivityManager::dispatchDestroy");
}
mActivities.clear();
mActivityArray.clear();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 6e4098646b27..e80610b0fa83 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1055,11 +1055,10 @@ public class Notification implements Parcelable
/**
* {@link #extras} key: A
* {@link android.content.ContentUris content URI} pointing to an image that can be displayed
- * in the background when the notification is selected. The URI must point to an image stream
- * suitable for passing into
+ * in the background when the notification is selected. Used on television platforms.
+ * The URI must point to an image stream suitable for passing into
* {@link android.graphics.BitmapFactory#decodeStream(java.io.InputStream)
- * BitmapFactory.decodeStream}; all other content types will be ignored. The content provider
- * URI used for this purpose must require no permissions to read the image data.
+ * BitmapFactory.decodeStream}; all other content types will be ignored.
*/
public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
@@ -4617,10 +4616,15 @@ public class Notification implements Parcelable
if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
for (int i=0; i<N; i++) {
Action action = mActions.get(i);
- validRemoteInput |= hasValidRemoteInput(action);
+ boolean actionHasValidInput = hasValidRemoteInput(action);
+ validRemoteInput |= actionHasValidInput;
final RemoteViews button = generateActionButton(action, emphazisedMode,
i % 2 != 0, p.ambient);
+ if (actionHasValidInput) {
+ // Clear the drawable
+ button.setInt(R.id.action0, "setBackgroundResource", 0);
+ }
big.addView(R.id.actions, button);
}
} else {
@@ -6435,7 +6439,7 @@ public class Notification implements Parcelable
public RemoteViews makeContentView(boolean increasedHeight) {
mBuilder.mOriginalActions = mBuilder.mActions;
mBuilder.mActions = new ArrayList<>();
- RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */);
+ RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
mBuilder.mActions = mBuilder.mOriginalActions;
mBuilder.mOriginalActions = null;
return remoteViews;
@@ -6470,11 +6474,11 @@ public class Notification implements Parcelable
*/
@Override
public RemoteViews makeBigContentView() {
- return makeBigContentView(false /* showRightIcon */);
+ return makeMessagingView(false /* isCollapsed */);
}
@NonNull
- private RemoteViews makeBigContentView(boolean showRightIcon) {
+ private RemoteViews makeMessagingView(boolean isCollapsed) {
CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
? super.mBigContentTitle
: mConversationTitle;
@@ -6485,21 +6489,24 @@ public class Notification implements Parcelable
nameReplacement = conversationTitle;
conversationTitle = null;
}
+ boolean hideLargeIcon = !isCollapsed || isOneToOne;
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
mBuilder.getMessagingLayoutResource(),
mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
- .hideLargeIcon(!showRightIcon || isOneToOne)
+ .hideLargeIcon(hideLargeIcon)
.headerTextSecondary(conversationTitle)
- .alwaysShowReply(showRightIcon));
+ .alwaysShowReply(isCollapsed));
addExtras(mBuilder.mN.extras);
// also update the end margin if there is an image
int endMargin = R.dimen.notification_content_margin_end;
- if (mBuilder.mN.hasLargeIcon() && showRightIcon) {
+ if (isCollapsed) {
endMargin = R.dimen.notification_content_plus_picture_margin_end;
}
contentView.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
mBuilder.resolveContrastColor());
+ contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed",
+ isCollapsed);
contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
mBuilder.mN.mLargeIcon);
contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
@@ -6566,7 +6573,7 @@ public class Notification implements Parcelable
*/
@Override
public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
- RemoteViews remoteViews = makeBigContentView(true /* showRightIcon */);
+ RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
return remoteViews;
}
@@ -8855,6 +8862,7 @@ public class Notification implements Parcelable
private static final String EXTRA_CONTENT_INTENT = "content_intent";
private static final String EXTRA_DELETE_INTENT = "delete_intent";
private static final String EXTRA_CHANNEL_ID = "channel_id";
+ private static final String EXTRA_SUPPRESS_SHOW_OVER_APPS = "suppressShowOverApps";
// Flags bitwise-ored to mFlags
private static final int FLAG_AVAILABLE_ON_TV = 0x1;
@@ -8863,6 +8871,7 @@ public class Notification implements Parcelable
private String mChannelId;
private PendingIntent mContentIntent;
private PendingIntent mDeleteIntent;
+ private boolean mSuppressShowOverApps;
/**
* Create a {@link TvExtender} with default options.
@@ -8882,6 +8891,7 @@ public class Notification implements Parcelable
if (bundle != null) {
mFlags = bundle.getInt(EXTRA_FLAGS);
mChannelId = bundle.getString(EXTRA_CHANNEL_ID);
+ mSuppressShowOverApps = bundle.getBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS);
mContentIntent = bundle.getParcelable(EXTRA_CONTENT_INTENT);
mDeleteIntent = bundle.getParcelable(EXTRA_DELETE_INTENT);
}
@@ -8898,6 +8908,7 @@ public class Notification implements Parcelable
bundle.putInt(EXTRA_FLAGS, mFlags);
bundle.putString(EXTRA_CHANNEL_ID, mChannelId);
+ bundle.putBoolean(EXTRA_SUPPRESS_SHOW_OVER_APPS, mSuppressShowOverApps);
if (mContentIntent != null) {
bundle.putParcelable(EXTRA_CONTENT_INTENT, mContentIntent);
}
@@ -8990,6 +9001,23 @@ public class Notification implements Parcelable
public PendingIntent getDeleteIntent() {
return mDeleteIntent;
}
+
+ /**
+ * Specifies whether this notification should suppress showing a message over top of apps
+ * outside of the launcher.
+ */
+ public TvExtender setSuppressShowOverApps(boolean suppress) {
+ mSuppressShowOverApps = suppress;
+ return this;
+ }
+
+ /**
+ * Returns true if this notification should not show messages over top of apps
+ * outside of the launcher.
+ */
+ public boolean getSuppressShowOverApps() {
+ return mSuppressShowOverApps;
+ }
}
/**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 538623f26a95..b10e60870721 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -24,6 +24,7 @@ import android.annotation.TestApi;
import android.app.Notification.Builder;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -350,6 +351,14 @@ public class NotificationManager {
* the same tag and id has already been posted by your application and has not yet been
* canceled, it will be replaced by the updated information.
*
+ * All {@link android.service.notification.NotificationListenerService listener services} will
+ * be granted {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} access to any {@link Uri uris}
+ * provided on this notification or the
+ * {@link NotificationChannel} this notification is posted to using
+ * {@link Context#grantUriPermission(String, Uri, int)}. Permission will be revoked when the
+ * notification is canceled, or you can revoke permissions with
+ * {@link Context#revokeUriPermission(Uri, int)}.
+ *
* @param tag A string identifier for this notification. May be {@code null}.
* @param id An identifier for this notification. The pair (tag, id) must be unique
* within your application.
@@ -370,11 +379,13 @@ public class NotificationManager {
String pkg = mContext.getPackageName();
// Fix the notification as best we can.
Notification.addFieldsFromContext(mContext, notification);
+
if (notification.sound != null) {
notification.sound = notification.sound.getCanonicalUri();
if (StrictMode.vmFileUriExposureEnabled()) {
notification.sound.checkFileUriExposed("Notification.sound");
}
+
}
fixLegacySmallIcon(notification, pkg);
if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
@@ -385,6 +396,7 @@ public class NotificationManager {
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
notification.reduceImageSizes(mContext);
+
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
boolean isLowRam = am.isLowRamDevice();
final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam);
diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java
new file mode 100644
index 000000000000..39db16d10575
--- /dev/null
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The memory stats for a process.
+ * {@hide}
+ */
+public class ProcessMemoryState implements Parcelable {
+ public int uid;
+ public String processName;
+ public int oomScore;
+ public long pgfault;
+ public long pgmajfault;
+ public long rssInBytes;
+ public long cacheInBytes;
+ public long swapInBytes;
+
+ public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
+ long pgmajfault, long rssInBytes, long cacheInBytes,
+ long swapInBytes) {
+ this.uid = uid;
+ this.processName = processName;
+ this.oomScore = oomScore;
+ this.pgfault = pgfault;
+ this.pgmajfault = pgmajfault;
+ this.rssInBytes = rssInBytes;
+ this.cacheInBytes = cacheInBytes;
+ this.swapInBytes = swapInBytes;
+ }
+
+ private ProcessMemoryState(Parcel in) {
+ uid = in.readInt();
+ processName = in.readString();
+ oomScore = in.readInt();
+ pgfault = in.readLong();
+ pgmajfault = in.readLong();
+ rssInBytes = in.readLong();
+ cacheInBytes = in.readLong();
+ swapInBytes = in.readLong();
+ }
+
+ public static final Creator<ProcessMemoryState> CREATOR = new Creator<ProcessMemoryState>() {
+ @Override
+ public ProcessMemoryState createFromParcel(Parcel in) {
+ return new ProcessMemoryState(in);
+ }
+
+ @Override
+ public ProcessMemoryState[] newArray(int size) {
+ return new ProcessMemoryState[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeInt(uid);
+ parcel.writeString(processName);
+ parcel.writeInt(oomScore);
+ parcel.writeLong(pgfault);
+ parcel.writeLong(pgmajfault);
+ parcel.writeLong(rssInBytes);
+ parcel.writeLong(cacheInBytes);
+ parcel.writeLong(swapInBytes);
+ }
+}
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index c525c89c88e7..c2c91c2bcbd6 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -30,20 +30,27 @@ import android.util.Slog;
* @hide
*/
@SystemApi
-public final class StatsManager extends android.util.StatsManager { // TODO: Remove the extends.
+public final class StatsManager {
IStatsManager mService;
private static final String TAG = "StatsManager";
+ private static final boolean DEBUG = false;
- /** Long extra of uid that added the relevant stats config. */
- public static final String EXTRA_STATS_CONFIG_UID =
- "android.app.extra.STATS_CONFIG_UID";
- /** Long extra of the relevant stats config's configKey. */
- public static final String EXTRA_STATS_CONFIG_KEY =
- "android.app.extra.STATS_CONFIG_KEY";
- /** Long extra of the relevant statsd_config.proto's Subscription.id. */
+ /**
+ * Long extra of uid that added the relevant stats config.
+ */
+ public static final String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID";
+ /**
+ * Long extra of the relevant stats config's configKey.
+ */
+ public static final String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
+ /**
+ * Long extra of the relevant statsd_config.proto's Subscription.id.
+ */
public static final String EXTRA_STATS_SUBSCRIPTION_ID =
"android.app.extra.STATS_SUBSCRIPTION_ID";
- /** Long extra of the relevant statsd_config.proto's Subscription.rule_id. */
+ /**
+ * Long extra of the relevant statsd_config.proto's Subscription.rule_id.
+ */
public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID =
"android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
/**
@@ -68,28 +75,34 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem
}
/**
+ * Temporary. Will be deleted.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean addConfiguration(long configKey, byte[] config, String a, String b) {
+ return addConfiguration(configKey, config);
+ }
+
+ /**
* Clients can send a configuration and simultaneously registers the name of a broadcast
* receiver that listens for when it should request data.
*
* @param configKey An arbitrary integer that allows clients to track the configuration.
* @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
* dependencies eg, conditions and matchers).
- * @param pkg The package name to receive the broadcast.
- * @param cls The name of the class that receives the broadcast.
* @return true if successful
*/
@RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
+ public boolean addConfiguration(long configKey, byte[] config) {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when adding configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when adding configuration");
return false;
}
- return service.addConfiguration(configKey, config, pkg, cls);
+ return service.addConfiguration(configKey, config);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when adding configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when adding configuration");
return false;
}
}
@@ -107,12 +120,12 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when removing configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when removing configuration");
return false;
}
return service.removeConfiguration(configKey);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when removing configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when removing configuration");
return false;
}
}
@@ -121,38 +134,34 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem
/**
* Set the PendingIntent to be used when broadcasting subscriber information to the given
* subscriberId within the given config.
- *
* <p>
* Suppose that the calling uid has added a config with key configKey, and that in this config
* it is specified that when a particular anomaly is detected, a broadcast should be sent to
* a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with
* that subscriberId (for that config), so that this pendingIntent is used to send the broadcast
* when the anomaly is detected.
- *
* <p>
* When statsd sends the broadcast, the PendingIntent will used to send an intent with
* information of
- * {@link #EXTRA_STATS_CONFIG_UID},
- * {@link #EXTRA_STATS_CONFIG_KEY},
- * {@link #EXTRA_STATS_SUBSCRIPTION_ID},
- * {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and
- * {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
- *
+ * {@link #EXTRA_STATS_CONFIG_UID},
+ * {@link #EXTRA_STATS_CONFIG_KEY},
+ * {@link #EXTRA_STATS_SUBSCRIPTION_ID},
+ * {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and
+ * {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
* <p>
* This function can only be called by the owner (uid) of the config. It must be called each
* time statsd starts. The config must have been added first (via addConfiguration()).
*
- * @param configKey The integer naming the config to which this subscriber is attached.
- * @param subscriberId ID of the subscriber, as used in the config.
+ * @param configKey The integer naming the config to which this subscriber is attached.
+ * @param subscriberId ID of the subscriber, as used in the config.
* @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
* associated with the given subscriberId. May be null, in which case
* it undoes any previous setting of this subscriberId.
* @return true if successful
*/
@RequiresPermission(Manifest.permission.DUMP)
- public boolean setBroadcastSubscriber(long configKey,
- long subscriberId,
- PendingIntent pendingIntent) {
+ public boolean setBroadcastSubscriber(
+ long configKey, long subscriberId, PendingIntent pendingIntent) {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
@@ -175,6 +184,44 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem
}
/**
+ * Registers the operation that is called to retrieve the metrics data. This must be called
+ * each time statsd starts. The config must have been added first (via addConfiguration(),
+ * although addConfiguration could have been called on a previous boot). This operation allows
+ * statsd to send metrics data whenever statsd determines that the metrics in memory are
+ * approaching the memory limits. The fetch operation should call {@link #getData} to fetch the
+ * data, which also deletes the retrieved metrics from statsd's memory.
+ *
+ * @param configKey The integer naming the config to which this operation is attached.
+ * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
+ * associated with the given subscriberId. May be null, in which case
+ * it removes any associated pending intent with this configKey.
+ * @return true if successful
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ Slog.d(TAG, "Failed to find statsd when registering data listener.");
+ return false;
+ }
+ if (pendingIntent == null) {
+ return service.removeDataFetchOperation(configKey);
+ } else {
+ // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
+ IBinder intentSender = pendingIntent.getTarget().asBinder();
+ return service.setDataFetchOperation(configKey, intentSender);
+ }
+
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to connect to statsd when registering data listener.");
+ return false;
+ }
+ }
+ }
+
+ /**
* Clients can request data with a binder call. This getter is destructive and also clears
* the retrieved metrics from statsd memory.
*
@@ -187,12 +234,12 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting data");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when getting data");
return null;
}
return service.getData(configKey);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting data");
+ if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting data");
return null;
}
}
@@ -211,12 +258,12 @@ public final class StatsManager extends android.util.StatsManager { // TODO: Rem
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting metadata");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when getting metadata");
return null;
}
return service.getMetadata();
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting metadata");
+ if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting metadata");
return null;
}
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 87f32b2613bc..aa52cdef70c6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -38,7 +38,6 @@ import android.content.ClipboardManager;
import android.content.Context;
import android.content.IRestrictionsManager;
import android.content.RestrictionsManager;
-import android.content.pm.ApplicationInfo;
import android.content.pm.CrossProfileApps;
import android.content.pm.ICrossProfileApps;
import android.content.pm.IShortcutService;
@@ -90,7 +89,6 @@ import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
-import android.net.wifi.IRttManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.IWifiScanner;
import android.net.wifi.RttManager;
@@ -117,7 +115,6 @@ import android.os.ISystemUpdateManager;
import android.os.IUserManager;
import android.os.IncidentManager;
import android.os.PowerManager;
-import android.os.Process;
import android.os.RecoverySystem;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -639,13 +636,13 @@ final class SystemServiceRegistry {
registerService(Context.WIFI_RTT_SERVICE, RttManager.class,
new CachedServiceFetcher<RttManager>() {
- @Override
- public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_SERVICE);
- IRttManager service = IRttManager.Stub.asInterface(b);
- return new RttManager(ctx.getOuterContext(), service,
- ConnectivityThread.getInstanceLooper());
- }});
+ @Override
+ public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_RANGING_SERVICE);
+ IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
+ return new RttManager(ctx.getOuterContext(),
+ new WifiRttManager(ctx.getOuterContext(), service));
+ }});
registerService(Context.WIFI_RTT_RANGING_SERVICE, WifiRttManager.class,
new CachedServiceFetcher<WifiRttManager>() {
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 28e845a04e44..8c30fc403713 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -19,6 +19,8 @@ package android.app.admin;
import android.accounts.AccountManager;
import android.annotation.BroadcastBehavior;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
@@ -34,9 +36,6 @@ import android.os.Process;
import android.os.UserHandle;
import android.security.KeyChain;
-import libcore.util.NonNull;
-import libcore.util.Nullable;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -928,29 +927,29 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
int networkLogsCount) {
}
- /**
- * Called when a user or profile is created.
- *
- * <p>This callback is only applicable to device owners.
- *
- * @param context The running context as per {@link #onReceive}.
- * @param intent The received intent as per {@link #onReceive}.
- * @param newUser The {@link UserHandle} of the user that has just been added.
- */
- public void onUserAdded(Context context, Intent intent, UserHandle newUser) {
- }
-
- /**
- * Called when a user or profile is removed.
- *
- * <p>This callback is only applicable to device owners.
- *
- * @param context The running context as per {@link #onReceive}.
- * @param intent The received intent as per {@link #onReceive}.
- * @param removedUser The {@link UserHandle} of the user that has just been removed.
- */
- public void onUserRemoved(Context context, Intent intent, UserHandle removedUser) {
- }
+ /**
+ * Called when a user or profile is created.
+ *
+ * <p>This callback is only applicable to device owners.
+ *
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param newUser The {@link UserHandle} of the user that has just been added.
+ */
+ public void onUserAdded(Context context, Intent intent, @NonNull UserHandle newUser) {
+ }
+
+ /**
+ * Called when a user or profile is removed.
+ *
+ * <p>This callback is only applicable to device owners.
+ *
+ * @param context The running context as per {@link #onReceive}.
+ * @param intent The received intent as per {@link #onReceive}.
+ * @param removedUser The {@link UserHandle} of the user that has just been removed.
+ */
+ public void onUserRemoved(Context context, Intent intent, @NonNull UserHandle removedUser) {
+ }
/**
* Called when a user or profile is started.
@@ -961,7 +960,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @param intent The received intent as per {@link #onReceive}.
* @param startedUser The {@link UserHandle} of the user that has just been started.
*/
- public void onUserStarted(Context context, Intent intent, UserHandle startedUser) {
+ public void onUserStarted(Context context, Intent intent, @NonNull UserHandle startedUser) {
}
/**
@@ -973,7 +972,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @param intent The received intent as per {@link #onReceive}.
* @param stoppedUser The {@link UserHandle} of the user that has just been stopped.
*/
- public void onUserStopped(Context context, Intent intent, UserHandle stoppedUser) {
+ public void onUserStopped(Context context, Intent intent, @NonNull UserHandle stoppedUser) {
}
/**
@@ -985,7 +984,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @param intent The received intent as per {@link #onReceive}.
* @param switchedUser The {@link UserHandle} of the user that has just been switched to.
*/
- public void onUserSwitched(Context context, Intent intent, UserHandle switchedUser) {
+ public void onUserSwitched(Context context, Intent intent, @NonNull UserHandle switchedUser) {
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8d161006be76..14b2119b4e4b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -21,6 +21,7 @@ import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -116,6 +117,7 @@ import java.util.concurrent.Executor;
* guide. </div>
*/
@SystemService(Context.DEVICE_POLICY_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_DEVICE_ADMIN)
public class DevicePolicyManager {
private static String TAG = "DevicePolicyManager";
@@ -3444,9 +3446,6 @@ public class DevicePolicyManager {
/**
* Flag for {@link #wipeData(int)}: also erase the device's eUICC data.
- *
- * TODO(b/35851809): make this public.
- * @hide
*/
public static final int WIPE_EUICC = 0x0004;
@@ -9404,41 +9403,6 @@ public class DevicePolicyManager {
}
/**
- * Allows/disallows printing.
- *
- * Called by a device owner or a profile owner.
- * Device owner changes policy for all users. Profile owner can override it if present.
- * Printing is enabled by default. If {@code FEATURE_PRINTING} is absent, the call is ignored.
- *
- * @param admin which {@link DeviceAdminReceiver} this request is associated with.
- * @param enabled whether printing should be allowed or not.
- * @throws SecurityException if {@code admin} is neither device, nor profile owner.
- */
- public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) {
- try {
- mService.setPrintingEnabled(admin, enabled);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
- /**
- * Returns whether printing is enabled for this user.
- *
- * Always {@code false} if {@code FEATURE_PRINTING} is absent.
- * Otherwise, {@code true} by default.
- *
- * @return {@code true} iff printing is enabled.
- */
- public boolean isPrintingEnabled() {
- try {
- return mService.isPrintingEnabled();
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
- }
-
- /**
* Called by device owner to add an override APN.
*
* @param admin which {@link DeviceAdminReceiver} this request is associated with
diff --git a/core/java/android/app/admin/FreezeInterval.java b/core/java/android/app/admin/FreezeInterval.java
index 7acdfc8fe100..de5e21ac75c4 100644
--- a/core/java/android/app/admin/FreezeInterval.java
+++ b/core/java/android/app/admin/FreezeInterval.java
@@ -84,6 +84,10 @@ public class FreezeInterval {
}
}
+ boolean after(LocalDate localDate) {
+ return mStartDay > dayOfYearDisregardLeapYear(localDate);
+ }
+
/**
* Instantiate the current interval to real calendar dates, given a calendar date
* {@code now}. If the interval contains now, the returned calendar dates should be the
@@ -161,7 +165,7 @@ public class FreezeInterval {
* 3. At most one wrapped Interval remains, and it will be at the end of the list
* @hide
*/
- private static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) {
+ protected static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) {
boolean[] taken = new boolean[DAYS_IN_YEAR];
// First convert the intervals into flat array
for (FreezeInterval interval : intervals) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ef990071dbfd..5218a7340ec9 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -402,9 +402,6 @@ interface IDevicePolicyManager {
CharSequence getStartUserSessionMessage(in ComponentName admin);
CharSequence getEndUserSessionMessage(in ComponentName admin);
- void setPrintingEnabled(in ComponentName admin, boolean enabled);
- boolean isPrintingEnabled();
-
List<String> setMeteredDataDisabled(in ComponentName admin, in List<String> packageNames);
List<String> getMeteredDataDisabled(in ComponentName admin);
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 08effd9c148a..faaa0043cce8 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -77,6 +77,9 @@ public class SecurityLog {
TAG_KEY_DESTRUCTION,
TAG_CERT_AUTHORITY_INSTALLED,
TAG_CERT_AUTHORITY_REMOVED,
+ TAG_CRYPTO_SELF_TEST_COMPLETED,
+ TAG_KEY_INTEGRITY_VIOLATION,
+ TAG_CERT_VALIDATION_FAILURE,
})
public @interface SecurityLogTag {}
@@ -316,6 +319,7 @@ public class SecurityLog {
* {@link SecurityEvent#getData()}:
* <li> [0] admin package name ({@code String}),
* <li> [1] admin user ID ({@code Integer}).
+ * <li> [2] target user ID ({@code Integer})
*/
public static final int TAG_REMOTE_LOCK = SecurityLogTags.SECURITY_REMOTE_LOCK;
@@ -400,6 +404,31 @@ public class SecurityLog {
SecurityLogTags.SECURITY_USER_RESTRICTION_REMOVED;
/**
+ * Indicates that cryptographic functionality self test has completed. The log entry contains an
+ * {@code Integer} payload, indicating the result of the test (0 if the test failed, 1 if
+ * succeeded) and accessible via {@link SecurityEvent#getData()}.
+ */
+ public static final int TAG_CRYPTO_SELF_TEST_COMPLETED =
+ SecurityLogTags.SECURITY_CRYPTO_SELF_TEST_COMPLETED;
+
+ /**
+ * Indicates a failed cryptographic key integrity check. The log entry contains the following
+ * information about the event, encapsulated in an {@link Object} array and accessible via
+ * {@link SecurityEvent#getData()}:
+ * <li> [0] alias of the key ({@code String})
+ * <li> [1] owner application uid ({@code Integer}).
+ */
+ public static final int TAG_KEY_INTEGRITY_VIOLATION =
+ SecurityLogTags.SECURITY_KEY_INTEGRITY_VIOLATION;
+
+ /**
+ * Indicates a failure to validate X.509v3 certificate. The log entry contains a {@code String}
+ * payload indicating the failure reason, accessible via {@link SecurityEvent#getData()}.
+ */
+ public static final int TAG_CERT_VALIDATION_FAILURE =
+ SecurityLogTags.SECURITY_CERT_VALIDATION_FAILURE;
+
+ /**
* Event severity level indicating that the event corresponds to normal workflow.
*/
public static final int LEVEL_INFO = 1;
@@ -529,6 +558,7 @@ public class SecurityLog {
case TAG_USER_RESTRICTION_REMOVED:
return LEVEL_INFO;
case TAG_CERT_AUTHORITY_REMOVED:
+ case TAG_CRYPTO_SELF_TEST_COMPLETED:
return getSuccess() ? LEVEL_INFO : LEVEL_ERROR;
case TAG_CERT_AUTHORITY_INSTALLED:
case TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT:
@@ -538,7 +568,10 @@ public class SecurityLog {
return getSuccess() ? LEVEL_INFO : LEVEL_WARNING;
case TAG_LOG_BUFFER_SIZE_CRITICAL:
case TAG_WIPE_FAILURE:
+ case TAG_KEY_INTEGRITY_VIOLATION:
return LEVEL_ERROR;
+ case TAG_CERT_VALIDATION_FAILURE:
+ return LEVEL_WARNING;
default:
return LEVEL_INFO;
}
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index be626786c3c6..fe2519d2bdf1 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -34,4 +34,7 @@ option java_package android.app.admin
210027 security_user_restriction_added (package|3),(admin_user|1),(restriction|3)
210028 security_user_restriction_removed (package|3),(admin_user|1),(restriction|3)
210029 security_cert_authority_installed (success|1),(subject|3)
-210030 security_cert_authority_removed (success|1),(subject|3) \ No newline at end of file
+210030 security_cert_authority_removed (success|1),(subject|3)
+210031 security_crypto_self_test_completed (success|1)
+210032 security_key_integrity_violation (key_id|3),(uid|1)
+210033 security_cert_validation_failure (reason|3)
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index 05d3fd9c632c..47b3a81d0174 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -21,6 +21,7 @@ import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.TEXT;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -33,9 +34,15 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -103,6 +110,19 @@ public class SystemUpdatePolicy implements Parcelable {
*/
public static final int TYPE_POSTPONE = 3;
+ /**
+ * Incoming system updates (including security updates) should be blocked. This flag is not
+ * exposed to third-party apps (and any attempt to set it will raise exceptions). This is used
+ * to represent the current installation option type to the privileged system update clients,
+ * for example to indicate OTA freeze is currently in place or when system is outside a daily
+ * maintenance window.
+ *
+ * @see InstallationOption
+ * @hide
+ */
+ @SystemApi
+ public static final int TYPE_PAUSE = 4;
+
private static final String KEY_POLICY_TYPE = "policy_type";
private static final String KEY_INSTALL_WINDOW_START = "install_window_start";
private static final String KEY_INSTALL_WINDOW_END = "install_window_end";
@@ -460,6 +480,30 @@ public class SystemUpdatePolicy implements Parcelable {
return null;
}
+ /**
+ * Returns time (in milliseconds) until the start of the next freeze period, assuming now
+ * is not within a freeze period.
+ */
+ private long timeUntilNextFreezePeriod(long now) {
+ List<FreezeInterval> sortedPeriods = FreezeInterval.canonicalizeIntervals(mFreezePeriods);
+ LocalDate nowDate = millisToDate(now);
+ LocalDate nextFreezeStart = null;
+ for (FreezeInterval interval : sortedPeriods) {
+ if (interval.after(nowDate)) {
+ nextFreezeStart = interval.toCurrentOrFutureRealDates(nowDate).first;
+ break;
+ } else if (interval.contains(nowDate)) {
+ throw new IllegalArgumentException("Given date is inside a freeze period");
+ }
+ }
+ if (nextFreezeStart == null) {
+ // If no interval is after now, then it must be the one that starts at the beginning
+ // of next year
+ nextFreezeStart = sortedPeriods.get(0).toCurrentOrFutureRealDates(nowDate).first;
+ }
+ return dateToMillis(nextFreezeStart) - now;
+ }
+
/** @hide */
public void validateFreezePeriods() {
FreezeInterval.validatePeriods(mFreezePeriods);
@@ -472,6 +516,134 @@ public class SystemUpdatePolicy implements Parcelable {
prevPeriodEnd, now);
}
+ /**
+ * An installation option represents how system update clients should act on incoming system
+ * updates and how long this action is valid for, given the current system update policy. Its
+ * action could be one of the following
+ * <ul>
+ * <li> {@code TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and without
+ * user intervention as soon as they become available.
+ * <li> {@code TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days
+ * <li> {@code TYPE_PAUSE} system updates should be postponed indefinitely until further notice
+ * </ul>
+ *
+ * The effective time measures how long this installation option is valid for from the queried
+ * time, in milliseconds.
+ *
+ * This is an internal API for system update clients.
+ * @hide
+ */
+ @SystemApi
+ public static class InstallationOption {
+ private final int mType;
+ private long mEffectiveTime;
+
+ InstallationOption(int type, long effectiveTime) {
+ this.mType = type;
+ this.mEffectiveTime = effectiveTime;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public long getEffectiveTime() {
+ return mEffectiveTime;
+ }
+
+ /** @hide */
+ protected void limitEffectiveTime(long otherTime) {
+ mEffectiveTime = Long.min(mEffectiveTime, otherTime);
+ }
+ }
+
+ /**
+ * Returns the installation option at the specified time, under the current
+ * {@code SystemUpdatePolicy} object. This is a convenience method for system update clients
+ * so they can instantiate this policy at any given time and find out what to do with incoming
+ * system updates, without the need of examining the overall policy structure.
+ *
+ * Normally the system update clients will query the current installation option by calling this
+ * method with the current timestamp, and act on the returned option until its effective time
+ * lapses. It can then query the latest option using a new timestamp. It should also listen
+ * for {@code DevicePolicyManager#ACTION_SYSTEM_UPDATE_POLICY_CHANGED} broadcast, in case the
+ * whole policy is updated.
+ *
+ * @param when At what time the intallation option is being queried, specified in number of
+ milliseonds since the epoch.
+ * @see InstallationOption
+ * @hide
+ */
+ @SystemApi
+ public InstallationOption getInstallationOptionAt(long when) {
+ LocalDate whenDate = millisToDate(when);
+ Pair<LocalDate, LocalDate> current = getCurrentFreezePeriod(whenDate);
+ if (current != null) {
+ return new InstallationOption(TYPE_PAUSE,
+ dateToMillis(roundUpLeapDay(current.second).plusDays(1)) - when);
+ }
+ // We are not within a freeze period, query the underlying policy.
+ // But also consider the start of the next freeze period, which might
+ // reduce the effective time of the current installation option
+ InstallationOption option = getInstallationOptionRegardlessFreezeAt(when);
+ if (mFreezePeriods.size() > 0) {
+ option.limitEffectiveTime(timeUntilNextFreezePeriod(when));
+ }
+ return option;
+ }
+
+ private InstallationOption getInstallationOptionRegardlessFreezeAt(long when) {
+ if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
+ return new InstallationOption(mPolicyType, Long.MAX_VALUE);
+ } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+ Calendar query = Calendar.getInstance();
+ query.setTimeInMillis(when);
+ // Calculate the number of milliseconds since midnight of the time specified by when
+ long whenMillis = TimeUnit.HOURS.toMillis(query.get(Calendar.HOUR_OF_DAY))
+ + TimeUnit.MINUTES.toMillis(query.get(Calendar.MINUTE))
+ + TimeUnit.SECONDS.toMillis(query.get(Calendar.SECOND))
+ + query.get(Calendar.MILLISECOND);
+ long windowStartMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowStart);
+ long windowEndMillis = TimeUnit.MINUTES.toMillis(mMaintenanceWindowEnd);
+ final long dayInMillis = TimeUnit.DAYS.toMillis(1);
+
+ if ((windowStartMillis <= whenMillis && whenMillis <= windowEndMillis)
+ || ((windowStartMillis > windowEndMillis)
+ && (windowStartMillis <= whenMillis || whenMillis <= windowEndMillis))) {
+ return new InstallationOption(TYPE_INSTALL_AUTOMATIC,
+ (windowEndMillis - whenMillis + dayInMillis) % dayInMillis);
+ } else {
+ return new InstallationOption(TYPE_PAUSE,
+ (windowStartMillis - whenMillis + dayInMillis) % dayInMillis);
+ }
+ } else {
+ throw new RuntimeException("Unknown policy type");
+ }
+ }
+
+ private static LocalDate roundUpLeapDay(LocalDate date) {
+ if (date.isLeapYear() && date.getMonthValue() == 2 && date.getDayOfMonth() == 28) {
+ return date.plusDays(1);
+ } else {
+ return date;
+ }
+ }
+
+ /** Convert a timestamp since epoch to a LocalDate using default timezone, truncating
+ * the hour/min/seconds part.
+ */
+ private static LocalDate millisToDate(long when) {
+ return Instant.ofEpochMilli(when).atZone(ZoneId.systemDefault()).toLocalDate();
+ }
+
+ /**
+ * Returns the timestamp since epoch of a LocalDate, assuming the time is 00:00:00.
+ */
+ private static long dateToMillis(LocalDate when) {
+ return LocalDateTime.of(when, LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant()
+ .toEpochMilli();
+ }
+
@Override
public String toString() {
return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d, "
@@ -480,11 +652,13 @@ public class SystemUpdatePolicy implements Parcelable {
mFreezePeriods.stream().map(n -> n.toString()).collect(Collectors.joining(",")));
}
+ @SystemApi
@Override
public int describeContents() {
return 0;
}
+ @SystemApi
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mPolicyType);
@@ -499,6 +673,7 @@ public class SystemUpdatePolicy implements Parcelable {
}
}
+ @SystemApi
public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR =
new Parcelable.Creator<SystemUpdatePolicy>() {
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 8f49bc177dcb..1312a2e6b623 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -24,6 +24,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.View;
+import android.view.View.AutofillImportance;
import android.view.ViewRootImpl;
import android.view.ViewStructure;
import android.view.ViewStructure.HtmlInfo;
@@ -632,6 +633,7 @@ public class AssistStructure implements Parcelable {
int mMaxEms = -1;
int mMaxLength = -1;
@Nullable String mTextIdEntry;
+ @AutofillImportance int mImportantForAutofill;
// POJO used to override some autofill-related values when the node is parcelized.
// Not written to parcel.
@@ -733,6 +735,7 @@ public class AssistStructure implements Parcelable {
mMaxEms = in.readInt();
mMaxLength = in.readInt();
mTextIdEntry = preader.readString();
+ mImportantForAutofill = in.readInt();
}
if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
mX = in.readInt();
@@ -900,6 +903,7 @@ public class AssistStructure implements Parcelable {
out.writeInt(mMaxEms);
out.writeInt(mMaxLength);
pwriter.writeString(mTextIdEntry);
+ out.writeInt(mImportantForAutofill);
}
if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
out.writeInt(mX);
@@ -1299,6 +1303,17 @@ public class AssistStructure implements Parcelable {
}
/**
+ * @hide
+ */
+ public void setWebDomain(@Nullable String domain) {
+ if (domain == null) return;
+
+ final Uri uri = Uri.parse(domain);
+ mWebScheme = uri.getScheme();
+ mWebDomain = uri.getHost();
+ }
+
+ /**
* 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
@@ -1512,6 +1527,16 @@ public class AssistStructure implements Parcelable {
public int getMaxTextLength() {
return mMaxLength;
}
+
+ /**
+ * Gets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of
+ * the view associated with this node.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes.
+ */
+ public @AutofillImportance int getImportantForAutofill() {
+ return mImportantForAutofill;
+ }
}
/**
@@ -1844,6 +1869,11 @@ public class AssistStructure implements Parcelable {
}
@Override
+ public void setImportantForAutofill(@AutofillImportance int mode) {
+ mNode.mImportantForAutofill = mode;
+ }
+
+ @Override
public void setInputType(int inputType) {
mNode.mInputType = inputType;
}
@@ -1870,14 +1900,7 @@ public class AssistStructure implements Parcelable {
@Override
public void setWebDomain(@Nullable String domain) {
- if (domain == null) {
- mNode.mWebScheme = null;
- mNode.mWebDomain = null;
- return;
- }
- Uri uri = Uri.parse(domain);
- mNode.mWebScheme = uri.getScheme();
- mNode.mWebDomain = uri.getHost();
+ mNode.setWebDomain(domain);
}
@Override
@@ -2144,7 +2167,8 @@ public class AssistStructure implements Parcelable {
+ ", options=" + Arrays.toString(node.getAutofillOptions())
+ ", hints=" + Arrays.toString(node.getAutofillHints())
+ ", value=" + node.getAutofillValue()
- + ", sanitized=" + node.isSanitized());
+ + ", sanitized=" + node.isSanitized()
+ + ", importantFor=" + node.getImportantForAutofill());
}
final int NCHILDREN = node.getChildCount();
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 72eb4948f0f9..d36a794ac046 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -163,6 +163,16 @@ public abstract class BackupAgent extends ContextWrapper {
*/
public static final int FLAG_DEVICE_TO_DEVICE_TRANSFER = 2;
+ /**
+ * Flag for {@link BackupDataOutput#getTransportFlags()} and
+ * {@link FullBackupDataOutput#getTransportFlags()} only.
+ *
+ * <p>Used for internal testing only. Do not check this flag in production code.
+ *
+ * @hide
+ */
+ public static final int FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED = 1 << 31;
+
Handler mHandler = null;
Handler getHandler() {
diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS
new file mode 100644
index 000000000000..1c9a43acfa65
--- /dev/null
+++ b/core/java/android/app/backup/OWNERS
@@ -0,0 +1,7 @@
+artikz@google.com
+brufino@google.com
+bryanmawhinney@google.com
+ctate@google.com
+jorlow@google.com
+mkarpinski@google.com
+
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index a1ad825c3a29..ee13880c9016 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -365,9 +365,7 @@ public class JobInfo implements Parcelable {
/** @hide */
public boolean isExemptedFromAppStandby() {
- return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0)
- && !hasEarlyConstraint()
- && !hasLateConstraint();
+ return ((flags & FLAG_EXEMPT_FROM_APP_STANDBY) != 0) && !isPeriodic();
}
/**
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 9a50a009ce34..7f8c50cd4ce5 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -91,4 +91,9 @@ public abstract class ActivityLifecycleItem extends ClientTransactionItem {
pw.println(prefix + "target state:" + getTargetState());
pw.println(prefix + "description: " + mDescription);
}
+
+ @Override
+ public void recycle() {
+ setDescription(null);
+ }
}
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
new file mode 100644
index 000000000000..d8a7463cb086
--- /dev/null
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.app.ActivityThread.DEBUG_ORDER;
+
+import android.app.ActivityThread;
+import android.app.ClientTransactionHandler;
+import android.app.ResultInfo;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+import android.util.MergedConfiguration;
+import android.util.Slog;
+
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Activity relaunch callback.
+ * @hide
+ */
+public class ActivityRelaunchItem extends ClientTransactionItem {
+
+ private static final String TAG = "ActivityRelaunchItem";
+
+ private List<ResultInfo> mPendingResults;
+ private List<ReferrerIntent> mPendingNewIntents;
+ private int mConfigChanges;
+ private MergedConfiguration mConfig;
+ private boolean mPreserveWindow;
+
+ /**
+ * A record that was properly configured for relaunch. Execution will be cancelled if not
+ * initialized after {@link #preExecute(ClientTransactionHandler, IBinder)}.
+ */
+ private ActivityThread.ActivityClientRecord mActivityClientRecord;
+
+ @Override
+ public void preExecute(ClientTransactionHandler client, IBinder token) {
+ mActivityClientRecord = client.prepareRelaunchActivity(token, mPendingResults,
+ mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow);
+ }
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token,
+ PendingTransactionActions pendingActions) {
+ if (mActivityClientRecord == null) {
+ if (DEBUG_ORDER) Slog.d(TAG, "Activity relaunch cancelled");
+ return;
+ }
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
+ client.handleRelaunchActivity(mActivityClientRecord, pendingActions);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @Override
+ public void postExecute(ClientTransactionHandler client, IBinder token,
+ PendingTransactionActions pendingActions) {
+ client.reportRelaunch(token, pendingActions);
+ }
+
+ // ObjectPoolItem implementation
+
+ private ActivityRelaunchItem() {}
+
+ /** Obtain an instance initialized with provided params. */
+ public static ActivityRelaunchItem obtain(List<ResultInfo> pendingResults,
+ List<ReferrerIntent> pendingNewIntents, int configChanges, MergedConfiguration config,
+ boolean preserveWindow) {
+ ActivityRelaunchItem instance = ObjectPool.obtain(ActivityRelaunchItem.class);
+ if (instance == null) {
+ instance = new ActivityRelaunchItem();
+ }
+ instance.mPendingResults = pendingResults;
+ instance.mPendingNewIntents = pendingNewIntents;
+ instance.mConfigChanges = configChanges;
+ instance.mConfig = config;
+ instance.mPreserveWindow = preserveWindow;
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mPendingResults = null;
+ mPendingNewIntents = null;
+ mConfigChanges = 0;
+ mConfig = null;
+ mPreserveWindow = false;
+ mActivityClientRecord = null;
+ ObjectPool.recycle(this);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedList(mPendingResults, flags);
+ dest.writeTypedList(mPendingNewIntents, flags);
+ dest.writeInt(mConfigChanges);
+ dest.writeTypedObject(mConfig, flags);
+ dest.writeBoolean(mPreserveWindow);
+ }
+
+ /** Read from Parcel. */
+ private ActivityRelaunchItem(Parcel in) {
+ mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR);
+ mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
+ mConfigChanges = in.readInt();
+ mConfig = in.readTypedObject(MergedConfiguration.CREATOR);
+ mPreserveWindow = in.readBoolean();
+ }
+
+ public static final Creator<ActivityRelaunchItem> CREATOR =
+ new Creator<ActivityRelaunchItem>() {
+ public ActivityRelaunchItem createFromParcel(Parcel in) {
+ return new ActivityRelaunchItem(in);
+ }
+
+ public ActivityRelaunchItem[] newArray(int size) {
+ return new ActivityRelaunchItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ActivityRelaunchItem other = (ActivityRelaunchItem) o;
+ return Objects.equals(mPendingResults, other.mPendingResults)
+ && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
+ && mConfigChanges == other.mConfigChanges && Objects.equals(mConfig, other.mConfig)
+ && mPreserveWindow == other.mPreserveWindow;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mPendingResults);
+ result = 31 * result + Objects.hashCode(mPendingNewIntents);
+ result = 31 * result + mConfigChanges;
+ result = 31 * result + Objects.hashCode(mConfig);
+ result = 31 * result + (mPreserveWindow ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ActivityRelaunchItem{pendingResults=" + mPendingResults
+ + ",pendingNewIntents=" + mPendingNewIntents + ",configChanges=" + mConfigChanges
+ + ",config=" + mConfig + ",preserveWindow" + mPreserveWindow + "}";
+ }
+}
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index cbcf6c750fed..0edcf1884f01 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -37,7 +37,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
client.handleDestroyActivity(token, mFinished, mConfigChanges,
- false /* getNonConfigInstance */);
+ false /* getNonConfigInstance */, getDescription());
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -65,6 +65,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem {
@Override
public void recycle() {
+ super.recycle();
mFinished = false;
mConfigChanges = 0;
ObjectPool.recycle(this);
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 70a4755f99af..91e73cd5b1cd 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -102,6 +102,7 @@ public class PauseActivityItem extends ActivityLifecycleItem {
@Override
public void recycle() {
+ super.recycle();
mFinished = false;
mUserLeaving = false;
mConfigChanges = 0;
diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
index 073d28cfa27f..af7b7a21a582 100644
--- a/core/java/android/app/servertransaction/PendingTransactionActions.java
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -44,6 +44,7 @@ public class PendingTransactionActions {
private boolean mCallOnPostCreate;
private Bundle mOldState;
private StopInfo mStopInfo;
+ private boolean mReportRelaunchToWM;
public PendingTransactionActions() {
clear();
@@ -91,6 +92,24 @@ public class PendingTransactionActions {
mStopInfo = stopInfo;
}
+ /**
+ * Check if we should report an activity relaunch to WindowManager. We report back for every
+ * relaunch request to ActivityManager, but only for those that were actually finished to we
+ * report to WindowManager.
+ */
+ public boolean shouldReportRelaunchToWindowManager() {
+ return mReportRelaunchToWM;
+ }
+
+ /**
+ * Set if we should report an activity relaunch to WindowManager. We report back for every
+ * relaunch request to ActivityManager, but only for those that were actually finished we report
+ * to WindowManager.
+ */
+ public void setReportRelaunchToWindowManager(boolean reportToWm) {
+ mReportRelaunchToWM = reportToWm;
+ }
+
/** Reports to server about activity stop. */
public static class StopInfo implements Runnable {
private static final String TAG = "ActivityStopInfo";
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index ed90f2cb1013..af2fb713e1bc 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -101,6 +101,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem {
@Override
public void recycle() {
+ super.recycle();
mProcState = ActivityManager.PROCESS_STATE_UNKNOWN;
mUpdateProcState = false;
mIsForward = false;
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index b814d1ae1392..f955a903d649 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -72,6 +72,7 @@ public class StopActivityItem extends ActivityLifecycleItem {
@Override
public void recycle() {
+ super.recycle();
mShowWindow = false;
mConfigChanges = 0;
ObjectPool.recycle(this);
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 78b393a831f9..840fef80a5ff 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -194,7 +194,9 @@ public class TransactionExecutor {
break;
case ON_DESTROY:
mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
- 0 /* configChanges */, false /* getNonConfigInstance */);
+ 0 /* configChanges */, false /* getNonConfigInstance */,
+ "performLifecycleSequence. cycling to:"
+ + mLifecycleSequence.get(size - 1));
break;
case ON_RESTART:
mTransactionHandler.performRestartActivity(r.token, false /* start */);
diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java
index 0a38eb9ae777..dc7925694176 100644
--- a/core/java/android/app/timezone/RulesManager.java
+++ b/core/java/android/app/timezone/RulesManager.java
@@ -68,6 +68,23 @@ public final class RulesManager {
private static final String TAG = "timezone.RulesManager";
private static final boolean DEBUG = false;
+ /**
+ * The action of the intent that the Android system will broadcast when a time zone rules update
+ * operation has been successfully staged (i.e. to be applied next reboot) or unstaged.
+ *
+ * <p>See {@link #EXTRA_OPERATION_STAGED}
+ *
+ * <p>This is a protected intent that can only be sent by the system.
+ */
+ public static final String ACTION_RULES_UPDATE_OPERATION =
+ "com.android.intent.action.timezone.RULES_UPDATE_OPERATION";
+
+ /**
+ * The key for a boolean extra for the {@link #ACTION_RULES_UPDATE_OPERATION} intent used to
+ * indicate whether the operation was a "stage" or an "unstage".
+ */
+ public static final String EXTRA_OPERATION_STAGED = "staged";
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "SUCCESS", "ERROR_" }, value = {
SUCCESS,
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index edb992bd265c..6b573e99f1fa 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -16,6 +16,7 @@
package android.app.usage;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
@@ -104,12 +105,14 @@ public final class UsageEvents implements Parcelable {
* An event type denoting that a notification was viewed by the user.
* @hide
*/
+ @SystemApi
public static final int NOTIFICATION_SEEN = 10;
/**
* An event type denoting a change in App Standby Bucket.
* @hide
*/
+ @SystemApi
public static final int STANDBY_BUCKET_CHANGED = 11;
/** @hide */
@@ -257,6 +260,17 @@ public final class UsageEvents implements Parcelable {
return mShortcutId;
}
+ /**
+ * Returns the standby bucket of the app, if the event is of type
+ * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
+ * @return the standby bucket associated with the event.
+ * @hide
+ */
+ @SystemApi
+ public int getStandbyBucket() {
+ return mBucket;
+ }
+
/** @hide */
public Event getObfuscatedIfInstantApp() {
if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index a2c75a6c014c..e736f34eea11 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -19,6 +19,7 @@ package android.appwidget;
import android.annotation.BroadcastBehavior;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
@@ -29,6 +30,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
@@ -55,6 +57,7 @@ import java.util.List;
* </div>
*/
@SystemService(Context.APPWIDGET_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_APP_WIDGETS)
public class AppWidgetManager {
/**
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index bc7823b00f49..1dc7549e763a 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2307,6 +2307,9 @@ public final class BluetoothAdapter {
} else if (profile == BluetoothProfile.HID_DEVICE) {
BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener);
return true;
+ } else if (profile == BluetoothProfile.HEARING_AID) {
+ BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener);
+ return true;
} else {
return false;
}
@@ -2389,6 +2392,9 @@ public final class BluetoothAdapter {
BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy;
hidDevice.close();
break;
+ case BluetoothProfile.HEARING_AID:
+ BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
+ hearingAid.close();
}
}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
new file mode 100644
index 000000000000..647e0d033fb7
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -0,0 +1,693 @@
+/*
+ * Copyright 2018 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * This class provides the public APIs to control the Bluetooth Hearing Aid
+ * profile.
+ *
+ * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothHearingAid proxy object.
+ *
+ * <p> Each method is protected with its appropriate permission.
+ * @hide
+ */
+public final class BluetoothHearingAid implements BluetoothProfile {
+ private static final String TAG = "BluetoothHearingAid";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
+
+ /**
+ * Intent used to broadcast the change in connection state of the Hearing Aid
+ * profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
+
+ /**
+ * Intent used to broadcast the change in the Playing state of the Hearing Aid
+ * profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PLAYING_STATE_CHANGED =
+ "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED";
+
+ /**
+ * Intent used to broadcast the selection of a connected device as active.
+ *
+ * <p>This intent will have one extra:
+ * <ul>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
+ * be null if no device is active. </li>
+ * </ul>
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ACTIVE_DEVICE_CHANGED =
+ "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
+
+ /**
+ * Hearing Aid device is streaming music. This state can be one of
+ * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+ * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+ */
+ public static final int STATE_PLAYING = 10;
+
+ /**
+ * Hearing Aid device is NOT streaming music. This state can be one of
+ * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+ * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+ */
+ public static final int STATE_NOT_PLAYING = 11;
+
+ /** This device represents Left Hearing Aid. */
+ public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
+
+ /** This device represents Right Hearing Aid. */
+ public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT;
+
+ /** This device is Monaural. */
+ public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL;
+
+ /** This device is Binaural (should receive only left or right audio). */
+ public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL;
+
+ /** Can't read ClientID for this device */
+ public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
+
+ private Context mContext;
+ private ServiceListener mServiceListener;
+ private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
+ @GuardedBy("mServiceLock")
+ private IBluetoothHearingAid mService;
+ private BluetoothAdapter mAdapter;
+
+ private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ if (VDBG) Log.d(TAG, "Unbinding service...");
+ try {
+ mServiceLock.writeLock().lock();
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+ } else {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ if (VDBG) Log.d(TAG, "Binding service...");
+ doBind();
+ }
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+ }
+ };
+
+ /**
+ * Create a BluetoothHearingAid proxy object for interacting with the local
+ * Bluetooth Hearing Aid service.
+ */
+ /*package*/ BluetoothHearingAid(Context context, ServiceListener l) {
+ mContext = context;
+ mServiceListener = l;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ doBind();
+ }
+
+ void doBind() {
+ Intent intent = new Intent(IBluetoothHearingAid.class.getName());
+ ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ intent.setComponent(comp);
+ if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+ android.os.Process.myUserHandle())) {
+ Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
+ return;
+ }
+ }
+
+ /*package*/ void close() {
+ mServiceListener = null;
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (Exception e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ try {
+ mServiceLock.writeLock().lock();
+ if (mService != null) {
+ mService = null;
+ mContext.unbindService(mConnection);
+ }
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public void finalize() {
+ // The empty finalize needs to be kept or the
+ // cts signature tests would fail.
+ }
+
+ /**
+ * Initiate connection to a profile of the remote bluetooth device.
+ *
+ * <p> This API returns false in scenarios like the profile on the
+ * device is already connected or Bluetooth is not turned on.
+ * When this API returns true, it is guaranteed that
+ * connection state intent for the profile will be broadcasted with
+ * the state. Users can get the connection state of the profile
+ * from this intent.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Remote Bluetooth Device
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean connect(BluetoothDevice device) {
+ if (DBG) log("connect(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ return mService.connect(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Initiate disconnection from a profile
+ *
+ * <p> This API will return false in scenarios like the profile on the
+ * Bluetooth device is not in connected state etc. When this API returns,
+ * true, it is guaranteed that the connection state change
+ * intent will be broadcasted with the state. Users can get the
+ * disconnection state of the profile from this intent.
+ *
+ * <p> If the disconnection is initiated by a remote device, the state
+ * will transition from {@link #STATE_CONNECTED} to
+ * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
+ * host (local) device the state will transition from
+ * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
+ * state {@link #STATE_DISCONNECTED}. The transition to
+ * {@link #STATE_DISCONNECTING} can be used to distinguish between the
+ * two scenarios.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Remote Bluetooth Device
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean disconnect(BluetoothDevice device) {
+ if (DBG) log("disconnect(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ return mService.disconnect(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (VDBG) log("getConnectedDevices()");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getConnectedDevices();
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ if (VDBG) log("getDevicesMatchingStates()");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getDevicesMatchingConnectionStates(states);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getConnectionState(BluetoothDevice device) {
+ if (VDBG) log("getState(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getConnectionState(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.STATE_DISCONNECTED;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.STATE_DISCONNECTED;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Set priority of the profile
+ *
+ * <p> The device should already be paired.
+ * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
+ * {@link #PRIORITY_OFF},
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Paired bluetooth device
+ * @param priority
+ * @return true if priority is set, false on error
+ * @hide
+ */
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) log("setPriority(" + device + ", " + priority + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ if (priority != BluetoothProfile.PRIORITY_OFF
+ && priority != BluetoothProfile.PRIORITY_ON) {
+ return false;
+ }
+ return mService.setPriority(device, priority);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the priority of the profile.
+ *
+ * <p> The priority can be any of:
+ * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+ * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+ *
+ * @param device Bluetooth device
+ * @return priority of the device
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getPriority(BluetoothDevice device) {
+ if (VDBG) log("getPriority(" + device + ")");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getPriority(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.PRIORITY_OFF;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.PRIORITY_OFF;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Helper for converting a state to a string.
+ *
+ * For debug use only - strings are not internationalized.
+ *
+ * @hide
+ */
+ public static String stateToString(int state) {
+ switch (state) {
+ case STATE_DISCONNECTED:
+ return "disconnected";
+ case STATE_CONNECTING:
+ return "connecting";
+ case STATE_CONNECTED:
+ return "connected";
+ case STATE_DISCONNECTING:
+ return "disconnecting";
+ case STATE_PLAYING:
+ return "playing";
+ case STATE_NOT_PLAYING:
+ return "not playing";
+ default:
+ return "<unknown state " + state + ">";
+ }
+ }
+
+ /**
+ * Get the volume of the device.
+ *
+ * <p> The volume is between -128 dB (mute) to 0 dB.
+ *
+ * @return volume of the hearing aid device.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getVolume() {
+ if (VDBG) {
+ log("getVolume()");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getVolume();
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return 0;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return 0;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Tells remote device to adjust volume. Uses the following values:
+ * <ul>
+ * <li>{@link AudioManager#ADJUST_LOWER}</li>
+ * <li>{@link AudioManager#ADJUST_RAISE}</li>
+ * <li>{@link AudioManager#ADJUST_MUTE}</li>
+ * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
+ * </ul>
+ *
+ * @param direction One of the supported adjust values.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public void adjustVolume(int direction) {
+ if (DBG) log("adjustVolume(" + direction + ")");
+
+ try {
+ mServiceLock.readLock().lock();
+
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ return;
+ }
+
+ if (!isEnabled()) return;
+
+ mService.adjustVolume(direction);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Tells remote device to set an absolute volume.
+ *
+ * @param volume Absolute volume to be set on remote
+ * @hide
+ */
+ public void setVolume(int volume) {
+ if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
+
+ try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ return;
+ }
+
+ if (!isEnabled()) return;
+
+ mService.setVolume(volume);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the CustomerId of the device.
+ *
+ * @param device Bluetooth device
+ * @return the CustomerId of the device
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public long getHiSyncId(BluetoothDevice device) {
+ if (VDBG) {
+ log("getCustomerId(" + device + ")");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ return HI_SYNC_ID_INVALID;
+ }
+
+ if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
+
+ return mService.getHiSyncId(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return HI_SYNC_ID_INVALID;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the side of the device.
+ *
+ * @param device Bluetooth device.
+ * @return SIDE_LEFT or SIDE_RIGHT
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getDeviceSide(BluetoothDevice device) {
+ if (VDBG) {
+ log("getDeviceSide(" + device + ")");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getDeviceSide(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return SIDE_LEFT;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return SIDE_LEFT;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Get the mode of the device.
+ *
+ * @param device Bluetooth device
+ * @return MODE_MONAURAL or MODE_BINAURAL
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public int getDeviceMode(BluetoothDevice device) {
+ if (VDBG) {
+ log("getDeviceMode(" + device + ")");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ return mService.getDeviceMode(device);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return MODE_MONAURAL;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return MODE_MONAURAL;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) Log.d(TAG, "Proxy object connected");
+ try {
+ mServiceLock.writeLock().lock();
+ mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID,
+ BluetoothHearingAid.this);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) Log.d(TAG, "Proxy object disconnected");
+ try {
+ mServiceLock.writeLock().lock();
+ mService = null;
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID);
+ }
+ }
+ };
+
+ private boolean isEnabled() {
+ if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+ return false;
+ }
+
+ private boolean isValidDevice(BluetoothDevice device) {
+ if (device == null) return false;
+
+ if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+ return false;
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 7e3bb05fe024..11f8ab7551c2 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -17,9 +17,11 @@
package android.bluetooth;
import android.Manifest;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.util.Log;
@@ -47,6 +49,7 @@ import java.util.List;
* @see BluetoothAdapter#getDefaultAdapter()
*/
@SystemService(Context.BLUETOOTH_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_BLUETOOTH)
public final class BluetoothManager {
private static final String TAG = "BluetoothManager";
private static final boolean DBG = true;
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 0e2263f773b8..656188fbdfb8 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -165,12 +165,19 @@ public interface BluetoothProfile {
public static final int OPP = 20;
/**
+ * Hearing Aid Device
+ *
+ * @hide
+ */
+ int HEARING_AID = 21;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- public static final int MAX_PROFILE_ID = 20;
+ int MAX_PROFILE_ID = 21;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 76cb3f5b548e..0a0d21498032 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -79,6 +79,9 @@ public final class BluetoothUuid {
ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
public static final ParcelUuid SAP =
ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
+ /* TODO: b/69623109 update this value. It will change to 16bit UUID!! */
+ public static final ParcelUuid HearingAid =
+ ParcelUuid.fromString("7312C48F-22CC-497F-85FD-A0616A3B9E05");
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b85a3199881c..f184380fd36c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -560,6 +560,7 @@ public abstract class Context {
*
* @param resId Resource id for the CharSequence text
*/
+ @NonNull
public final CharSequence getText(@StringRes int resId) {
return getResources().getText(resId);
}
@@ -616,12 +617,11 @@ public abstract class Context {
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
- * @return An object that can be used to draw this resource, or
- * {@code null} if the resource could not be resolved.
+ * @return An object that can be used to draw this resource.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
- @Nullable
+ @NonNull
public final Drawable getDrawable(@DrawableRes int id) {
return getResources().getDrawable(id, getTheme());
}
@@ -633,12 +633,11 @@ public abstract class Context {
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
- * @return A color state list, or {@code null} if the resource could not be
- * resolved.
+ * @return A color state list.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
- @Nullable
+ @NonNull
public final ColorStateList getColorStateList(@ColorRes int id) {
return getResources().getColorStateList(id, getTheme());
}
@@ -3539,6 +3538,7 @@ public abstract class Context {
* @hide
*/
@SystemApi
+ @Deprecated
public static final String WIFI_RTT_SERVICE = "rttmanager";
/**
@@ -3671,10 +3671,8 @@ public abstract class Context {
*
* @see #getSystemService(String)
* @see android.telephony.euicc.EuiccManager
- * TODO(b/35851809): Unhide this API.
- * @hide
*/
- public static final String EUICC_SERVICE = "euicc_service";
+ public static final String EUICC_SERVICE = "euicc";
/**
* Use with {@link #getSystemService(String)} to retrieve a
@@ -3682,10 +3680,10 @@ public abstract class Context {
*
* @see #getSystemService(String)
* @see android.telephony.euicc.EuiccCardManager
- * TODO(b/35851809): Make this a SystemApi.
* @hide
*/
- public static final String EUICC_CARD_SERVICE = "euicc_card_service";
+ @SystemApi
+ public static final String EUICC_CARD_SERVICE = "euicc_card";
/**
* Use with {@link #getSystemService(String)} to retrieve a
@@ -4921,7 +4919,7 @@ public abstract class Context {
/**
* @hide
*/
- public void setAutofillClient(AutofillClient client) {
+ public void setAutofillClient(@SuppressWarnings("unused") AutofillClient client) {
}
/**
@@ -4934,7 +4932,9 @@ public abstract class Context {
/**
* @hide
*/
- public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
+ @TestApi
+ public void setAutofillCompatibilityEnabled(
+ @SuppressWarnings("unused") boolean autofillCompatEnabled) {
}
/**
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index a788989a7578..1867a6d879c7 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -17,6 +17,7 @@
package android.content;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.content.pm.ApplicationInfo;
@@ -1000,14 +1001,17 @@ public class ContextWrapper extends Context {
*/
@Override
public boolean isAutofillCompatibilityEnabled() {
- return mBase.isAutofillCompatibilityEnabled();
+ return mBase != null && mBase.isAutofillCompatibilityEnabled();
}
/**
* @hide
*/
+ @TestApi
@Override
public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) {
- mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
+ if (mBase != null) {
+ mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
+ }
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index fa73e3cbab99..e7aead17d6ed 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2399,6 +2399,26 @@ public class Intent implements Parcelable, Cloneable {
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
+
+ /**
+ * Broadcast Action: The current device {@link android.content.res.Configuration} has changed
+ * such that the device may be eligible for the installation of additional configuration splits.
+ * Configuration properties that can trigger this broadcast include locale and display density.
+ *
+ * <p class="note">
+ * Unlike {@link #ACTION_CONFIGURATION_CHANGED}, you <em>can</em> receive this through
+ * components declared in manifests. However, the receiver <em>must</em> hold the
+ * {@link android.Manifest.permission#INSTALL_PACKAGES} permission.
+ *
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SPLIT_CONFIGURATION_CHANGED =
+ "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
/**
* Broadcast Action: The current device's locale has changed.
*
@@ -2478,6 +2498,9 @@ public class Intent implements Parcelable, Cloneable {
* off, not sleeping). Once the broadcast is complete, the final shutdown
* will proceed and all unsaved data lost. Apps will not normally need
* to handle this, since the foreground activity will be paused as well.
+ * <p>As of {@link Build.VERSION_CODES#P} this broadcast is only sent to receivers registered
+ * through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
+ * Context.registerReceiver}.
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 86c1aa8228f1..5b3c9dd93370 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -83,17 +83,36 @@ interface IOverlayManager {
* @param packageName The name of the overlay package.
* @param enable true to enable the overlay, false to disable it.
* @param userId The user for which to change the overlay.
- * @return true if the system successfully registered the request, false
- * otherwise.
+ * @return true if the system successfully registered the request, false otherwise.
*/
boolean setEnabled(in String packageName, in boolean enable, in int userId);
/**
- * Version of setEnabled that will also disable any other overlays for the target package.
+ * Request that an overlay package is enabled and any other overlay packages with the same
+ * target package are disabled.
+ *
+ * See {@link #setEnabled} for the details on overlay packages.
+ *
+ * @param packageName the name of the overlay package to enable.
+ * @param enabled must be true, otherwise the operation fails.
+ * @param userId The user for which to change the overlay.
+ * @return true if the system successfully registered the request, false otherwise.
*/
boolean setEnabledExclusive(in String packageName, in boolean enable, in int userId);
/**
+ * Request that an overlay package is enabled and any other overlay packages with the same
+ * target package and category are disabled.
+ *
+ * See {@link #setEnabled} for the details on overlay packages.
+ *
+ * @param packageName the name of the overlay package to enable.
+ * @param userId The user for which to change the overlay.
+ * @return true if the system successfully registered the request, false otherwise.
+ */
+ boolean setEnabledExclusiveInCategory(in String packageName, in int userId);
+
+ /**
* Change the priority of the given overlay to be just higher than the
* overlay with package name newParentPackageName. Both overlay packages
* must have the same target and user.
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 8464e26ec6cd..6e63342698b3 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -18,6 +18,7 @@ package android.content.om;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -66,14 +67,14 @@ public final class OverlayInfo implements Parcelable {
/**
* The overlay is currently disabled. It can be enabled.
*
- * @see IOverlayManager.setEnabled
+ * @see IOverlayManager#setEnabled
*/
public static final int STATE_DISABLED = 2;
/**
* The overlay is currently enabled. It can be disabled.
*
- * @see IOverlayManager.setEnabled
+ * @see IOverlayManager#setEnabled
*/
public static final int STATE_ENABLED = 3;
@@ -90,6 +91,11 @@ public final class OverlayInfo implements Parcelable {
public static final int STATE_OVERLAY_UPGRADING = 5;
/**
+ * Category for theme overlays.
+ */
+ public static final String CATEGORY_THEME = "android.theme";
+
+ /**
* Package name of the overlay package
*/
public final String packageName;
@@ -100,6 +106,11 @@ public final class OverlayInfo implements Parcelable {
public final String targetPackageName;
/**
+ * Category of the overlay package
+ */
+ public final String category;
+
+ /**
* Full path to the base APK for this overlay package
*/
public final String baseCodePath;
@@ -121,14 +132,15 @@ public final class OverlayInfo implements Parcelable {
* @param state the new state for the source OverlayInfo
*/
public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
- this(source.packageName, source.targetPackageName, source.baseCodePath, state,
- source.userId);
+ this(source.packageName, source.targetPackageName, source.category, source.baseCodePath,
+ state, source.userId);
}
public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
- @NonNull String baseCodePath, @State int state, int userId) {
+ @Nullable String category, @NonNull String baseCodePath, int state, int userId) {
this.packageName = packageName;
this.targetPackageName = targetPackageName;
+ this.category = category;
this.baseCodePath = baseCodePath;
this.state = state;
this.userId = userId;
@@ -138,6 +150,7 @@ public final class OverlayInfo implements Parcelable {
public OverlayInfo(Parcel source) {
packageName = source.readString();
targetPackageName = source.readString();
+ category = source.readString();
baseCodePath = source.readString();
state = source.readInt();
userId = source.readInt();
@@ -177,6 +190,7 @@ public final class OverlayInfo implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(packageName);
dest.writeString(targetPackageName);
+ dest.writeString(category);
dest.writeString(baseCodePath);
dest.writeInt(state);
dest.writeInt(userId);
@@ -275,6 +289,9 @@ public final class OverlayInfo implements Parcelable {
if (!targetPackageName.equals(other.targetPackageName)) {
return false;
}
+ if (!category.equals(other.category)) {
+ return false;
+ }
if (!baseCodePath.equals(other.baseCodePath)) {
return false;
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index e115f896137c..0e50002288cd 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1631,7 +1631,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* @hide
*/
public boolean isAllowedToUseHiddenApi() {
- return isSystemApp();
+ return isSystemApp() || isUpdatedSystemApp();
}
/**
diff --git a/core/java/android/content/pm/InstantAppRequest.java b/core/java/android/content/pm/InstantAppRequest.java
index 38f02256ee6e..361d4e4acb3b 100644
--- a/core/java/android/content/pm/InstantAppRequest.java
+++ b/core/java/android/content/pm/InstantAppRequest.java
@@ -18,12 +18,14 @@ package android.content.pm;
import android.content.Intent;
import android.os.Bundle;
+import android.text.TextUtils;
/**
* Information needed to make an instant application resolution request.
* @hide
*/
public final class InstantAppRequest {
+
/** Response from the first phase of instant application resolution */
public final AuxiliaryResolveInfo responseObj;
/** The original intent that triggered instant application resolution */
@@ -40,6 +42,8 @@ public final class InstantAppRequest {
public final Bundle verificationBundle;
/** Whether resolution occurs because an application is starting */
public final boolean resolveForStart;
+ /** The instant app digest for this request */
+ public final InstantAppResolveInfo.InstantAppDigest digest;
public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
String resolvedType, String callingPackage, int userId, Bundle verificationBundle,
@@ -51,5 +55,11 @@ public final class InstantAppRequest {
this.userId = userId;
this.verificationBundle = verificationBundle;
this.resolveForStart = resolveForStart;
+ if (origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())) {
+ digest = new InstantAppResolveInfo.InstantAppDigest(
+ origIntent.getData().getHost(), 5 /*maxDigests*/);
+ } else {
+ digest = InstantAppResolveInfo.InstantAppDigest.UNDEFINED;
+ }
}
}
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 112c5dae6731..3a95a5f87d97 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -26,10 +26,13 @@ import android.os.Parcelable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Random;
/**
* Describes an externally resolvable instant application. There are three states that this class
@@ -227,14 +230,25 @@ public final class InstantAppResolveInfo implements Parcelable {
*/
@SystemApi
public static final class InstantAppDigest implements Parcelable {
- private static final int DIGEST_MASK = 0xfffff000;
-
+ static final int DIGEST_MASK = 0xfffff000;
public static final InstantAppDigest UNDEFINED =
new InstantAppDigest(new byte[][]{}, new int[]{});
+
+ private static Random sRandom = null;
+ static {
+ try {
+ sRandom = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ // oh well
+ sRandom = new Random();
+ }
+ }
/** Full digest of the domain hashes */
private final byte[][] mDigestBytes;
- /** The first 4 bytes of the domain hashes */
+ /** The first 5 bytes of the domain hashes */
private final int[] mDigestPrefix;
+ /** The first 5 bytes of the domain hashes interspersed with random data */
+ private int[] mDigestPrefixSecure;
public InstantAppDigest(@NonNull String hostName) {
this(hostName, -1 /*maxDigests*/);
@@ -306,6 +320,7 @@ public final class InstantAppResolveInfo implements Parcelable {
}
}
mDigestPrefix = in.createIntArray();
+ mDigestPrefixSecure = in.createIntArray();
}
public byte[][] getDigestBytes() {
@@ -316,6 +331,26 @@ public final class InstantAppResolveInfo implements Parcelable {
return mDigestPrefix;
}
+ /**
+ * Returns a digest prefix with additional random prefixes interspersed.
+ * @hide
+ */
+ public int[] getDigestPrefixSecure() {
+ if (this == InstantAppResolveInfo.InstantAppDigest.UNDEFINED) {
+ return getDigestPrefix();
+ } else if (mDigestPrefixSecure == null) {
+ // let's generate some random data to intersperse throughout the set of prefixes
+ final int realSize = getDigestPrefix().length;
+ final int manufacturedSize = realSize + 10 + sRandom.nextInt(10);
+ mDigestPrefixSecure = Arrays.copyOf(getDigestPrefix(), manufacturedSize);
+ for (int i = realSize; i < manufacturedSize; i++) {
+ mDigestPrefixSecure[i] = sRandom.nextInt() & DIGEST_MASK;
+ }
+ Arrays.sort(mDigestPrefixSecure);
+ }
+ return mDigestPrefixSecure;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -323,6 +358,11 @@ public final class InstantAppResolveInfo implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
+ final boolean isUndefined = this == UNDEFINED;
+ out.writeBoolean(isUndefined);
+ if (isUndefined) {
+ return;
+ }
if (mDigestBytes == null) {
out.writeInt(-1);
} else {
@@ -332,6 +372,7 @@ public final class InstantAppResolveInfo implements Parcelable {
}
}
out.writeIntArray(mDigestPrefix);
+ out.writeIntArray(mDigestPrefixSecure);
}
@SuppressWarnings("hiding")
@@ -339,6 +380,9 @@ public final class InstantAppResolveInfo implements Parcelable {
new Parcelable.Creator<InstantAppDigest>() {
@Override
public InstantAppDigest createFromParcel(Parcel in) {
+ if (in.readBoolean() /* is undefined */) {
+ return UNDEFINED;
+ }
return new InstantAppDigest(in);
}
@Override
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 09a46b8acf4b..627ceb7871f2 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -362,6 +362,13 @@ public class PackageInfo implements Parcelable {
*/
public String overlayTarget;
+ /**
+ * The overlay category, if any, of this package
+ *
+ * @hide
+ */
+ public String overlayCategory;
+
/** @hide */
public int overlayPriority;
@@ -464,10 +471,23 @@ public class PackageInfo implements Parcelable {
dest.writeString(restrictedAccountType);
dest.writeString(requiredAccountType);
dest.writeString(overlayTarget);
+ dest.writeString(overlayCategory);
dest.writeInt(overlayPriority);
dest.writeBoolean(mOverlayIsStatic);
dest.writeInt(compileSdkVersion);
dest.writeString(compileSdkVersionCodename);
+ writeSigningCertificateHistoryToParcel(dest, parcelableFlags);
+ }
+
+ private void writeSigningCertificateHistoryToParcel(Parcel dest, int parcelableFlags) {
+ if (signingCertificateHistory != null) {
+ dest.writeInt(signingCertificateHistory.length);
+ for (int i = 0; i < signingCertificateHistory.length; i++) {
+ dest.writeTypedArray(signingCertificateHistory[i], parcelableFlags);
+ }
+ } else {
+ dest.writeInt(-1);
+ }
}
public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -519,10 +539,12 @@ public class PackageInfo implements Parcelable {
restrictedAccountType = source.readString();
requiredAccountType = source.readString();
overlayTarget = source.readString();
+ overlayCategory = source.readString();
overlayPriority = source.readInt();
mOverlayIsStatic = source.readBoolean();
compileSdkVersion = source.readInt();
compileSdkVersionCodename = source.readString();
+ readSigningCertificateHistoryFromParcel(source);
// The component lists were flattened with the redundant ApplicationInfo
// instances omitted. Distribute the canonical one here as appropriate.
@@ -534,6 +556,16 @@ public class PackageInfo implements Parcelable {
}
}
+ private void readSigningCertificateHistoryFromParcel(Parcel source) {
+ int len = source.readInt();
+ if (len != -1) {
+ signingCertificateHistory = new Signature[len][];
+ for (int i = 0; i < len; i++) {
+ signingCertificateHistory[i] = source.createTypedArray(Signature.CREATOR);
+ }
+ }
+ }
+
private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
if (components != null) {
for (ComponentInfo ci : components) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 486c86cedba3..07a991188a49 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2070,6 +2070,15 @@ public abstract class PackageManager {
"android.hardware.sensor.hifi_sensors";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports a hardware mechanism for invoking an assist gesture.
+ * @see android.provider.Settings.Secure#ASSIST_GESTURE_ENABLED
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_ASSIST_GESTURE = "android.hardware.sensor.assist";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a telephony radio with data
* communication support.
@@ -2108,8 +2117,6 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
* supports embedded subscriptions on eUICCs.
- * TODO(b/35851809): Make this public.
- * @hide
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
@@ -5067,6 +5074,7 @@ public abstract class PackageManager {
* which market the package came from.
*
* @param packageName The name of the package to query
+ * @throws IllegalArgumentException if the given package name is not installed
*/
public abstract String getInstallerPackageName(String packageName);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3afc346af36e..9e4166eaec1b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -196,10 +196,6 @@ public class PackageParser {
private static final String TAG_RESTRICT_UPDATE = "restrict-update";
private static final String TAG_USES_SPLIT = "uses-split";
- // [b/36551762] STOPSHIP remove the ability to expose components via meta-data
- // Temporary workaround; allow meta-data to expose components to instant apps
- private static final String META_DATA_INSTANT_APPS = "instantapps.clients.allowed";
-
private static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
/**
@@ -680,6 +676,7 @@ public class PackageParser {
pi.restrictedAccountType = p.mRestrictedAccountType;
pi.requiredAccountType = p.mRequiredAccountType;
pi.overlayTarget = p.mOverlayTarget;
+ pi.overlayCategory = p.mOverlayCategory;
pi.overlayPriority = p.mOverlayPriority;
pi.mOverlayIsStatic = p.mOverlayIsStatic;
pi.compileSdkVersion = p.mCompileSdkVersion;
@@ -2077,6 +2074,8 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestResourceOverlay);
pkg.mOverlayTarget = sa.getString(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
+ pkg.mOverlayCategory = sa.getString(
+ com.android.internal.R.styleable.AndroidManifestResourceOverlay_category);
pkg.mOverlayPriority = sa.getInt(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
0);
@@ -4430,24 +4429,6 @@ public class PackageParser {
outError)) == null) {
return null;
}
- // we don't have an attribute [or it's false], but, we have meta-data
- if (!visibleToEphemeral && a.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
- visibleToEphemeral = true; // set in case there are more intent filters
- a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- a.info.flags &= ~ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
- owner.visibleToInstantApps = true;
- // cycle through any filters already seen
- for (int i = a.intents.size() - 1; i >= 0; --i) {
- a.intents.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- if (owner.preferredActivityFilters != null) {
- for (int i = owner.preferredActivityFilters.size() - 1; i >= 0; --i) {
- owner.preferredActivityFilters.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- }
- }
} else if (!receiver && parser.getName().equals("layout")) {
parseLayout(res, parser, a);
} else {
@@ -4942,7 +4923,7 @@ public class PackageParser {
p.info.authority = cpname.intern();
if (!parseProviderTags(
- res, parser, visibleToEphemeral, owner, p, outError)) {
+ res, parser, visibleToEphemeral, p, outError)) {
return null;
}
@@ -4950,7 +4931,7 @@ public class PackageParser {
}
private boolean parseProviderTags(Resources res, XmlResourceParser parser,
- boolean visibleToEphemeral, Package owner, Provider outInfo, String[] outError)
+ boolean visibleToEphemeral, Provider outInfo, String[] outError)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -4978,17 +4959,6 @@ public class PackageParser {
outInfo.metaData, outError)) == null) {
return false;
}
- // we don't have an attribute [or it's false], but, we have meta-data
- if (!visibleToEphemeral && outInfo.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
- visibleToEphemeral = true; // set in case there are more intent filters
- outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- owner.visibleToInstantApps = true;
- // cycle through any filters already seen
- for (int i = outInfo.intents.size() - 1; i >= 0; --i) {
- outInfo.intents.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- }
} else if (parser.getName().equals("grant-uri-permission")) {
TypedArray sa = res.obtainAttributes(parser,
@@ -5277,17 +5247,6 @@ public class PackageParser {
outError)) == null) {
return null;
}
- // we don't have an attribute [or it's false], but, we have meta-data
- if (!visibleToEphemeral && s.metaData.getBoolean(META_DATA_INSTANT_APPS)) {
- visibleToEphemeral = true; // set in case there are more intent filters
- s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- owner.visibleToInstantApps = true;
- // cycle through any filters already seen
- for (int i = s.intents.size() - 1; i >= 0; --i) {
- s.intents.get(i)
- .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- }
- }
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <service>: "
@@ -6368,6 +6327,7 @@ public class PackageParser {
public String mRequiredAccountType;
public String mOverlayTarget;
+ public String mOverlayCategory;
public int mOverlayPriority;
public boolean mOverlayIsStatic;
@@ -6878,6 +6838,7 @@ public class PackageParser {
mRestrictedAccountType = dest.readString();
mRequiredAccountType = dest.readString();
mOverlayTarget = dest.readString();
+ mOverlayCategory = dest.readString();
mOverlayPriority = dest.readInt();
mOverlayIsStatic = (dest.readInt() == 1);
mCompileSdkVersion = dest.readInt();
@@ -7001,6 +6962,7 @@ public class PackageParser {
dest.writeString(mRestrictedAccountType);
dest.writeString(mRequiredAccountType);
dest.writeString(mOverlayTarget);
+ dest.writeString(mOverlayCategory);
dest.writeInt(mOverlayPriority);
dest.writeInt(mOverlayIsStatic ? 1 : 0);
dest.writeInt(mCompileSdkVersion);
diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.java b/core/java/android/content/pm/VerifierDeviceIdentity.java
index a8cdb6ae8af6..90be6f316ce6 100644
--- a/core/java/android/content/pm/VerifierDeviceIdentity.java
+++ b/core/java/android/content/pm/VerifierDeviceIdentity.java
@@ -19,6 +19,8 @@ package android.content.pm;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Random;
@@ -86,6 +88,7 @@ public class VerifierDeviceIdentity implements Parcelable {
* @return verifier device identity based on the input from the provided
* random number generator
*/
+ @VisibleForTesting
static VerifierDeviceIdentity generate(Random rng) {
long identity = rng.nextLong();
return new VerifierDeviceIdentity(identity);
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
index 02d0a6d8bd36..79bc9a30b1e2 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -274,6 +274,7 @@ public final class RuntimePermissionPresenter {
}
}
+ @GuardedBy("mLock")
private void scheduleNextMessageIfNeededLocked() {
if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
Message nextMessage = mPendingWork.remove(0);
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 78665609bdd4..5f8a34d46ecd 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -28,6 +28,8 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -915,6 +917,7 @@ public final class AssetManager implements AutoCloseable {
private native final void init(boolean isSystem);
private native final void destroy();
+ @GuardedBy("this")
private final void incRefsLocked(long id) {
if (DEBUG_REFS) {
if (mRefStacks == null) {
@@ -926,7 +929,8 @@ public final class AssetManager implements AutoCloseable {
}
mNumRefs++;
}
-
+
+ @GuardedBy("this")
private final void decRefsLocked(long id) {
if (DEBUG_REFS && mRefStacks != null) {
mRefStacks.remove(id);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index e173653cd961..ad85e71b86f9 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -867,8 +867,9 @@ public class Resources {
* @param theme The theme used to style the drawable attributes, may be {@code null}.
* @return Drawable An object that can be used to draw this resource.
* @throws NotFoundException Throws NotFoundException if the given ID does
- * not exist.
+ * not exist, or cannot be decoded.
*/
+ @NonNull
public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
final TypedValue value = obtainTempTypedValue();
try {
@@ -980,7 +981,7 @@ public class Resources {
* or multiple colors that can be selected based on a state.
* @deprecated Use {@link #getColorStateList(int, Theme)} instead.
*/
- @Nullable
+ @NonNull
@Deprecated
public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
final ColorStateList csl = getColorStateList(id, null);
@@ -1011,7 +1012,7 @@ public class Resources {
* @return A themed ColorStateList object containing either a single solid
* color or multiple colors that can be selected based on a state.
*/
- @Nullable
+ @NonNull
public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
@@ -1024,7 +1025,7 @@ public class Resources {
}
}
- @Nullable
+ @NonNull
ColorStateList loadColorStateList(@NonNull TypedValue value, int id, @Nullable Theme theme)
throws NotFoundException {
return mResourcesImpl.loadColorStateList(this, value, id, theme);
@@ -1033,7 +1034,7 @@ public class Resources {
/**
* @hide
*/
- @Nullable
+ @NonNull
public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, @Nullable Theme theme) {
return mResourcesImpl.loadComplexColor(this, value, id, theme);
}
@@ -1139,6 +1140,7 @@ public class Resources {
*
* @see #getXml
*/
+ @NonNull
public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "layout");
}
@@ -1163,6 +1165,7 @@ public class Resources {
*
* @see #getXml
*/
+ @NonNull
public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "anim");
}
@@ -1188,6 +1191,7 @@ public class Resources {
*
* @see android.util.AttributeSet
*/
+ @NonNull
public XmlResourceParser getXml(@XmlRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "xml");
}
@@ -1203,8 +1207,8 @@ public class Resources {
* @return InputStream Access to the resource data.
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
- *
*/
+ @NonNull
public InputStream openRawResource(@RawRes int id) throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
try {
@@ -1261,6 +1265,7 @@ public class Resources {
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
+ @NonNull
public InputStream openRawResource(@RawRes int id, TypedValue value)
throws NotFoundException {
return mResourcesImpl.openRawResource(id, value);
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 97cb78bc4243..424fa833cd48 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -27,9 +27,11 @@ import android.annotation.StyleRes;
import android.annotation.StyleableRes;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
+import android.content.res.AssetManager.AssetInputStream;
import android.content.res.Configuration.NativeConfig;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
+import android.graphics.ImageDecoder;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -543,7 +545,7 @@ public class ResourcesImpl {
}
}
- @Nullable
+ @NonNull
Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
int density, @Nullable Resources.Theme theme)
throws NotFoundException {
@@ -627,7 +629,7 @@ public class ResourcesImpl {
} else if (isColorDrawable) {
dr = new ColorDrawable(value.data);
} else {
- dr = loadDrawableForCookie(wrapper, value, id, density, null);
+ dr = loadDrawableForCookie(wrapper, value, id, density);
}
// DrawableContainer' constant state has drawables instances. In order to leave the
// constant state intact in the cache, we need to create a new DrawableContainer after
@@ -752,10 +754,31 @@ public class ResourcesImpl {
}
/**
+ * Loads a Drawable from an encoded image stream, or null.
+ *
+ * This call will handle closing ais.
+ */
+ private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
+ @NonNull Resources wrapper, @NonNull TypedValue value) {
+ ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
+ wrapper, value);
+ try {
+ return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ } catch (IOException ioe) {
+ // This is okay. This may be something that ImageDecoder does not
+ // support, like SVG.
+ return null;
+ }
+ }
+
+ /**
* Loads a drawable from XML or resources stream.
*/
+ @NonNull
private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
- int id, int density, @Nullable Resources.Theme theme) {
+ int id, int density) {
if (value.string == null) {
throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
@@ -774,22 +797,23 @@ public class ResourcesImpl {
}
}
- // For prelaod tracing.
+ // For preload tracing.
long startTime = 0;
int startBitmapCount = 0;
long startBitmapSize = 0;
- int startDrwableCount = 0;
+ int startDrawableCount = 0;
if (TRACE_FOR_DETAILED_PRELOAD) {
startTime = System.nanoTime();
startBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
startBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
- startDrwableCount = sPreloadTracingNumLoadedDrawables;
+ startDrawableCount = sPreloadTracingNumLoadedDrawables;
}
if (DEBUG_LOAD) {
Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
}
+
final Drawable dr;
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
@@ -804,13 +828,13 @@ public class ResourcesImpl {
if (file.endsWith(".xml")) {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "drawable");
- dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
+ dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
rp.close();
} else {
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
- is.close();
+ AssetInputStream ais = (AssetInputStream) is;
+ dr = decodeImageDrawable(ais, wrapper, value);
}
} finally {
stack.pop();
@@ -834,7 +858,7 @@ public class ResourcesImpl {
final long loadedBitmapSize =
Bitmap.sPreloadTracingTotalBitmapsSize - startBitmapSize;
final int loadedDrawables =
- sPreloadTracingNumLoadedDrawables - startDrwableCount;
+ sPreloadTracingNumLoadedDrawables - startDrawableCount;
sPreloadTracingNumLoadedDrawables++;
@@ -910,6 +934,7 @@ public class ResourcesImpl {
* first try to load CSL from the cache. If not found, try to get from the constant state.
* Last, parse the XML and generate the CSL.
*/
+ @Nullable
private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
TypedValue value, int id) {
final long key = (((long) value.assetCookie) << 32) | value.data;
@@ -985,7 +1010,7 @@ public class ResourcesImpl {
return complexColor;
}
- @Nullable
+ @NonNull
ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id,
Resources.Theme theme)
throws NotFoundException {
@@ -1043,9 +1068,10 @@ public class ResourcesImpl {
* We deferred the parser creation to this function b/c we need to differentiate b/t gradient
* and selector tag.
*
- * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
+ * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content, or
+ * {@code null} if the XML file is neither.
*/
- @Nullable
+ @NonNull
private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
Resources.Theme theme) {
if (value.string == null) {
diff --git a/core/java/android/content/res/XmlResourceParser.java b/core/java/android/content/res/XmlResourceParser.java
index 6be9b9eb0438..86f4ba6189de 100644
--- a/core/java/android/content/res/XmlResourceParser.java
+++ b/core/java/android/content/res/XmlResourceParser.java
@@ -27,6 +27,8 @@ import org.xmlpull.v1.XmlPullParser;
* it is done reading the resource.
*/
public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
+ String getAttributeNamespace (int index);
+
/**
* Close this parser. Calls on the interface are no longer valid after this call.
*/
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index b211700328b9..dc60612451d4 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -422,6 +422,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ @GuardedBy("mLock")
private boolean recycleConnectionLocked(SQLiteConnection connection,
AcquiredConnectionStatus status) {
if (status == AcquiredConnectionStatus.RECONFIGURE) {
@@ -531,6 +532,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeAvailableConnectionsAndLogExceptionsLocked() {
closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
@@ -541,6 +543,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ @GuardedBy("mLock")
private boolean closeAvailableConnectionLocked(int connectionId) {
final int count = mAvailableNonPrimaryConnections.size();
for (int i = count - 1; i >= 0; i--) {
@@ -562,6 +565,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() {
final int count = mAvailableNonPrimaryConnections.size();
for (int i = 0; i < count; i++) {
@@ -581,6 +585,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeExcessConnectionsAndLogExceptionsLocked() {
int availableCount = mAvailableNonPrimaryConnections.size();
while (availableCount-- > mMaxConnectionPoolSize - 1) {
@@ -591,6 +596,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) {
try {
connection.close(); // might throw
@@ -609,6 +615,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ @GuardedBy("mLock")
private void reconfigureAllConnectionsLocked() {
if (mAvailablePrimaryConnection != null) {
try {
@@ -776,6 +783,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ @GuardedBy("mLock")
private void cancelConnectionWaiterLocked(ConnectionWaiter waiter) {
if (waiter.mAssignedConnection != null || waiter.mException != null) {
// Waiter is done waiting but has not woken up yet.
@@ -848,6 +856,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Can't throw.
+ @GuardedBy("mLock")
private void wakeConnectionWaitersLocked() {
// Unpark all waiters that have requests that we can fulfill.
// This method is designed to not throw runtime exceptions, although we might send
@@ -910,6 +919,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Might throw.
+ @GuardedBy("mLock")
private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) {
// If the primary connection is available, acquire it now.
SQLiteConnection connection = mAvailablePrimaryConnection;
@@ -935,6 +945,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Might throw.
+ @GuardedBy("mLock")
private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
String sql, int connectionFlags) {
// Try to acquire the next connection in the queue.
@@ -974,6 +985,7 @@ public final class SQLiteConnectionPool implements Closeable {
}
// Might throw.
+ @GuardedBy("mLock")
private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) {
try {
final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0;
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index c1c0812e129e..ae1f57d62228 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2006,7 +2006,6 @@ public final class SQLiteDatabase extends SQLiteClosable {
* SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
* SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING,
* myDatabaseErrorHandler);
- * db.enableWriteAheadLogging();
* </pre></code>
* </p><p>
* Another way to enable write-ahead logging is to call {@link #enableWriteAheadLogging}
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index a2991e6e9cb6..64e9e5db7c4b 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -58,7 +58,7 @@ public abstract class SQLiteOpenHelper {
private SQLiteDatabase mDatabase;
private boolean mIsInitializing;
- private final SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder;
+ private SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder;
/**
* Create a helper object to create, open, and/or manage a database.
@@ -163,8 +163,7 @@ public abstract class SQLiteOpenHelper {
mName = name;
mNewVersion = version;
mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
- mOpenParamsBuilder = openParamsBuilder;
- mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
+ setOpenParamsBuilder(openParamsBuilder);
}
/**
@@ -230,6 +229,30 @@ public abstract class SQLiteOpenHelper {
}
/**
+ * Sets configuration parameters that are used for opening {@link SQLiteDatabase}.
+ * <p>Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be set when
+ * opening the database
+ *
+ * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
+ * @throws IllegalStateException if the database is already open
+ */
+ public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) {
+ Preconditions.checkNotNull(openParams);
+ synchronized (this) {
+ if (mDatabase != null && mDatabase.isOpen()) {
+ throw new IllegalStateException(
+ "OpenParams cannot be set after opening the database");
+ }
+ setOpenParamsBuilder(new SQLiteDatabase.OpenParams.Builder(openParams));
+ }
+ }
+
+ private void setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
+ mOpenParamsBuilder = openParamsBuilder;
+ mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
+ }
+
+ /**
* Sets the maximum number of milliseconds that SQLite connection is allowed to be idle
* before it is closed and removed from the pool.
*
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 931b5c913851..f08e1cc24b26 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -242,6 +242,9 @@ public class Camera {
/**
* Returns the number of physical cameras available on this device.
+ * The return value of this method might change dynamically if the device
+ * supports external cameras and an external camera is connected or
+ * disconnected.
*
* @return total number of accessible camera devices, or 0 if there are no
* cameras or an error was encountered enumerating them.
@@ -3542,8 +3545,8 @@ public class Camera {
/**
* Gets the horizontal angle of view in degrees.
*
- * @return horizontal angle of view. This method will always return a
- * valid value.
+ * @return horizontal angle of view. Returns -1.0 when the device
+ * doesn't report view angle information.
*/
public float getHorizontalViewAngle() {
return Float.parseFloat(get(KEY_HORIZONTAL_VIEW_ANGLE));
@@ -3552,8 +3555,8 @@ public class Camera {
/**
* Gets the vertical angle of view in degrees.
*
- * @return vertical angle of view. This method will always return a
- * valid value.
+ * @return vertical angle of view. Returns -1.0 when the device
+ * doesn't report view angle information.
*/
public float getVerticalViewAngle() {
return Float.parseFloat(get(KEY_VERTICAL_VIEW_ANGLE));
diff --git a/core/java/android/hardware/ConsumerIrManager.java b/core/java/android/hardware/ConsumerIrManager.java
index c7a33ffa1b0f..6f589cd9190b 100644
--- a/core/java/android/hardware/ConsumerIrManager.java
+++ b/core/java/android/hardware/ConsumerIrManager.java
@@ -16,8 +16,10 @@
package android.hardware;
+import android.annotation.RequiresFeature;
import android.annotation.SystemService;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -27,6 +29,7 @@ import android.util.Log;
* Class that operates consumer infrared on the device.
*/
@SystemService(Context.CONSUMER_IR_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_CONSUMER_IR)
public final class ConsumerIrManager {
private static final String TAG = "ConsumerIr";
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
new file mode 100644
index 000000000000..b8fea556e4c5
--- /dev/null
+++ b/core/java/android/hardware/OWNERS
@@ -0,0 +1,7 @@
+# Camera
+per-file *Camera* = cychen@google.com
+per-file *Camera* = epeev@google.com
+per-file *Camera* = etalvala@google.com
+per-file *Camera* = shuzhenwang@google.com
+per-file *Camera* = yinchiayeh@google.com
+per-file *Camera* = zhijunhe@google.com
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 96d043c2a894..8502fc413c05 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -341,7 +341,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
*/
@SuppressWarnings({"unchecked"})
public List<CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys() {
- if (mAvailableSessionKeys == null) {
+ if (mAvailablePhysicalRequestKeys == null) {
Object crKey = CaptureRequest.Key.class;
Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
@@ -1372,9 +1372,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>The origin for {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p>
* <p>Different calibration methods and use cases can produce better or worse results
* depending on the selected coordinate origin.</p>
- * <p>For devices designed to support the MOTION_TRACKING capability, the GYROSCOPE origin
- * makes device calibration and later usage by applications combining camera and gyroscope
- * information together simpler.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #LENS_POSE_REFERENCE_PRIMARY_CAMERA PRIMARY_CAMERA}</li>
@@ -1793,11 +1790,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* The respective value of such request key can be obtained by calling
* {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
* individual physical device requests must be built via
- * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
- * Such extended capture requests can be passed only to
- * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
- * not to {@link CameraCaptureSession#setRepeatingRequest } or
- * {@link CameraCaptureSession#setRepeatingBurst }.</p>
+ * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Limited capability</b> -
* Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index df644012ffb7..72db33f90c20 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -145,37 +145,6 @@ public abstract class CameraDevice implements AutoCloseable {
*/
public static final int TEMPLATE_MANUAL = 6;
- /**
- * A template for selecting camera parameters that match TEMPLATE_PREVIEW as closely as
- * possible while improving the camera output for motion tracking use cases.
- *
- * <p>This template is best used by applications that are frequently switching between motion
- * tracking use cases and regular still capture use cases, to minimize the IQ changes
- * when swapping use cases.</p>
- *
- * <p>This template is guaranteed to be supported on camera devices that support the
- * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}
- * capability.</p>
- *
- * @see #createCaptureRequest
- */
- public static final int TEMPLATE_MOTION_TRACKING_PREVIEW = 7;
-
- /**
- * A template for selecting camera parameters that maximize the quality of camera output for
- * motion tracking use cases.
- *
- * <p>This template is best used by applications dedicated to motion tracking applications,
- * which aren't concerned about fast switches between motion tracking and other use cases.</p>
- *
- * <p>This template is guaranteed to be supported on camera devices that support the
- * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}
- * capability.</p>
- *
- * @see #createCaptureRequest
- */
- public static final int TEMPLATE_MOTION_TRACKING_BEST = 8;
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"TEMPLATE_"}, value =
@@ -184,9 +153,7 @@ public abstract class CameraDevice implements AutoCloseable {
TEMPLATE_RECORD,
TEMPLATE_VIDEO_SNAPSHOT,
TEMPLATE_ZERO_SHUTTER_LAG,
- TEMPLATE_MANUAL,
- TEMPLATE_MOTION_TRACKING_PREVIEW,
- TEMPLATE_MOTION_TRACKING_BEST})
+ TEMPLATE_MANUAL})
public @interface RequestTemplate {};
/**
@@ -420,27 +387,6 @@ public abstract class CameraDevice implements AutoCloseable {
* </table><br>
* </p>
*
- * <p>MOTION_TRACKING-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
- * includes
- * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING})
- * devices support at least the below stream combinations in addition to those for
- * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices. The
- * {@code FULL FOV 640} entry means that the device will support a resolution that's 640 pixels
- * wide, with the height set so that the resolution aspect ratio matches the MAXIMUM output
- * aspect ratio, rounded down. So for a device with a 4:3 image sensor, this will be 640x480,
- * and for a device with a 16:9 sensor, this will be 640x360, and so on. And the
- * {@code MAX 30FPS} entry means the largest JPEG resolution on the device for which
- * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration}
- * returns a value less than or equal to 1/30s.
- *
- * <table>
- * <tr><th colspan="7">MOTION_TRACKING-capability additional guaranteed configurations</th></tr>
- * <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th rowspan="2">Sample use case(s)</th> </tr>
- * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr>
- * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV }</td><td id="rb">{@code FULL FOV 640}</td> <td>{@code JPEG}</td><td id="rb">{@code MAX 30FPS}</td> <td>Preview with a tracking YUV output and a as-large-as-possible JPEG for still captures.</td> </tr>
- * </table><br>
- * </p>
- *
* <p>BURST-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes
* {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}) devices
* support at least the below stream combinations in addition to those for
@@ -917,11 +863,8 @@ public abstract class CameraDevice implements AutoCloseable {
* request for a specific physical camera. The settings are chosen
* to be the best options for the specific logical camera device. If
* additional physical camera ids are passed, then they will also use the
- * same settings template. Requests containing individual physical camera
- * settings can be passed only to {@link CameraCaptureSession#capture} or
- * {@link CameraCaptureSession#captureBurst} and not to
- * {@link CameraCaptureSession#setRepeatingRequest} or
- * {@link CameraCaptureSession#setRepeatingBurst}</p>
+ * same settings template. Clients can further modify individual camera
+ * settings by calling {@link CaptureRequest.Builder#setPhysicalCameraKey}.</p>
*
* <p>Individual physical camera settings will only be honored for camera session
* that was initialiazed with corresponding physical camera id output configuration
@@ -950,8 +893,8 @@ public abstract class CameraDevice implements AutoCloseable {
* @see #TEMPLATE_STILL_CAPTURE
* @see #TEMPLATE_VIDEO_SNAPSHOT
* @see #TEMPLATE_MANUAL
- * @see CaptureRequest.Builder#setKey
- * @see CaptureRequest.Builder#getKey
+ * @see CaptureRequest.Builder#setPhysicalCameraKey
+ * @see CaptureRequest.Builder#getPhysicalCameraKey
*/
@NonNull
public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType,
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index e7c8961186ec..e558b7e29a20 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -342,7 +342,7 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the optical center of
* the largest camera device facing the same direction as this camera.</p>
- * <p>This default value for API levels before Android P.</p>
+ * <p>This is the default value for API levels before Android P.</p>
*
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
* @see CameraCharacteristics#LENS_POSE_REFERENCE
@@ -352,7 +352,6 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the position of the
* primary gyroscope of this Android device.</p>
- * <p>This is the value reported by all devices that support the MOTION_TRACKING capability.</p>
*
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
* @see CameraCharacteristics#LENS_POSE_REFERENCE
@@ -801,46 +800,12 @@ public abstract class CameraMetadata<TKey> {
public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9;
/**
- * <p>The device supports controls and metadata required for accurate motion tracking for
- * use cases such as augmented reality, electronic image stabilization, and so on.</p>
- * <p>This means this camera device has accurate optical calibration and timestamps relative
- * to the inertial sensors.</p>
- * <p>This capability requires the camera device to support the following:</p>
- * <ul>
- * <li>Capture request templates {@link android.hardware.camera2.CameraDevice#TEMPLATE_MOTION_TRACKING_PREVIEW } and {@link android.hardware.camera2.CameraDevice#TEMPLATE_MOTION_TRACKING_BEST } are defined.</li>
- * <li>The stream configurations listed in {@link android.hardware.camera2.CameraDevice#createCaptureSession } for MOTION_TRACKING are
- * supported, either at 30 or 60fps maximum frame rate.</li>
- * <li>The following camera characteristics and capture result metadata are provided:<ul>
- * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
- * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
- * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
- * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
- * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} with value GYROSCOPE</li>
- * </ul>
- * </li>
- * <li>The {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE android.sensor.info.timestampSource} field has value <code>REALTIME</code>. When compared to
- * timestamps from the device's gyroscopes, the clock difference for events occuring at
- * the same actual time instant will be less than 1 ms.</li>
- * <li>The value of the {@link CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW android.sensor.rollingShutterSkew} field is accurate to within 1 ms.</li>
- * <li>The value of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} is guaranteed to be available in the
- * capture result.</li>
- * <li>The {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} control supports MOTION_TRACKING to limit maximum
- * exposure to 20 milliseconds.</li>
- * <li>The stream configurations required for MOTION_TRACKING (listed at {@link android.hardware.camera2.CameraDevice#createCaptureSession }) can operate at least at
- * 30fps; optionally, they can operate at 60fps, and '[60, 60]' is listed in
- * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}.</li>
- * </ul>
+ * <p>The camera device supports the MOTION_TRACKING value for
+ * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent}, which limits maximum exposure time to 20 ms.</p>
+ * <p>This limits the motion blur of capture images, resulting in better image tracking
+ * results for use cases such as image stabilization or augmented reality.</p>
*
- * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
- * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
- * @see CameraCharacteristics#LENS_POSE_REFERENCE
- * @see CameraCharacteristics#LENS_POSE_ROTATION
- * @see CameraCharacteristics#LENS_POSE_TRANSLATION
- * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
- * @see CaptureRequest#SENSOR_EXPOSURE_TIME
- * @see CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
- * @see CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10;
@@ -864,19 +829,24 @@ public abstract class CameraMetadata<TKey> {
* <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
* </ul>
* </li>
+ * <li>The SENSOR_INFO_TIMESTAMP_SOURCE of the logical device and physical devices must be
+ * the same.</li>
* <li>The logical camera device must be LIMITED or higher device.</li>
* </ul>
* <p>Both the logical camera device and its underlying physical devices support the
* mandatory stream combinations required for their device levels.</p>
* <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
* <ul>
- * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
+ * <li>For each guaranteed stream combination, the logical camera supports replacing one
+ * logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
* or raw stream with two physical streams of the same size and format, each from a
* separate physical camera, given that the size and format are supported by both
* physical cameras.</li>
- * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't
- * advertise RAW capability, but the underlying physical cameras do. This is usually
- * the case when the physical cameras have different sensor sizes.</li>
+ * <li>If the logical camera doesn't advertise RAW capability, but the underlying physical
+ * cameras do, the logical camera will support guaranteed stream combinations for RAW
+ * capability, except that the RAW streams will be physical streams, each from a separate
+ * physical camera. This is usually the case when the physical cameras have different
+ * sensor sizes.</li>
* </ul>
* <p>Using physical streams in place of a logical stream of the same size and format will
* not slow down the frame rate of the capture, as long as the minimum frame duration
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 481b7649610a..3ed533a4efcb 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2757,11 +2757,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
/**
- * <p>Whether the camera device outputs the OIS data in output
+ * <p>A control for selecting whether OIS position information is included in output
* result metadata.</p>
* <p>When set to ON,
* {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
- * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+ * and android.statistics.oisShiftPixelY provide OIS data in the output result metadata.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index d730fa8a31cf..c332d3028aed 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -3909,11 +3909,11 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
/**
- * <p>Whether the camera device outputs the OIS data in output
+ * <p>A control for selecting whether OIS position information is included in output
* result metadata.</p>
* <p>When set to ON,
* {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
- * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+ * and android.statistics.oisShiftPixelY provide OIS data in the output result metadata.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
diff --git a/core/java/android/hardware/camera2/OWNERS b/core/java/android/hardware/camera2/OWNERS
new file mode 100644
index 000000000000..18acfee14555
--- /dev/null
+++ b/core/java/android/hardware/camera2/OWNERS
@@ -0,0 +1,6 @@
+cychen@google.com
+epeev@google.com
+etalvala@google.com
+shuzhenwang@google.com
+yinchiayeh@google.com
+zhijunhe@google.com
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index b205e2c7649d..a040a09cf469 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -82,11 +82,9 @@ import java.util.List;
*
* </ul>
*
- * <p>Please note that surface sharing is currently only enabled for outputs that use the
- * {@link ImageFormat#PRIVATE} format. This includes surface sources like
- * {@link android.view.SurfaceView}, {@link android.media.MediaRecorder},
- * {@link android.graphics.SurfaceTexture} and {@link android.media.ImageReader}, configured using
- * the aforementioned format.</p>
+ * <p> As of {@link android.os.Build.VERSION_CODES#P Android P}, all formats can be used for
+ * sharing, subject to device support. On prior API levels, only {@link ImageFormat#PRIVATE}
+ * format may be used.</p>
*
* @see CameraDevice#createCaptureSessionByOutputConfigurations
*
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
index 00f3c36d0361..1aa2557f92a2 100644
--- a/core/java/android/hardware/display/AmbientBrightnessDayStats.java
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -47,6 +47,11 @@ public final class AmbientBrightnessDayStats implements Parcelable {
private final float[] mStats;
/**
+ * Initialize day stats from the given state. The time spent in each of the bucket is
+ * initialized to 0.
+ *
+ * @param localDate The date for which stats are being tracked
+ * @param bucketBoundaries Bucket boundaries used from creating the buckets from
* @hide
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
@@ -55,6 +60,11 @@ public final class AmbientBrightnessDayStats implements Parcelable {
}
/**
+ * Initialize day stats from the given state
+ *
+ * @param localDate The date for which stats are being tracked
+ * @param bucketBoundaries Bucket boundaries used from creating the buckets from
+ * @param stats Time spent in each of the buckets (in seconds)
* @hide
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
@@ -81,14 +91,26 @@ public final class AmbientBrightnessDayStats implements Parcelable {
mStats = stats;
}
+ /**
+ * @return The {@link LocalDate} for which brightness stats are being tracked.
+ */
public LocalDate getLocalDate() {
return mLocalDate;
}
+ /**
+ * @return Aggregated stats of time spent (in seconds) in various buckets.
+ */
public float[] getStats() {
return mStats;
}
+ /**
+ * Returns the bucket boundaries (in lux) used for creating buckets. For eg., if the bucket
+ * boundaries array is {b1, b2, b3}, the buckets will be [b1, b2), [b2, b3), [b3, inf).
+ *
+ * @return The list of bucket boundaries.
+ */
public float[] getBucketBoundaries() {
return mBucketBoundaries;
}
@@ -169,7 +191,14 @@ public final class AmbientBrightnessDayStats implements Parcelable {
dest.writeFloatArray(mStats);
}
- /** @hide */
+ /**
+ * Updates the stats by incrementing the time spent for the appropriate bucket based on ambient
+ * brightness reading.
+ *
+ * @param ambientBrightness Ambient brightness reading (in lux)
+ * @param durationSec Time spent with the given reading (in seconds)
+ * @hide
+ */
public void log(float ambientBrightness, float durationSec) {
int bucketIndex = getBucketIndex(ambientBrightness);
if (bucketIndex >= 0) {
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 92d6bbb0a20f..bd54522719b2 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -23,10 +23,12 @@ import static android.Manifest.permission.USE_FINGERPRINT;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.app.ActivityManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.os.Binder;
@@ -59,6 +61,7 @@ import javax.crypto.Mac;
*/
@Deprecated
@SystemService(Context.FINGERPRINT_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_FINGERPRINT)
public class FingerprintManager implements BiometricFingerprintConstants {
private static final String TAG = "FingerprintManager";
private static final boolean DEBUG = true;
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index a772cbe43196..e34423c05a87 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -17,11 +17,13 @@
package android.hardware.hdmi;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.os.RemoteException;
@@ -42,6 +44,7 @@ import android.util.Log;
*/
@SystemApi
@SystemService(Context.HDMI_CONTROL_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_HDMI_CEC)
public final class HdmiControlManager {
private static final String TAG = "HdmiControlManager";
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index e1d7edfa7d9c..8fde82ef2012 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -21,10 +21,12 @@ import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
@@ -58,6 +60,7 @@ import java.util.stream.Collectors;
*/
@SystemApi
@SystemService(Context.RADIO_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_BROADCAST_RADIO)
public class RadioManager {
private static final String TAG = "BroadcastRadio.manager";
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 5b15c0d2fd9c..9e5174ad93a8 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -222,7 +222,10 @@ public class UsbDeviceConnection {
* @param endpoint the endpoint for this transaction
* @param buffer buffer for data to send or receive; can be {@code null} to wait for next
* transaction without reading data
- * @param length the length of the data to send or receive
+ * @param length the length of the data to send or receive. Before
+ * {@value Build.VERSION_CODES#P}, a value larger than 16384 bytes
+ * would be truncated down to 16384. In API {@value Build.VERSION_CODES#P}
+ * and after, any value of length is valid.
* @param timeout in milliseconds, 0 is infinite
* @return length of data transferred (or zero) for success,
* or negative value for failure
@@ -239,7 +242,10 @@ public class UsbDeviceConnection {
* @param endpoint the endpoint for this transaction
* @param buffer buffer for data to send or receive
* @param offset the index of the first byte in the buffer to send or receive
- * @param length the length of the data to send or receive
+ * @param length the length of the data to send or receive. Before
+ * {@value Build.VERSION_CODES#P}, a value larger than 16384 bytes
+ * would be truncated down to 16384. In API {@value Build.VERSION_CODES#P}
+ * and after, any value of length is valid.
* @param timeout in milliseconds, 0 is infinite
* @return length of data transferred (or zero) for success,
* or negative value for failure
@@ -247,6 +253,10 @@ public class UsbDeviceConnection {
public int bulkTransfer(UsbEndpoint endpoint,
byte[] buffer, int offset, int length, int timeout) {
checkBounds(buffer, offset, length);
+ if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+ && length > UsbRequest.MAX_USBFS_BUFFER_SIZE) {
+ length = UsbRequest.MAX_USBFS_BUFFER_SIZE;
+ }
return native_bulk_request(endpoint.getAddress(), buffer, offset, length, timeout);
}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 8daecac5a109..74a36df03b00 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -19,6 +19,7 @@ package android.hardware.usb;
import android.Manifest;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -27,6 +28,7 @@ import android.annotation.SystemService;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.hardware.usb.gadget.V1_0.GadgetFunction;
import android.os.Bundle;
@@ -382,6 +384,7 @@ public class UsbManager {
*
* @return HashMap containing all connected USB devices.
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_HOST)
public HashMap<String,UsbDevice> getDeviceList() {
HashMap<String,UsbDevice> result = new HashMap<String,UsbDevice>();
if (mService == null) {
@@ -406,6 +409,7 @@ public class UsbManager {
* @param device the device to open
* @return a {@link UsbDeviceConnection}, or {@code null} if open failed
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_HOST)
public UsbDeviceConnection openDevice(UsbDevice device) {
try {
String deviceName = device.getDeviceName();
@@ -430,6 +434,7 @@ public class UsbManager {
*
* @return list of USB accessories, or null if none are attached.
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
public UsbAccessory[] getAccessoryList() {
if (mService == null) {
return null;
@@ -452,6 +457,7 @@ public class UsbManager {
* @param accessory the USB accessory to open
* @return file descriptor, or null if the accessor could not be opened.
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
try {
return mService.openAccessory(accessory);
@@ -472,6 +478,7 @@ public class UsbManager {
* @param device to check permissions for
* @return true if caller has permission
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_HOST)
public boolean hasPermission(UsbDevice device) {
if (mService == null) {
return false;
@@ -492,6 +499,7 @@ public class UsbManager {
* @param accessory to check permissions for
* @return true if caller has permission
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
public boolean hasPermission(UsbAccessory accessory) {
if (mService == null) {
return false;
@@ -525,6 +533,7 @@ public class UsbManager {
* @param device to request permissions for
* @param pi PendingIntent for returning result
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_HOST)
public void requestPermission(UsbDevice device, PendingIntent pi) {
try {
mService.requestDevicePermission(device, mContext.getPackageName(), pi);
@@ -551,6 +560,7 @@ public class UsbManager {
* @param accessory to request permissions for
* @param pi PendingIntent for returning result
*/
+ @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
public void requestPermission(UsbAccessory accessory, PendingIntent pi) {
try {
mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi);
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index 2e8f8e12b508..f59c87eecfcb 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -17,6 +17,7 @@
package android.hardware.usb;
import android.annotation.Nullable;
+import android.os.Build;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -43,7 +44,7 @@ public class UsbRequest {
private static final String TAG = "UsbRequest";
// From drivers/usb/core/devio.c
- private static final int MAX_USBFS_BUFFER_SIZE = 16384;
+ static final int MAX_USBFS_BUFFER_SIZE = 16384;
// used by the JNI code
private long mNativeContext;
@@ -175,7 +176,9 @@ public class UsbRequest {
* capacity will be ignored. Once the request
* {@link UsbDeviceConnection#requestWait() is processed} the position will be set
* to the number of bytes read/written.
- * @param length number of bytes to read or write.
+ * @param length number of bytes to read or write. Before {@value Build.VERSION_CODES#P}, a
+ * value larger than 16384 bytes would be truncated down to 16384. In API
+ * {@value Build.VERSION_CODES#P} and after, any value of length is valid.
*
* @return true if the queueing operation succeeded
*
@@ -186,6 +189,11 @@ public class UsbRequest {
boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
boolean result;
+ if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+ && length > MAX_USBFS_BUFFER_SIZE) {
+ length = MAX_USBFS_BUFFER_SIZE;
+ }
+
synchronized (mLock) {
// save our buffer for when the request has completed
mBuffer = buffer;
@@ -222,7 +230,10 @@ public class UsbRequest {
* of the buffer is undefined until the request is returned by
* {@link UsbDeviceConnection#requestWait}. If the request failed the buffer
* will be unchanged; if the request succeeded the position of the buffer is
- * incremented by the number of bytes sent/received.
+ * incremented by the number of bytes sent/received. Before
+ * {@value Build.VERSION_CODES#P}, a buffer of length larger than 16384 bytes
+ * would throw IllegalArgumentException. In API {@value Build.VERSION_CODES#P}
+ * and after, any size buffer is valid.
*
* @return true if the queueing operation succeeded
*/
@@ -244,9 +255,12 @@ public class UsbRequest {
mIsUsingNewQueue = true;
wasQueued = native_queue(null, 0, 0);
} else {
- // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
- Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
- "number of remaining bytes");
+ if (mConnection.getContext().getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.P) {
+ // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
+ Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
+ "number of remaining bytes");
+ }
// Can not receive into read-only buffers.
Preconditions.checkArgument(!(buffer.isReadOnly() && !isSend), "buffer can not be "
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index a817f33ce7ba..017674f5ea8a 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1818,9 +1818,9 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
- * Called when the input method window has been shown to the user, after
- * previously not being visible. This is done after all of the UI setup
- * for the window has occurred (creating its views etc).
+ * Called immediately before the input method window is shown to the user.
+ * You could override this to prepare for the window to be shown
+ * (update view structure etc).
*/
public void onWindowShown() {
// Intentionally empty
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 91ba57f334f0..fd030e24af5f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -112,8 +112,14 @@ public class ConnectivityManager {
* <p/>
* For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY
* is set to {@code true} if there are no connected networks at all.
+ *
+ * @deprecated apps should use the more versatile {@link #requestNetwork},
+ * {@link #registerNetworkCallback} or {@link #registerDefaultNetworkCallback}
+ * functions instead for faster and more detailed updates about the network
+ * changes they care about.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
/**
@@ -2663,7 +2669,7 @@ public class ConnectivityManager {
* A {@code NetworkCallback} is registered by calling
* {@link #requestNetwork(NetworkRequest, NetworkCallback)},
* {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)},
- * or {@link #registerDefaultNetworkCallback(NetworkCallback). A {@code NetworkCallback} is
+ * or {@link #registerDefaultNetworkCallback(NetworkCallback)}. A {@code NetworkCallback} is
* unregistered by calling {@link #unregisterNetworkCallback(NetworkCallback)}.
* A {@code NetworkCallback} should be registered at most once at any time.
* A {@code NetworkCallback} that has been unregistered can be registered again.
@@ -2692,6 +2698,32 @@ public class ConnectivityManager {
* satisfying the request changes.
*
* @param network The {@link Network} of the satisfying network.
+ * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
+ * @param linkProperties The {@link LinkProperties} of the satisfying network.
+ * @hide
+ */
+ public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties) {
+ // Internally only this method is called when a new network is available, and
+ // it calls the callback in the same way and order that older versions used
+ // to call so as not to change the behavior.
+ onAvailable(network);
+ if (!networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
+ onNetworkSuspended(network);
+ }
+ onCapabilitiesChanged(network, networkCapabilities);
+ onLinkPropertiesChanged(network, linkProperties);
+ }
+
+ /**
+ * Called when the framework connects and has declared a new network ready for use.
+ * This callback may be called more than once if the {@link Network} that is
+ * satisfying the request changes. This will always immediately be followed by a
+ * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
+ * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+ *
+ * @param network The {@link Network} of the satisfying network.
*/
public void onAvailable(Network network) {}
@@ -2734,7 +2766,8 @@ public class ConnectivityManager {
* changes capabilities but still satisfies the stated need.
*
* @param network The {@link Network} whose capabilities have changed.
- * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this network.
+ * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
+ * network.
*/
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {}
@@ -2750,7 +2783,7 @@ public class ConnectivityManager {
/**
* Called when the network the framework connected to for this request
- * goes into {@link NetworkInfo.DetailedState.SUSPENDED}.
+ * goes into {@link NetworkInfo.State#SUSPENDED}.
* This generally means that while the TCP connections are still live,
* temporarily network data fails to transfer. Specifically this is used
* on cellular networks to mask temporary outages when driving through
@@ -2761,9 +2794,8 @@ public class ConnectivityManager {
/**
* Called when the network the framework connected to for this request
- * returns from a {@link NetworkInfo.DetailedState.SUSPENDED} state.
- * This should always be preceeded by a matching {@code onNetworkSuspended}
- * call.
+ * returns from a {@link NetworkInfo.State#SUSPENDED} state. This should always be
+ * preceded by a matching {@link NetworkCallback#onNetworkSuspended} call.
* @hide
*/
public void onNetworkResumed(Network network) {}
@@ -2872,7 +2904,9 @@ public class ConnectivityManager {
break;
}
case CALLBACK_AVAILABLE: {
- callback.onAvailable(network);
+ NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
+ LinkProperties lp = getObject(message, LinkProperties.class);
+ callback.onAvailable(network, cap, lp);
break;
}
case CALLBACK_LOSING: {
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 6a262e2c87ca..8599f47c6245 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -218,6 +218,25 @@ public final class IpSecConfig implements Parcelable {
@VisibleForTesting
public IpSecConfig() {}
+ /** Copy constructor */
+ @VisibleForTesting
+ public IpSecConfig(IpSecConfig c) {
+ mMode = c.mMode;
+ mSourceAddress = c.mSourceAddress;
+ mDestinationAddress = c.mDestinationAddress;
+ mNetwork = c.mNetwork;
+ mSpiResourceId = c.mSpiResourceId;
+ mEncryption = c.mEncryption;
+ mAuthentication = c.mAuthentication;
+ mAuthenticatedEncryption = c.mAuthenticatedEncryption;
+ mEncapType = c.mEncapType;
+ mEncapSocketResourceId = c.mEncapSocketResourceId;
+ mEncapRemotePort = c.mEncapRemotePort;
+ mNattKeepaliveInterval = c.mNattKeepaliveInterval;
+ mMarkValue = c.mMarkValue;
+ mMarkMask = c.mMarkMask;
+ }
+
private IpSecConfig(Parcel in) {
mMode = in.readInt();
mSourceAddress = in.readString();
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 24a078fccc1d..b60984771a2d 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -19,6 +19,7 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -761,6 +762,7 @@ public final class IpSecManager {
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
@NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
throws ResourceUnavailableException, IOException {
@@ -780,6 +782,7 @@ public final class IpSecManager {
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
IpSecTransform transform) throws IOException {
try {
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 0829b4a3e9fe..60e96f943401 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
@@ -83,9 +84,11 @@ public final class IpSecTransform implements AutoCloseable {
@Retention(RetentionPolicy.SOURCE)
public @interface EncapType {}
- private IpSecTransform(Context context, IpSecConfig config) {
+ /** @hide */
+ @VisibleForTesting
+ public IpSecTransform(Context context, IpSecConfig config) {
mContext = context;
- mConfig = config;
+ mConfig = new IpSecConfig(config);
mResourceId = INVALID_RESOURCE_ID;
}
@@ -142,6 +145,18 @@ public final class IpSecTransform implements AutoCloseable {
}
/**
+ * Equals method used for testing
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static boolean equals(IpSecTransform lhs, IpSecTransform rhs) {
+ if (lhs == null || rhs == null) return (lhs == rhs);
+ return IpSecConfig.equals(lhs.getConfig(), rhs.getConfig())
+ && lhs.mResourceId == rhs.mResourceId;
+ }
+
+ /**
* Deactivate this {@code IpSecTransform} and free allocated resources.
*
* <p>Deactivating a transform while it is still applied to a socket will result in errors on
@@ -266,6 +281,10 @@ public final class IpSecTransform implements AutoCloseable {
* @hide
*/
@SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
+ })
public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback,
int intervalSeconds, @NonNull Handler handler) throws IOException {
checkNotNull(userCallback);
@@ -305,6 +324,10 @@ public final class IpSecTransform implements AutoCloseable {
* @hide
*/
@SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
+ })
public void stopNattKeepalive() {
synchronized (mKeepaliveCallback) {
if (mKeepalive == null) {
@@ -449,6 +472,7 @@ public final class IpSecTransform implements AutoCloseable {
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public IpSecTransform buildTunnelModeTransform(
@NonNull InetAddress sourceAddress,
@NonNull IpSecManager.SecurityParameterIndex spi)
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 287bdc88dd3e..74d64704c8d2 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -26,6 +26,7 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Random;
@@ -329,16 +330,34 @@ public final class MacAddress implements Parcelable {
/**
* Returns a generated MAC address whose 24 least significant bits constituting the
- * NIC part of the address are randomly selected.
+ * NIC part of the address are randomly selected and has Google OUI base.
*
* The locally assigned bit is always set to 1. The multicast bit is always set to 0.
*
- * @return a random locally assigned MacAddress.
+ * @return a random locally assigned, unicast MacAddress with Google OUI.
+ *
+ * @hide
+ */
+ public static @NonNull MacAddress createRandomUnicastAddressWithGoogleBase() {
+ return createRandomUnicastAddress(BASE_GOOGLE_MAC, new SecureRandom());
+ }
+
+ /**
+ * Returns a generated MAC address whose 46 bits, excluding the locally assigned bit and the
+ * unicast bit, are randomly selected.
+ *
+ * The locally assigned bit is always set to 1. The multicast bit is always set to 0.
+ *
+ * @return a random locally assigned, unicast MacAddress.
*
* @hide
*/
public static @NonNull MacAddress createRandomUnicastAddress() {
- return createRandomUnicastAddress(BASE_GOOGLE_MAC, new Random());
+ SecureRandom r = new SecureRandom();
+ long addr = r.nextLong() & VALID_LONG_MASK;
+ addr |= LOCALLY_ASSIGNED_MASK;
+ addr &= ~MULTICAST_MASK;
+ return new MacAddress(addr);
}
/**
@@ -355,8 +374,8 @@ public final class MacAddress implements Parcelable {
*/
public static @NonNull MacAddress createRandomUnicastAddress(MacAddress base, Random r) {
long addr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong());
- addr = addr | LOCALLY_ASSIGNED_MASK;
- addr = addr & ~MULTICAST_MASK;
+ addr |= LOCALLY_ASSIGNED_MASK;
+ addr &= ~MULTICAST_MASK;
return new MacAddress(addr);
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8e05cfa96625..bae373d7564b 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -116,6 +116,7 @@ public final class NetworkCapabilities implements Parcelable {
NET_CAPABILITY_NOT_ROAMING,
NET_CAPABILITY_FOREGROUND,
NET_CAPABILITY_NOT_CONGESTED,
+ NET_CAPABILITY_NOT_SUSPENDED,
})
public @interface NetCapability { }
@@ -239,7 +240,6 @@ public final class NetworkCapabilities implements Parcelable {
/**
* Indicates that this network is available for use by apps, and not a network that is being
* kept up in the background to facilitate fast network switching.
- * @hide
*/
public static final int NET_CAPABILITY_FOREGROUND = 19;
@@ -252,8 +252,20 @@ public final class NetworkCapabilities implements Parcelable {
*/
public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
+ /**
+ * Indicates that this network is not currently suspended.
+ * <p>
+ * When a network is suspended, the network's IP addresses and any connections
+ * established on the network remain valid, but the network is temporarily unable
+ * to transfer data. This can happen, for example, if a cellular network experiences
+ * a temporary loss of signal, such as when driving through a tunnel, etc.
+ * A network with this capability is not suspended, so is expected to be able to
+ * transfer data.
+ */
+ public static final int NET_CAPABILITY_NOT_SUSPENDED = 21;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_CONGESTED;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_SUSPENDED;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -262,12 +274,13 @@ public final class NetworkCapabilities implements Parcelable {
private static final long MUTABLE_CAPABILITIES =
// TRUSTED can change when user explicitly connects to an untrusted network in Settings.
// http://b/18206275
- (1 << NET_CAPABILITY_TRUSTED) |
- (1 << NET_CAPABILITY_VALIDATED) |
- (1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
- (1 << NET_CAPABILITY_NOT_ROAMING) |
- (1 << NET_CAPABILITY_FOREGROUND) |
- (1 << NET_CAPABILITY_NOT_CONGESTED);
+ (1 << NET_CAPABILITY_TRUSTED)
+ | (1 << NET_CAPABILITY_VALIDATED)
+ | (1 << NET_CAPABILITY_CAPTIVE_PORTAL)
+ | (1 << NET_CAPABILITY_NOT_ROAMING)
+ | (1 << NET_CAPABILITY_FOREGROUND)
+ | (1 << NET_CAPABILITY_NOT_CONGESTED)
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -1299,6 +1312,7 @@ public final class NetworkCapabilities implements Parcelable {
case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING";
case NET_CAPABILITY_FOREGROUND: return "FOREGROUND";
case NET_CAPABILITY_NOT_CONGESTED: return "NOT_CONGESTED";
+ case NET_CAPABILITY_NOT_SUSPENDED: return "NOT_SUSPENDED";
default: return Integer.toString(capability);
}
}
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index 3a40cf4bf8a3..3cd37bf4fbdd 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -1,3 +1,5 @@
+set noparent
+
ek@google.com
jsharkey@android.com
jchalard@google.com
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 5ca3a4106a2d..437153b5e010 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.Nullable;
import android.content.Intent;
import android.os.Environment;
import android.os.Parcel;
@@ -23,6 +24,8 @@ import android.os.Parcelable;
import android.os.StrictMode;
import android.util.Log;
+import libcore.net.UriCodec;
+
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@@ -38,8 +41,6 @@ import java.util.Objects;
import java.util.RandomAccess;
import java.util.Set;
-import libcore.net.UriCodec;
-
/**
* Immutable URI reference. A URI reference includes a URI and a fragment, the
* component of the URI following a '#'. Builds and parses URI references
@@ -174,6 +175,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the scheme or null if this is a relative URI
*/
+ @Nullable
public abstract String getScheme();
/**
@@ -208,6 +210,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the authority for this URI or null if not present
*/
+ @Nullable
public abstract String getAuthority();
/**
@@ -219,6 +222,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the authority for this URI or null if not present
*/
+ @Nullable
public abstract String getEncodedAuthority();
/**
@@ -228,6 +232,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the user info for this URI or null if not present
*/
+ @Nullable
public abstract String getUserInfo();
/**
@@ -237,6 +242,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the user info for this URI or null if not present
*/
+ @Nullable
public abstract String getEncodedUserInfo();
/**
@@ -246,6 +252,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the host for this URI or null if not present
*/
+ @Nullable
public abstract String getHost();
/**
@@ -262,6 +269,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
* @return the decoded path, or null if this is not a hierarchical URI
* (like "mailto:nobody@google.com") or the URI is invalid
*/
+ @Nullable
public abstract String getPath();
/**
@@ -270,6 +278,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
* @return the encoded path, or null if this is not a hierarchical URI
* (like "mailto:nobody@google.com") or the URI is invalid
*/
+ @Nullable
public abstract String getEncodedPath();
/**
@@ -280,6 +289,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the decoded query or null if there isn't one
*/
+ @Nullable
public abstract String getQuery();
/**
@@ -290,6 +300,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the encoded query or null if there isn't one
*/
+ @Nullable
public abstract String getEncodedQuery();
/**
@@ -297,6 +308,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the decoded fragment or null if there isn't one
*/
+ @Nullable
public abstract String getFragment();
/**
@@ -304,6 +316,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the encoded fragment or null if there isn't one
*/
+ @Nullable
public abstract String getEncodedFragment();
/**
@@ -318,6 +331,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
*
* @return the decoded last segment or null if the path is empty
*/
+ @Nullable
public abstract String getLastPathSegment();
/**
@@ -1674,6 +1688,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
* @throws NullPointerException if key is null
* @return the decoded value or null if no parameter is found
*/
+ @Nullable
public String getQueryParameter(String key) {
if (isOpaque()) {
throw new UnsupportedOperationException(NOT_HIERARCHICAL);
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index a734719afa5d..8b4f02efd153 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -323,4 +323,16 @@ public class BatteryManager {
public long getLongProperty(int id) {
return queryProperty(id);
}
+
+ /**
+ * Return true if the plugType given is wired
+ * @param plugType {@link #BATTERY_PLUGGED_AC}, {@link #BATTERY_PLUGGED_USB},
+ * or {@link #BATTERY_PLUGGED_WIRELESS}
+ *
+ * @return true if plugType is wired
+ * @hide
+ */
+ public static boolean isPlugWired(int plugType) {
+ return plugType == BATTERY_PLUGGED_USB || plugType == BATTERY_PLUGGED_AC;
+ }
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fd0e5ae51892..7cd58e8b7c36 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1537,6 +1537,7 @@ public abstract class BatteryStats implements Parcelable {
public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<22;
public static final int STATE2_CAMERA_FLAG = 1<<21;
public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
+ public static final int STATE2_CELLULAR_HIGH_TX_POWER_FLAG = 1 << 19;
public static final int MOST_INTERESTING_STATES2 =
STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
@@ -2239,12 +2240,16 @@ public abstract class BatteryStats implements Parcelable {
public static final int DATA_CONNECTION_LTE = 13;
public static final int DATA_CONNECTION_EHRPD = 14;
public static final int DATA_CONNECTION_HSPAP = 15;
- public static final int DATA_CONNECTION_OTHER = 16;
+ public static final int DATA_CONNECTION_GSM = 16;
+ public static final int DATA_CONNECTION_TD_SCDMA = 17;
+ public static final int DATA_CONNECTION_IWLAN = 18;
+ public static final int DATA_CONNECTION_LTE_CA = 19;
+ public static final int DATA_CONNECTION_OTHER = 20;
static final String[] DATA_CONNECTION_NAMES = {
"none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
"1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
- "ehrpd", "hspap", "other"
+ "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "other"
};
public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
@@ -2353,9 +2358,11 @@ public abstract class BatteryStats implements Parcelable {
WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"),
new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"),
+ new BitDescription(HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG,
+ "cellular_high_tx_power", "Chtp"),
new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK,
HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss",
- new String[] { "poor", "good"}, new String[] { "poor", "good"}),
+ new String[] { "poor", "good"}, new String[] { "poor", "good"})
};
public static final String[] HISTORY_EVENT_NAMES = new String[] {
diff --git a/core/java/android/os/BestClock.java b/core/java/android/os/BestClock.java
new file mode 100644
index 000000000000..aa066b633e6b
--- /dev/null
+++ b/core/java/android/os/BestClock.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+import java.time.Clock;
+import java.time.DateTimeException;
+import java.time.ZoneId;
+import java.util.Arrays;
+
+/**
+ * Single {@link Clock} that will return the best available time from a set of
+ * prioritized {@link Clock} instances.
+ * <p>
+ * For example, when {@link SystemClock#currentNetworkTimeClock()} isn't able to
+ * provide the time, this class could use {@link Clock#systemUTC()} instead.
+ *
+ * @hide
+ */
+public class BestClock extends SimpleClock {
+ private static final String TAG = "BestClock";
+
+ private final Clock[] clocks;
+
+ public BestClock(ZoneId zone, Clock... clocks) {
+ super(zone);
+ this.clocks = clocks;
+ }
+
+ @Override
+ public long millis() {
+ for (Clock clock : clocks) {
+ try {
+ return clock.millis();
+ } catch (DateTimeException e) {
+ // Ignore and attempt the next clock
+ Log.w(TAG, e.toString());
+ }
+ }
+ throw new DateTimeException(
+ "No clocks in " + Arrays.toString(clocks) + " were able to provide time");
+ }
+}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 682fdb7160f4..ff7c0c6681c6 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -1028,22 +1028,33 @@ final class BinderProxy implements IBinder {
* in use, then we return the same bp.
*
* @param nativeData C++ pointer to (possibly still empty) BinderProxyNativeData.
- * Takes ownership of nativeData iff <result>.mNativeData == nativeData. Caller will usually
- * delete nativeData if that's not the case.
+ * Takes ownership of nativeData iff <result>.mNativeData == nativeData, or if
+ * we exit via an exception. If neither applies, it's the callers responsibility to
+ * recycle nativeData.
* @param iBinder C++ pointer to IBinder. Does not take ownership of referenced object.
*/
private static BinderProxy getInstance(long nativeData, long iBinder) {
- BinderProxy result = sProxyMap.get(iBinder);
- if (result == null) {
+ BinderProxy result;
+ try {
+ result = sProxyMap.get(iBinder);
+ if (result != null) {
+ return result;
+ }
result = new BinderProxy(nativeData);
- sProxyMap.set(iBinder, result);
+ } catch (Throwable e) {
+ // We're throwing an exception (probably OOME); don't drop nativeData.
+ NativeAllocationRegistry.applyFreeFunction(NoImagePreloadHolder.sNativeFinalizer,
+ nativeData);
+ throw e;
}
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(result, nativeData);
+ // The registry now owns nativeData, even if registration threw an exception.
+ sProxyMap.set(iBinder, result);
return result;
}
private BinderProxy(long nativeData) {
mNativeData = nativeData;
- NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeData);
}
/**
@@ -1057,8 +1068,9 @@ final class BinderProxy implements IBinder {
// Use a Holder to allow static initialization of BinderProxy in the boot image, and
// to avoid some initialization ordering issues.
private static class NoImagePreloadHolder {
+ public static final long sNativeFinalizer = getNativeFinalizer();
public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- BinderProxy.class.getClassLoader(), getNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
+ BinderProxy.class.getClassLoader(), sNativeFinalizer, NATIVE_ALLOCATION_SIZE);
}
public native boolean pingBinder();
diff --git a/core/java/android/os/ChildZygoteProcess.java b/core/java/android/os/ChildZygoteProcess.java
new file mode 100644
index 000000000000..337a3e279a1a
--- /dev/null
+++ b/core/java/android/os/ChildZygoteProcess.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.net.LocalSocketAddress;
+
+/**
+ * Represents a connection to a child-zygote process. A child-zygote is spawend from another
+ * zygote process using {@link startChildZygote()}.
+ *
+ * {@hide}
+ */
+public class ChildZygoteProcess extends ZygoteProcess {
+ /**
+ * The PID of the child zygote process.
+ */
+ private final int mPid;
+
+ ChildZygoteProcess(LocalSocketAddress socketAddress, int pid) {
+ super(socketAddress, null);
+ mPid = pid;
+ }
+
+ /**
+ * Returns the PID of the child-zygote process.
+ */
+ public int getPid() {
+ return mPid;
+ }
+}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index cdee1101c27b..228fe7a3dae5 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -29,7 +29,13 @@ public abstract class HwBinder implements IHwBinder {
private static final NativeAllocationRegistry sNativeRegistry;
- /** @hide */
+ /**
+ * Create and initialize a HwBinder object and the native objects
+ * used to allow this to participate in hwbinder transactions.
+ *
+ * @hide
+ */
+ @SystemApi
public HwBinder() {
native_setup();
@@ -44,12 +50,28 @@ public abstract class HwBinder implements IHwBinder {
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
- /** @hide */
+ /**
+ * Process a hwbinder transaction.
+ *
+ * @param code interface specific code for interface.
+ * @param request parceled transaction
+ * @param reply object to parcel reply into
+ * @param flags transaction flags to be chosen by wire protocol
+ *
+ * @hide
+ */
+ @SystemApi
public abstract void onTransact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
- /** @hide */
+ /**
+ * Registers this service with the hwservicemanager.
+ *
+ * @param serviceName instance name of the service
+ * @hide
+ */
+ @SystemApi
public native final void registerService(String serviceName)
throws RemoteException;
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index a565dee5ddd0..fbdf27e38d67 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -21,12 +21,6 @@ import android.annotation.SystemApi;
/** @hide */
@SystemApi
public interface IHwBinder {
- // These MUST match their corresponding libhwbinder/IBinder.h definition !!!
- /** @hide */
- public static final int FIRST_CALL_TRANSACTION = 1;
- /** @hide */
- public static final int FLAG_ONEWAY = 1;
-
/**
* Process a hwbinder transaction.
*
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 8a27700edc15..eae52171ee48 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -55,9 +55,8 @@ interface IStatsCompanionService {
/** Pull the specified data. Results will be sent to statsd when complete. */
StatsLogEventWrapper[] pullData(int pullCode);
- /** Send a broadcast to the specified pkg and class that it should getData now. */
- // TODO: Rename this and use a pending intent instead.
- oneway void sendBroadcast(String pkg, String cls);
+ /** Send a broadcast to the specified PendingIntent's as IBinder that it should getData now. */
+ oneway void sendDataBroadcast(in IBinder intentSender);
/**
* Requests StatsCompanionService to send a broadcast using the given intentSender
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 679b49dfb974..682a24f17648 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -81,12 +81,26 @@ interface IStatsManager {
/**
* Sets a configuration with the specified config key and subscribes to updates for this
* configuration key. Broadcasts will be sent if this configuration needs to be collected.
- * The configuration must be a wire-encoded StatsdConfig. The caller specifies the name of the
- * package and class that should receive these broadcasts.
+ * The configuration must be a wire-encoded StatsDConfig. The receiver for this data is
+ * registered in a separate function.
*
* Returns if this configuration was correctly registered.
*/
- boolean addConfiguration(in long configKey, in byte[] config, in String pkg, in String cls);
+ boolean addConfiguration(in long configKey, in byte[] config);
+
+ /**
+ * Registers the given pending intent for this config key. This intent is invoked when the
+ * memory consumed by the metrics for this configuration approach the pre-defined limits. There
+ * can be at most one listener per config key.
+ *
+ * Returns if this listener was correctly registered.
+ */
+ boolean setDataFetchOperation(long configKey, in IBinder intentSender);
+
+ /**
+ * Removes the data fetch operation for the specified configuration.
+ */
+ boolean removeDataFetchOperation(long configKey);
/**
* Removes the configuration with the matching config key. No-op if this config key does not
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 65e9473380ce..5e23932c48cc 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -71,6 +71,7 @@ interface IUserManager {
Bundle getUserRestrictions(int userHandle);
boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
boolean hasUserRestriction(in String restrictionKey, int userHandle);
+ boolean hasUserRestrictionOnAnyUser(in String restrictionKey);
void setUserRestriction(String key, boolean value, int userHandle);
void setApplicationRestrictions(in String packageName, in Bundle restrictions,
int userHandle);
diff --git a/core/java/android/os/SimpleClock.java b/core/java/android/os/SimpleClock.java
new file mode 100644
index 000000000000..efc271f5408f
--- /dev/null
+++ b/core/java/android/os/SimpleClock.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+
+/** {@hide} */
+public abstract class SimpleClock extends Clock {
+ private final ZoneId zone;
+
+ public SimpleClock(ZoneId zone) {
+ this.zone = zone;
+ }
+
+ @Override
+ public ZoneId getZone() {
+ return zone;
+ }
+
+ @Override
+ public Clock withZone(ZoneId zone) {
+ return new SimpleClock(zone) {
+ @Override
+ public long millis() {
+ return SimpleClock.this.millis();
+ }
+ };
+ }
+
+ @Override
+ public abstract long millis();
+
+ @Override
+ public Instant instant() {
+ return Instant.ofEpochMilli(millis());
+ }
+}
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index c52c22d60ade..0f70427e6dc0 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -24,8 +24,7 @@ import android.util.Slog;
import dalvik.annotation.optimization.CriticalNative;
import java.time.Clock;
-import java.time.Instant;
-import java.time.ZoneId;
+import java.time.DateTimeException;
import java.time.ZoneOffset;
/**
@@ -148,8 +147,8 @@ public final class SystemClock {
* @return if the clock was successfully set to the specified time.
*/
public static boolean setCurrentTimeMillis(long millis) {
- IBinder b = ServiceManager.getService(Context.ALARM_SERVICE);
- IAlarmManager mgr = IAlarmManager.Stub.asInterface(b);
+ final IAlarmManager mgr = IAlarmManager.Stub
+ .asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
if (mgr == null) {
return false;
}
@@ -174,27 +173,25 @@ public final class SystemClock {
native public static long uptimeMillis();
/**
+ * @removed
+ */
+ @Deprecated
+ public static @NonNull Clock uptimeMillisClock() {
+ return uptimeClock();
+ }
+
+ /**
* Return {@link Clock} that starts at system boot, not counting time spent
* in deep sleep.
+ *
+ * @removed
*/
- public static @NonNull Clock uptimeMillisClock() {
- return new Clock() {
- @Override
- public ZoneId getZone() {
- return ZoneOffset.UTC;
- }
- @Override
- public Clock withZone(ZoneId zone) {
- throw new UnsupportedOperationException();
- }
+ public static @NonNull Clock uptimeClock() {
+ return new SimpleClock(ZoneOffset.UTC) {
@Override
public long millis() {
return SystemClock.uptimeMillis();
}
- @Override
- public Instant instant() {
- return Instant.ofEpochMilli(millis());
- }
};
}
@@ -209,25 +206,15 @@ public final class SystemClock {
/**
* Return {@link Clock} that starts at system boot, including time spent in
* sleep.
+ *
+ * @removed
*/
public static @NonNull Clock elapsedRealtimeClock() {
- return new Clock() {
- @Override
- public ZoneId getZone() {
- return ZoneOffset.UTC;
- }
- @Override
- public Clock withZone(ZoneId zone) {
- throw new UnsupportedOperationException();
- }
+ return new SimpleClock(ZoneOffset.UTC) {
@Override
public long millis() {
return SystemClock.elapsedRealtime();
}
- @Override
- public Instant instant() {
- return Instant.ofEpochMilli(millis());
- }
};
}
@@ -266,4 +253,62 @@ public final class SystemClock {
*/
@CriticalNative
public static native long currentTimeMicro();
+
+ /**
+ * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
+ * using a remote network source outside the device.
+ * <p>
+ * While the time returned by {@link System#currentTimeMillis()} can be
+ * adjusted by the user, the time returned by this method cannot be adjusted
+ * by the user. Note that synchronization may occur using an insecure
+ * network protocol, so the returned time should not be used for security
+ * purposes.
+ * <p>
+ * This performs no blocking network operations and returns values based on
+ * a recent successful synchronization event; it will either return a valid
+ * time or throw.
+ *
+ * @throws DateTimeException when no accurate network time can be provided.
+ */
+ public static long currentNetworkTimeMillis() {
+ final IAlarmManager mgr = IAlarmManager.Stub
+ .asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
+ if (mgr != null) {
+ try {
+ return mgr.currentNetworkTimeMillis();
+ } catch (ParcelableException e) {
+ e.maybeRethrow(DateTimeException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ throw new RuntimeException(new DeadSystemException());
+ }
+ }
+
+ /**
+ * Returns a {@link Clock} that starts at January 1, 1970 00:00:00.0 UTC,
+ * synchronized using a remote network source outside the device.
+ * <p>
+ * While the time returned by {@link System#currentTimeMillis()} can be
+ * adjusted by the user, the time returned by this method cannot be adjusted
+ * by the user. Note that synchronization may occur using an insecure
+ * network protocol, so the returned time should not be used for security
+ * purposes.
+ * <p>
+ * This performs no blocking network operations and returns values based on
+ * a recent successful synchronization event; it will either return a valid
+ * time or throw.
+ *
+ * @throws DateTimeException when no accurate network time can be provided.
+ */
+ public static @NonNull Clock currentNetworkTimeClock() {
+ return new SimpleClock(ZoneOffset.UTC) {
+ @Override
+ public long millis() {
+ return SystemClock.currentNetworkTimeMillis();
+ }
+ };
+ }
}
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index c6149bed9d6d..24c9c9177360 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -271,4 +271,22 @@ public class UpdateEngine {
}
}
}
+
+ /**
+ * Verifies that a payload associated with the given payload metadata
+ * {@code payloadMetadataFilename} can be safely applied to ths device.
+ * Returns {@code true} if the update can successfully be applied and
+ * returns {@code false} otherwise.
+ *
+ * @param payloadMetadataFilename the location of the metadata without the
+ * {@code file://} prefix.
+ */
+ @SystemApi
+ public boolean verifyPayloadMetadata(String payloadMetadataFilename) {
+ try {
+ return mUpdateEngine.verifyPayloadApplicable(payloadMetadataFilename);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2093cec769da..185620066454 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -943,6 +943,20 @@ public class UserManager {
* @see #getUserRestrictions()
*/
public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile";
+
+ /**
+ * Specifies whether the user is allowed to print.
+ *
+ * This restriction can be set by device or profile owner.
+ *
+ * The default value is {@code false}.
+ *
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_PRINTING = "no_printing";
+
/**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
@@ -1664,6 +1678,18 @@ public class UserManager {
}
/**
+ * @hide
+ * Returns whether any user on the device has the given user restriction set.
+ */
+ public boolean hasUserRestrictionOnAnyUser(String restrictionKey) {
+ try {
+ return mService.hasUserRestrictionOnAnyUser(restrictionKey);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return the serial number for a user. This is a device-unique
* number assigned to that user; if the user is deleted and then a new
* user created, the new users will not be given the same serial number.
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index b6f16a7b9ff8..e9b48535a34e 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -16,10 +16,15 @@
package android.os;
-import android.hardware.vibrator.V1_0.Constants.EffectStrength;
-import android.hardware.vibrator.V1_1.Constants.Effect_1_1;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.vibrator.V1_0.EffectStrength;
+import android.hardware.vibrator.V1_2.Effect;
+import android.net.Uri;
import android.util.MathUtils;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.Arrays;
/**
@@ -49,7 +54,7 @@ public abstract class VibrationEffect implements Parcelable {
* @see #get(int)
* @hide
*/
- public static final int EFFECT_CLICK = Effect_1_1.CLICK;
+ public static final int EFFECT_CLICK = Effect.CLICK;
/**
* A double click effect.
@@ -57,14 +62,62 @@ public abstract class VibrationEffect implements Parcelable {
* @see #get(int)
* @hide
*/
- public static final int EFFECT_DOUBLE_CLICK = Effect_1_1.DOUBLE_CLICK;
+ public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
/**
* A tick effect.
* @see #get(int)
* @hide
*/
- public static final int EFFECT_TICK = Effect_1_1.TICK;
+ public static final int EFFECT_TICK = Effect.TICK;
+
+ /**
+ * A thud effect.
+ * @see #get(int)
+ * @hide
+ */
+ public static final int EFFECT_THUD = Effect.THUD;
+
+ /**
+ * A pop effect.
+ * @see #get(int)
+ * @hide
+ */
+ public static final int EFFECT_POP = Effect.POP;
+
+ /**
+ * A heavy click effect.
+ * @see #get(int)
+ * @hide
+ */
+ public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
+
+
+ /**
+ * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
+ * pattern that can be played as a ringtone with any audio, depending on the device.
+ *
+ * @see #get(Uri, Context)
+ * @hide
+ */
+ @VisibleForTesting
+ public static final int[] RINGTONES = {
+ Effect.RINGTONE_1,
+ Effect.RINGTONE_2,
+ Effect.RINGTONE_3,
+ Effect.RINGTONE_4,
+ Effect.RINGTONE_5,
+ Effect.RINGTONE_6,
+ Effect.RINGTONE_7,
+ Effect.RINGTONE_8,
+ Effect.RINGTONE_9,
+ Effect.RINGTONE_10,
+ Effect.RINGTONE_11,
+ Effect.RINGTONE_12,
+ Effect.RINGTONE_13,
+ Effect.RINGTONE_14,
+ Effect.RINGTONE_15
+ };
/** @hide to prevent subclassing from outside of the framework */
public VibrationEffect() { }
@@ -198,6 +251,37 @@ public abstract class VibrationEffect implements Parcelable {
return effect;
}
+ /**
+ * Get a predefined vibration effect associated with a given URI.
+ *
+ * Predefined effects are a set of common vibration effects that should be identical, regardless
+ * of the app they come from, in order to provide a cohesive experience for users across
+ * the entire device. They also may be custom tailored to the device hardware in order to
+ * provide a better experience than you could otherwise build using the generic building
+ * blocks.
+ *
+ * @param uri The URI associated with the haptic effect.
+ * @param context The context used to get the URI to haptic effect association.
+ *
+ * @return The desired effect, or {@code null} if there's no associated effect.
+ *
+ * @hide
+ */
+ @Nullable
+ public static VibrationEffect get(Uri uri, Context context) {
+ String[] uris = context.getResources().getStringArray(
+ com.android.internal.R.array.config_ringtoneEffectUris);
+ for (int i = 0; i < uris.length && i < RINGTONES.length; i++) {
+ if (uris[i] == null) {
+ continue;
+ }
+ if (Uri.parse(uris[i]).equals(uri)) {
+ return get(RINGTONES[i]);
+ }
+ }
+ return null;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -548,10 +632,15 @@ public abstract class VibrationEffect implements Parcelable {
case EFFECT_CLICK:
case EFFECT_DOUBLE_CLICK:
case EFFECT_TICK:
+ case EFFECT_THUD:
+ case EFFECT_POP:
+ case EFFECT_HEAVY_CLICK:
break;
default:
- throw new IllegalArgumentException(
- "Unknown prebaked effect type (value=" + mEffectId + ")");
+ if (mEffectId < RINGTONES[0] || mEffectId > RINGTONES[RINGTONES.length - 1]) {
+ throw new IllegalArgumentException(
+ "Unknown prebaked effect type (value=" + mEffectId + ")");
+ }
}
if (!isValidEffectStrength(mEffectStrength)) {
throw new IllegalArgumentException(
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 12a495bf2821..fb22194098b6 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -80,4 +80,11 @@ public class VintfObject {
* ("28", ["libjpeg.so", "libbase.so"])]
*/
public static native Map<String, String[]> getVndkSnapshots();
+
+ /**
+ * @return target FCM version, a number specified in the device manifest
+ * indicating the FCM version that the device manifest implements. Null if
+ * device manifest doesn't specify this number (for legacy devices).
+ */
+ public static native Long getTargetFrameworkCompatibilityMatrixVersion();
}
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 670f7949dd19..57418c8b9879 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -33,6 +33,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.UUID;
/*package*/ class ZygoteStartFailedEx extends Exception {
ZygoteStartFailedEx(String s) {
@@ -61,18 +62,27 @@ public class ZygoteProcess {
/**
* The name of the socket used to communicate with the primary zygote.
*/
- private final String mSocket;
+ private final LocalSocketAddress mSocket;
/**
* The name of the secondary (alternate ABI) zygote socket.
*/
- private final String mSecondarySocket;
+ private final LocalSocketAddress mSecondarySocket;
public ZygoteProcess(String primarySocket, String secondarySocket) {
+ this(new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED),
+ new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED));
+ }
+
+ public ZygoteProcess(LocalSocketAddress primarySocket, LocalSocketAddress secondarySocket) {
mSocket = primarySocket;
mSecondarySocket = secondarySocket;
}
+ public LocalSocketAddress getPrimarySocketAddress() {
+ return mSocket;
+ }
+
/**
* State for communicating with the zygote process.
*/
@@ -92,14 +102,13 @@ public class ZygoteProcess {
this.abiList = abiList;
}
- public static ZygoteState connect(String socketAddress) throws IOException {
+ public static ZygoteState connect(LocalSocketAddress address) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket();
try {
- zygoteSocket.connect(new LocalSocketAddress(socketAddress,
- LocalSocketAddress.Namespace.RESERVED));
+ zygoteSocket.connect(address);
zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
@@ -115,8 +124,8 @@ public class ZygoteProcess {
}
String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
- Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "
- + abiListString);
+ Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/"
+ + address.getName() + " opened, supported ABIS: " + abiListString);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
@@ -209,7 +218,8 @@ public class ZygoteProcess {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
- abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
+ abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
+ zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -325,6 +335,8 @@ public class ZygoteProcess {
* @param abi the ABI the process should use.
* @param instructionSet null-ok the instruction set to use.
* @param appDataDir null-ok the data directory of the app.
+ * @param startChildZygote Start a sub-zygote. This creates a new zygote process
+ * that has its state cloned from this zygote process.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
@@ -340,6 +352,7 @@ public class ZygoteProcess {
String instructionSet,
String appDataDir,
String invokeWith,
+ boolean startChildZygote,
String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -396,6 +409,10 @@ public class ZygoteProcess {
argsForZygote.add(invokeWith);
}
+ if (startChildZygote) {
+ argsForZygote.add("--start-child-zygote");
+ }
+
argsForZygote.add(processClass);
if (extraArgs != null) {
@@ -410,6 +427,18 @@ public class ZygoteProcess {
}
/**
+ * Closes the connections to the zygote, if they exist.
+ */
+ public void close() {
+ if (primaryZygoteState != null) {
+ primaryZygoteState.close();
+ }
+ if (secondaryZygoteState != null) {
+ secondaryZygoteState.close();
+ }
+ }
+
+ /**
* Tries to establish a connection to the zygote that handles a given {@code abi}. Might block
* and retry if the zygote is unresponsive. This method is a no-op if a connection is
* already open.
@@ -514,9 +543,19 @@ public class ZygoteProcess {
* @param socketName The name of the socket to connect to.
*/
public static void waitForConnectionToZygote(String socketName) {
+ final LocalSocketAddress address =
+ new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.RESERVED);
+ waitForConnectionToZygote(address);
+ }
+
+ /**
+ * Try connecting to the Zygote over and over again until we hit a time-out.
+ * @param address The name of the socket to connect to.
+ */
+ public static void waitForConnectionToZygote(LocalSocketAddress address) {
for (int n = 20; n >= 0; n--) {
try {
- final ZygoteState zs = ZygoteState.connect(socketName);
+ final ZygoteState zs = ZygoteState.connect(address);
zs.close();
return;
} catch (IOException ioe) {
@@ -529,6 +568,38 @@ public class ZygoteProcess {
} catch (InterruptedException ie) {
}
}
- Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + socketName);
+ Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName());
+ }
+
+ /**
+ * Starts a new zygote process as a child of this zygote. This is used to create
+ * secondary zygotes that inherit data from the zygote that this object
+ * communicates with. This returns a new ZygoteProcess representing a connection
+ * to the newly created zygote. Throws an exception if the zygote cannot be started.
+ */
+ public ChildZygoteProcess startChildZygote(final String processClass,
+ final String niceName,
+ int uid, int gid, int[] gids,
+ int runtimeFlags,
+ String seInfo,
+ String abi,
+ String instructionSet) {
+ // Create an unguessable address in the global abstract namespace.
+ final LocalSocketAddress serverAddress = new LocalSocketAddress(
+ processClass + "/" + UUID.randomUUID().toString());
+
+ final String[] extraArgs = {Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG + serverAddress.getName()};
+
+ Process.ProcessStartResult result;
+ try {
+ result = startViaZygote(processClass, niceName, uid, gid,
+ gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
+ abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
+ true /* startChildZygote */, extraArgs);
+ } catch (ZygoteStartFailedEx ex) {
+ throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
+ }
+
+ return new ChildZygoteProcess(serverAddress, result.pid);
}
}
diff --git a/core/java/android/os/storage/StorageResultCode.java b/core/java/android/os/storage/StorageResultCode.java
deleted file mode 100644
index c8438870585a..000000000000
--- a/core/java/android/os/storage/StorageResultCode.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os.storage;
-
-/**
- * Class that provides access to constants returned from StorageManager
- * and lower level StorageManagerService APIs.
- *
- * @hide
- */
-public class StorageResultCode
-{
- /**
- * Operation succeeded.
- * @see android.os.storage.StorageManager
- */
- public static final int OperationSucceeded = 0;
-
- /**
- * Operation failed: Internal error.
- * @see android.os.storage.StorageManager
- */
- public static final int OperationFailedInternalError = -1;
-
- /**
- * Operation failed: Missing media.
- * @see android.os.storage.StorageManager
- */
- public static final int OperationFailedNoMedia = -2;
-
- /**
- * Operation failed: Media is blank.
- * @see android.os.storage.StorageManager
- */
- public static final int OperationFailedMediaBlank = -3;
-
- /**
- * Operation failed: Media is corrupt.
- * @see android.os.storage.StorageManager
- */
- public static final int OperationFailedMediaCorrupt = -4;
-
- /**
- * Operation failed: Storage not mounted.
- * @see android.os.storage.StorageManager
- */
- public static final int OperationFailedStorageNotMounted = -5;
-
- /**
- * Operation failed: Storage is mounted.
- * @see android.os.storage.StorageManager
- */
- public static final int OperationFailedStorageMounted = -6;
-
- /**
- * Operation failed: Storage is busy.
- * @see android.os.storage.StorageManager
- */
- public static final int OperationFailedStorageBusy = -7;
-
-}
diff --git a/core/java/android/preference/OWNERS b/core/java/android/preference/OWNERS
new file mode 100644
index 000000000000..d20511fcfdf2
--- /dev/null
+++ b/core/java/android/preference/OWNERS
@@ -0,0 +1,2 @@
+pavlis@google.com
+clarabayarri@google.com
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 51b77980fcf4..e436bc6ea30f 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -18,6 +18,7 @@ package android.print;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -27,6 +28,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -104,6 +106,7 @@ import java.util.Map;
* @see PrintJobInfo
*/
@SystemService(Context.PRINT_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_PRINTING)
public final class PrintManager {
private static final String LOG_TAG = "PrintManager";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e709c532200b..2ec9009ee549 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3692,18 +3692,15 @@ public final class Settings {
new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
/**
- * User-selected RTT mode
+ * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT
+ * calls when supported by the device and carrier. Boolean value.
* 0 = OFF
- * 1 = FULL
- * 2 = VCO
- * 3 = HCO
- * Uses the same constants as TTY (e.g. {@link android.telecom.TelecomManager#TTY_MODE_OFF})
- * @hide
+ * 1 = ON
*/
public static final String RTT_CALLING_MODE = "rtt_calling_mode";
/** @hide */
- public static final Validator RTT_CALLING_MODE_VALIDATOR = TTY_MODE_VALIDATOR;
+ public static final Validator RTT_CALLING_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
/**
* Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
@@ -5385,6 +5382,17 @@ public final class Settings {
"autofill_user_data_max_field_classification_size";
/**
+ * Defines value returned by
+ * {@link android.service.autofill.UserData#getMaxCategoryCount()}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT =
+ "autofill_user_data_max_category_count";
+
+ /**
* Defines value returned by {@link android.service.autofill.UserData#getMaxValueLength()}.
*
* @hide
@@ -5443,32 +5451,6 @@ public final class Settings {
*/
public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
- private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() {
- @Override
- public boolean validate(String value) {
- if (value == null) {
- return false;
- }
- String[] inputMethods = value.split(":");
- boolean valid = true;
- for (String inputMethod : inputMethods) {
- if (inputMethod.length() == 0) {
- return false;
- }
- String[] subparts = inputMethod.split(";");
- for (String subpart : subparts) {
- // allow either a non negative integer or a ComponentName
- valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart)
- || COMPONENT_NAME_VALIDATOR.validate(subpart));
- }
- if (!valid) {
- return false;
- }
- }
- return valid;
- }
- };
-
/**
* List of system input methods that are currently disabled. This is a string
* containing the IDs of all disabled input methods, each ID separated
@@ -7396,7 +7378,8 @@ public final class Settings {
*/
public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
- private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+ private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR =
+ new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
/**
* Control the color temperature of Night Display, represented in Kelvin.
@@ -7679,6 +7662,24 @@ public final class Settings {
*/
public static final String BACKUP_MANAGER_CONSTANTS = "backup_manager_constants";
+
+ /**
+ * Local transport parameters so we can configure it for tests.
+ * This is encoded as a key=value list, separated by commas.
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * fake_encryption_flag (boolean)
+ * </pre>
+ *
+ * <p>
+ * Type: string
+ * @hide
+ */
+ public static final String BACKUP_LOCAL_TRANSPORT_PARAMETERS =
+ "backup_local_transport_parameters";
+
/**
* Flag to set if the system should predictively attempt to re-enable Bluetooth while
* the user is driving.
@@ -7708,7 +7709,6 @@ public final class Settings {
ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
ENABLED_ACCESSIBILITY_SERVICES,
ENABLED_VR_LISTENERS,
- ENABLED_INPUT_METHODS,
TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
TOUCH_EXPLORATION_ENABLED,
ACCESSIBILITY_ENABLED,
@@ -7814,7 +7814,6 @@ public final class Settings {
VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES,
ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR);
VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR);
- VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR);
VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR);
VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR);
@@ -8389,10 +8388,10 @@ public final class Settings {
private static final Validator POWER_SOUNDS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
/**
- * URI for the "wireless charging started" sound.
+ * URI for the "wireless charging started" and "wired charging started" sound.
* @hide
*/
- public static final String WIRELESS_CHARGING_STARTED_SOUND =
+ public static final String CHARGING_STARTED_SOUND =
"wireless_charging_started_sound";
/**
@@ -8562,9 +8561,8 @@ public final class Settings {
*
* @see android.service.euicc.EuiccService
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+ @SystemApi
public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
/**
@@ -8780,6 +8778,7 @@ public final class Settings {
/** {@hide} */
public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
/** {@hide} */
+ @Deprecated
public static final String NETSTATS_TIME_CACHE_MAX_AGE = "netstats_time_cache_max_age";
/** {@hide} */
public static final String NETSTATS_GLOBAL_ALERT_BYTES = "netstats_global_alert_bytes";
@@ -10477,6 +10476,9 @@ public final class Settings {
* <pre>
* smart_selection_dark_launch (boolean)
* smart_selection_enabled_for_edit_text (boolean)
+ * suggest_selection_max_range_length (int)
+ * classify_text_max_range_length (int)
+ * generate_links_max_text_length (int)
* </pre>
*
* <p>
@@ -10495,6 +10497,7 @@ public final class Settings {
* track_cpu_times_by_proc_state (boolean)
* track_cpu_active_cluster_time (boolean)
* read_binary_cpu_time (boolean)
+ * proc_state_cpu_times_read_delay_ms (long)
* </pre>
*
* <p>
@@ -10505,6 +10508,16 @@ public final class Settings {
public static final String BATTERY_STATS_CONSTANTS = "battery_stats_constants";
/**
+ * SyncManager specific settings.
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * @see com.android.server.content.SyncManagerConstants
+ */
+ public static final String SYNC_MANAGER_CONSTANTS = "sync_manager_constants";
+
+ /**
* Whether or not App Standby feature is enabled. This controls throttling of apps
* based on usage patterns and predictions.
* Type: int (0 for false, 1 for true)
@@ -11372,15 +11385,25 @@ public final class Settings {
"chained_battery_attribution_enabled";
/**
- * The packages whitelisted to be run in autofill compatibility mode.
+ * The packages whitelisted to be run in autofill compatibility mode. The list
+ * of packages is ":" colon delimited.
*
* @hide
*/
@SystemApi
+ @TestApi
public static final String AUTOFILL_COMPAT_ALLOWED_PACKAGES =
"autofill_compat_allowed_packages";
/**
+ * Exemptions to the hidden API blacklist.
+ *
+ * @hide
+ */
+ public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS =
+ "hidden_api_blacklist_exemptions";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
@@ -12079,6 +12102,28 @@ public final class Settings {
"enable_gnss_raw_meas_full_tracking";
/**
+ * Whether the notification should be ongoing (persistent) when a carrier app install is
+ * required.
+ *
+ * The value is a boolean (1 or 0).
+ * @hide
+ */
+ @SystemApi
+ public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT =
+ "install_carrier_app_notification_persistent";
+
+ /**
+ * The amount of time (ms) to hide the install carrier app notification after the user has
+ * ignored it. After this time passes, the notification will be shown again
+ *
+ * The value is a long
+ * @hide
+ */
+ @SystemApi
+ public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS =
+ "install_carrier_app_notification_sleep_millis";
+
+ /**
* Whether we've enabled zram on this device. Takes effect on
* reboot. The value "1" enables zram; "0" disables it, and
* everything else is unspecified.
@@ -12088,11 +12133,22 @@ public final class Settings {
"zram_enabled";
/**
- * Whether smart replies in notifications are enabled.
+ * Configuration flags for smart replies in notifications.
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "enabled=1,max_squeeze_remeasure_count=3"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * enabled (boolean)
+ * max_squeeze_remeasure_attempts (int)
+ * </pre>
+ * @see com.android.systemui.statusbar.policy.SmartReplyConstants
* @hide
*/
- public static final String ENABLE_SMART_REPLIES_IN_NOTIFICATIONS =
- "enable_smart_replies_in_notifications";
+ public static final String SMART_REPLIES_IN_NOTIFICATIONS_FLAGS =
+ "smart_replies_in_notifications_flags";
/**
* If nonzero, crashes in foreground processes will bring up a dialog.
@@ -12113,6 +12169,12 @@ public final class Settings {
* @hide
*/
public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog";
+
+ /**
+ * If nonzero, will show the zen upgrade notification when the user toggles DND on/off.
+ * @hide
+ */
+ public static final String SHOW_ZEN_UPGRADE_NOTIFICATION = "show_zen_upgrade_notification";
}
/**
diff --git a/core/java/android/provider/SettingsSlicesContract.java b/core/java/android/provider/SettingsSlicesContract.java
index f79d852ddefc..7dc948899dfe 100644
--- a/core/java/android/provider/SettingsSlicesContract.java
+++ b/core/java/android/provider/SettingsSlicesContract.java
@@ -31,12 +31,12 @@ import android.net.Uri;
* <p>
* {@link Uri} builder example:
* <pre>
- * Uri wifiActionUri = AUTHORITY_URI
+ * Uri wifiActionUri = BASE_URI
* .buildUpon()
* .appendPath(PATH_SETTING_ACTION)
* .appendPath(KEY_WIFI)
* .build();
- * Uri bluetoothIntentUri = AUTHORITY_URI
+ * Uri bluetoothIntentUri = BASE_URI
* .buildUpon()
* .appendPath(PATH_SETTING_INTENT)
* .appendPath(KEY_BLUETOOTH)
diff --git a/core/java/android/security/IConfirmationPromptCallback.aidl b/core/java/android/security/IConfirmationPromptCallback.aidl
deleted file mode 100644
index 96a1a04828b5..000000000000
--- a/core/java/android/security/IConfirmationPromptCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2017, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security;
-
-/**
- * This must be kept manually in sync with system/security/keystore until AIDL
- * can generate both Java and C++ bindings.
- *
- * @hide
- */
-interface IConfirmationPromptCallback {
- oneway void onConfirmationPromptCompleted(in int result, in byte[] dataThatWasConfirmed);
-}
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
deleted file mode 100644
index 738eb6865230..000000000000
--- a/core/java/android/security/IKeystoreService.aidl
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Copyright (c) 2015, 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.security;
-
-import android.security.keymaster.ExportResult;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterCertificateChain;
-import android.security.keymaster.KeymasterBlob;
-import android.security.keymaster.OperationResult;
-import android.security.KeystoreArguments;
-
-/**
- * This must be kept manually in sync with system/security/keystore until AIDL
- * can generate both Java and C++ bindings.
- *
- * @hide
- */
-interface IKeystoreService {
- int getState(int userId);
- byte[] get(String name, int uid);
- int insert(String name, in byte[] item, int uid, int flags);
- int del(String name, int uid);
- int exist(String name, int uid);
- String[] list(String namePrefix, int uid);
- int reset();
- int onUserPasswordChanged(int userId, String newPassword);
- int lock(int userId);
- int unlock(int userId, String userPassword);
- int isEmpty(int userId);
- int generate(String name, int uid, int keyType, int keySize, int flags,
- in KeystoreArguments args);
- int import_key(String name, in byte[] data, int uid, int flags);
- byte[] sign(String name, in byte[] data);
- int verify(String name, in byte[] data, in byte[] signature);
- byte[] get_pubkey(String name);
- String grant(String name, int granteeUid);
- int ungrant(String name, int granteeUid);
- long getmtime(String name, int uid);
- int is_hardware_backed(String string);
- int clear_uid(long uid);
-
- // Keymaster 0.4 methods
- int addRngEntropy(in byte[] data, int flags);
- int generateKey(String alias, in KeymasterArguments arguments, in byte[] entropy, int uid,
- int flags, out KeyCharacteristics characteristics);
- int getKeyCharacteristics(String alias, in KeymasterBlob clientId, in KeymasterBlob appId,
- int uid, out KeyCharacteristics characteristics);
- int importKey(String alias, in KeymasterArguments arguments, int format,
- in byte[] keyData, int uid, int flags, out KeyCharacteristics characteristics);
- ExportResult exportKey(String alias, int format, in KeymasterBlob clientId,
- in KeymasterBlob appId, int uid);
- OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable,
- in KeymasterArguments params, in byte[] entropy, int uid);
- OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input);
- OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature,
- in byte[] entropy);
- int abort(IBinder handle);
- boolean isOperationAuthorized(IBinder token);
- int addAuthToken(in byte[] authToken);
- int onUserAdded(int userId, int parentId);
- int onUserRemoved(int userId);
- int attestKey(String alias, in KeymasterArguments params, out KeymasterCertificateChain chain);
- int attestDeviceIds(in KeymasterArguments params, out KeymasterCertificateChain chain);
- int onDeviceOffBody();
- int importWrappedKey(in String wrappedKeyAlias, in byte[] wrappedKey,
- in String wrappingKeyAlias, in byte[] maskingKey, in KeymasterArguments arguments,
- in long rootSid, in long fingerprintSid,
- out KeyCharacteristics characteristics);
- int presentConfirmationPrompt(IBinder listener, String promptText, in byte[] extraData,
- in String locale, in int uiOptionsAsFlags);
- int cancelConfirmationPrompt(IBinder listener);
-}
diff --git a/core/java/android/security/KeystoreArguments.aidl b/core/java/android/security/KeystoreArguments.aidl
deleted file mode 100644
index dc8ed50182ed..000000000000
--- a/core/java/android/security/KeystoreArguments.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Copyright (c) 2015, 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.security;
-
-/* @hide */
-parcelable KeystoreArguments cpp_header "keystore/KeystoreArguments.h";
diff --git a/core/java/android/security/keymaster/ExportResult.aidl b/core/java/android/security/keymaster/ExportResult.aidl
deleted file mode 100644
index 17486531a3f0..000000000000
--- a/core/java/android/security/keymaster/ExportResult.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.security.keymaster;
-
-/* @hide */
-parcelable ExportResult cpp_header "keystore/ExportResult.h";
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.aidl b/core/java/android/security/keymaster/KeyCharacteristics.aidl
deleted file mode 100644
index 32e75ad267b2..000000000000
--- a/core/java/android/security/keymaster/KeyCharacteristics.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.security.keymaster;
-
-/* @hide */
-parcelable KeyCharacteristics cpp_header "keystore/KeyCharacteristics.h";
diff --git a/core/java/android/security/keymaster/KeymasterArguments.aidl b/core/java/android/security/keymaster/KeymasterArguments.aidl
deleted file mode 100644
index 44d9f0954781..000000000000
--- a/core/java/android/security/keymaster/KeymasterArguments.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.security.keymaster;
-
-/* @hide */
-parcelable KeymasterArguments cpp_header "keystore/KeymasterArguments.h";
diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl
deleted file mode 100644
index 5c5db9ec314b..000000000000
--- a/core/java/android/security/keymaster/KeymasterBlob.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.security.keymaster;
-
-/* @hide */
-parcelable KeymasterBlob cpp_header "keystore/KeymasterBlob.h";
diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
deleted file mode 100644
index ddb5cae1a254..000000000000
--- a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keymaster;
-
-/* @hide */
-parcelable KeymasterCertificateChain cpp_header "keystore/KeymasterCertificateChain.h";
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 1d1333504350..f4dcce1e7e58 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -75,6 +75,7 @@ public final class KeymasterDefs {
public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508;
+ public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509;
public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
@@ -216,6 +217,7 @@ public final class KeymasterDefs {
public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58;
public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59;
public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
+ public static final int KM_ERROR_DEVICE_LOCKED = -72;
public static final int KM_ERROR_UNIMPLEMENTED = -100;
public static final int KM_ERROR_VERSION_MISMATCH = -101;
public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
@@ -262,6 +264,7 @@ public final class KeymasterDefs {
sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH,
"Invalid MAC or authentication tag length");
sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids");
+ sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked");
sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
}
diff --git a/core/java/android/security/keymaster/OperationResult.aidl b/core/java/android/security/keymaster/OperationResult.aidl
deleted file mode 100644
index db689d46521a..000000000000
--- a/core/java/android/security/keymaster/OperationResult.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2015 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.security.keymaster;
-
-/* @hide */
-parcelable OperationResult cpp_header "keystore/OperationResult.h";
diff --git a/core/java/android/security/keystore/OWNERS b/core/java/android/security/keystore/OWNERS
new file mode 100644
index 000000000000..bb487fb52c9f
--- /dev/null
+++ b/core/java/android/security/keystore/OWNERS
@@ -0,0 +1,4 @@
+aseemk@google.com
+bozhu@google.com
+dementyev@google.com
+robertberry@google.com
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index 29c6cea14980..70c4ec07ee4e 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -32,19 +32,19 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.util.Xml;
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.util.Map;
+import java.io.PrintWriter;
/**
* {@link ServiceInfo} and meta-data about an {@link AutofillService}.
@@ -79,7 +79,7 @@ public final class AutofillServiceInfo {
private final String mSettingsActivity;
@Nullable
- private final Map<String, Long> mCompatibilityPackages;
+ private final ArrayMap<String, Pair<Long, String>> mCompatibilityPackages;
public AutofillServiceInfo(Context context, ComponentName comp, int userHandle)
throws PackageManager.NameNotFoundException {
@@ -117,7 +117,7 @@ public final class AutofillServiceInfo {
}
String settingsActivity = null;
- Map<String, Long> compatibilityPackages = null;
+ ArrayMap<String, Pair<Long, String>> compatibilityPackages = null;
try {
final Resources resources = context.getPackageManager().getResourcesForApplication(
@@ -153,9 +153,10 @@ public final class AutofillServiceInfo {
mCompatibilityPackages = compatibilityPackages;
}
- private Map<String, Long> parseCompatibilityPackages(XmlPullParser parser, Resources resources)
+ private ArrayMap<String, Pair<Long, String>> parseCompatibilityPackages(XmlPullParser parser,
+ Resources resources)
throws IOException, XmlPullParserException {
- Map<String, Long> compatibilityPackages = null;
+ ArrayMap<String, Pair<Long, String>> compatibilityPackages = null;
final int outerDepth = parser.getDepth();
int type;
@@ -199,11 +200,13 @@ public final class AutofillServiceInfo {
} else {
maxVersionCode = Long.MAX_VALUE;
}
+ final String urlBarResourceId = cpAttributes.getString(
+ R.styleable.AutofillService_CompatibilityPackage_urlBarResourceId);
if (compatibilityPackages == null) {
compatibilityPackages = new ArrayMap<>();
}
- compatibilityPackages.put(name, maxVersionCode);
+ compatibilityPackages.put(name, new Pair<>(maxVersionCode, urlBarResourceId));
} finally {
XmlUtils.skipCurrentTag(parser);
if (cpAttributes != null) {
@@ -225,16 +228,21 @@ public final class AutofillServiceInfo {
return mSettingsActivity;
}
+ public ArrayMap<String, Pair<Long, String>> getCompatibilityPackages() {
+ return mCompatibilityPackages;
+ }
+
+ /**
+ * Gets the resource id of the URL bar for a package. Used in compat mode
+ */
+ // TODO: return a list of strings instead
@Nullable
- public boolean isCompatibilityModeRequested(String packageName, long versionCode) {
+ public String getUrlBarResourceId(String packageName) {
if (mCompatibilityPackages == null) {
- return false;
+ return null;
}
- final Long maxVersionCode = mCompatibilityPackages.get(packageName);
- if (maxVersionCode == null) {
- return false;
- }
- return versionCode <= maxVersionCode;
+ final Pair<Long, String> pair = mCompatibilityPackages.get(packageName);
+ return pair == null ? null : pair.second;
}
@Override
@@ -247,4 +255,13 @@ public final class AutofillServiceInfo {
&& !mCompatibilityPackages.isEmpty()).append("]");
return builder.toString();
}
+
+ /**
+ * Dumps it!
+ */
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("Component: "); pw.println(getServiceInfo().getComponentName());
+ pw.print(prefix); pw.print("Settings: "); pw.println(mSettingsActivity);
+ pw.print(prefix); pw.print("Compat packages: "); pw.println(mCompatibilityPackages);
+ }
}
diff --git a/core/java/android/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java
index 4e1425d86380..ec24a09a470f 100644
--- a/core/java/android/service/autofill/DateTransformation.java
+++ b/core/java/android/service/autofill/DateTransformation.java
@@ -20,6 +20,7 @@ import static android.view.autofill.Helper.sDebug;
import android.annotation.NonNull;
import android.annotation.TestApi;
+import android.icu.text.DateFormat;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -30,7 +31,6 @@ import android.widget.TextView;
import com.android.internal.util.Preconditions;
-import java.text.DateFormat;
import java.util.Date;
/**
diff --git a/core/java/android/service/autofill/DateValueSanitizer.java b/core/java/android/service/autofill/DateValueSanitizer.java
index 0f7b540f8a24..4f797f46da2a 100644
--- a/core/java/android/service/autofill/DateValueSanitizer.java
+++ b/core/java/android/service/autofill/DateValueSanitizer.java
@@ -21,6 +21,7 @@ import static android.view.autofill.Helper.sDebug;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.icu.text.DateFormat;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -28,7 +29,6 @@ import android.view.autofill.AutofillValue;
import com.android.internal.util.Preconditions;
-import java.text.DateFormat;
import java.util.Date;
/**
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index cd1efd68df6f..5bf56cb9c1b5 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -108,21 +108,21 @@ public final class FieldClassification {
*/
public static final class Match {
- private final String mRemoteId;
+ private final String mCategoryId;
private final float mScore;
/** @hide */
- public Match(String remoteId, float score) {
- mRemoteId = Preconditions.checkNotNull(remoteId);
+ public Match(String categoryId, float score) {
+ mCategoryId = Preconditions.checkNotNull(categoryId);
mScore = score;
}
/**
- * Gets the remote id of the {@link UserData} entry.
+ * Gets the category id of the {@link UserData} entry.
*/
@NonNull
- public String getRemoteId() {
- return mRemoteId;
+ public String getCategoryId() {
+ return mCategoryId;
}
/**
@@ -149,13 +149,13 @@ public final class FieldClassification {
public String toString() {
if (!sDebug) return super.toString();
- final StringBuilder string = new StringBuilder("Match: remoteId=");
- Helper.appendRedacted(string, mRemoteId);
+ final StringBuilder string = new StringBuilder("Match: categoryId=");
+ Helper.appendRedacted(string, mCategoryId);
return string.append(", score=").append(mScore).toString();
}
private void writeToParcel(@NonNull Parcel parcel) {
- parcel.writeString(mRemoteId);
+ parcel.writeString(mCategoryId);
parcel.writeFloat(mScore);
}
diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java
index cda2f4a23c9a..535c00bc5319 100644
--- a/core/java/android/service/autofill/FillContext.java
+++ b/core/java/android/service/autofill/FillContext.java
@@ -177,30 +177,6 @@ public final class FillContext implements Parcelable {
return foundNodes;
}
- /**
- * Finds the {@link ViewNode} that has the requested {@code id}, if any.
- *
- * @hide
- */
- @Nullable public ViewNode findViewNodeByAutofillId(@NonNull AutofillId id) {
- final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
- final int numWindowNodes = mStructure.getWindowNodeCount();
- for (int i = 0; i < numWindowNodes; i++) {
- nodesToProcess.add(mStructure.getWindowNodeAt(i).getRootViewNode());
- }
- while (!nodesToProcess.isEmpty()) {
- final ViewNode node = nodesToProcess.removeFirst();
- if (id.equals(node.getAutofillId())) {
- return node;
- }
- for (int i = 0; i < node.getChildCount(); i++) {
- nodesToProcess.addLast(node.getChildAt(i));
- }
- }
-
- return null;
- }
-
public static final Parcelable.Creator<FillContext> CREATOR =
new Parcelable.Creator<FillContext>() {
@Override
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 6bab6aa82375..a1dd1f846515 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -15,6 +15,7 @@
*/
package android.service.autofill;
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
@@ -31,6 +32,7 @@ import android.os.Parcelable;
import android.provider.Settings;
import android.service.autofill.FieldClassification.Match;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.view.autofill.AutofillManager;
import android.view.autofill.Helper;
@@ -48,23 +50,24 @@ public final class UserData implements Parcelable {
private static final String TAG = "UserData";
- private static final int DEFAULT_MAX_USER_DATA_SIZE = 10;
+ private static final int DEFAULT_MAX_USER_DATA_SIZE = 50;
+ private static final int DEFAULT_MAX_CATEGORY_COUNT = 10;
private static final int DEFAULT_MAX_FIELD_CLASSIFICATION_IDS_SIZE = 10;
- private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
+ private static final int DEFAULT_MIN_VALUE_LENGTH = 3;
private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
private final String mId;
private final String mAlgorithm;
private final Bundle mAlgorithmArgs;
- private final String[] mRemoteIds;
+ private final String[] mCategoryIds;
private final String[] mValues;
private UserData(Builder builder) {
mId = builder.mId;
mAlgorithm = builder.mAlgorithm;
mAlgorithmArgs = builder.mAlgorithmArgs;
- mRemoteIds = new String[builder.mRemoteIds.size()];
- builder.mRemoteIds.toArray(mRemoteIds);
+ mCategoryIds = new String[builder.mCategoryIds.size()];
+ builder.mCategoryIds.toArray(mCategoryIds);
mValues = new String[builder.mValues.size()];
builder.mValues.toArray(mValues);
}
@@ -91,8 +94,8 @@ public final class UserData implements Parcelable {
}
/** @hide */
- public String[] getRemoteIds() {
- return mRemoteIds;
+ public String[] getCategoryIds() {
+ return mCategoryIds;
}
/** @hide */
@@ -106,11 +109,11 @@ public final class UserData implements Parcelable {
pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm);
pw.print(" Args: "); pw.println(mAlgorithmArgs);
- // Cannot disclose remote ids or values because they could contain PII
- pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length);
- for (int i = 0; i < mRemoteIds.length; i++) {
+ // Cannot disclose field ids or values because they could contain PII
+ pw.print(prefix); pw.print("Field ids size: "); pw.println(mCategoryIds.length);
+ for (int i = 0; i < mCategoryIds.length; i++) {
pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": ");
- pw.println(Helper.getRedacted(mRemoteIds[i]));
+ pw.println(Helper.getRedacted(mCategoryIds[i]));
}
pw.print(prefix); pw.print("Values size: "); pw.println(mValues.length);
for (int i = 0; i < mValues.length; i++) {
@@ -124,6 +127,7 @@ public final class UserData implements Parcelable {
pw.print(prefix); pw.print("maxUserDataSize: "); pw.println(getMaxUserDataSize());
pw.print(prefix); pw.print("maxFieldClassificationIdsSize: ");
pw.println(getMaxFieldClassificationIdsSize());
+ pw.print(prefix); pw.print("maxCategoryCount: "); pw.println(getMaxCategoryCount());
pw.print(prefix); pw.print("minValueLength: "); pw.println(getMinValueLength());
pw.print(prefix); pw.print("maxValueLength: "); pw.println(getMaxValueLength());
}
@@ -133,44 +137,59 @@ public final class UserData implements Parcelable {
*/
public static final class Builder {
private final String mId;
- private final ArrayList<String> mRemoteIds;
+ private final ArrayList<String> mCategoryIds;
private final ArrayList<String> mValues;
private String mAlgorithm;
private Bundle mAlgorithmArgs;
private boolean mDestroyed;
+ // Non-persistent array used to limit the number of unique ids.
+ private final ArraySet<String> mUniqueCategoryIds;
+
/**
* Creates a new builder for the user data used for <a href="#FieldClassification">field
* classification</a>.
*
- * <p>The user data must contain at least one pair of {@code remoteId} -> {@code value}, and
- * more pairs can be added through the {@link #add(String, String)} method.
+ * <p>The user data must contain at least one pair of {@code value} -> {@code categoryId},
+ * and more pairs can be added through the {@link #add(String, String)} method. For example:
+ *
+ * <pre class="prettyprint">
+ * new UserData.Builder("v1", "Bart Simpson", "name")
+ * .add("bart.simpson@example.com", "email")
+ * .add("el_barto@example.com", "email")
+ * .build();
+ * </pre>
*
* @param id id used to identify the whole {@link UserData} object. This id is also returned
* by {@link AutofillManager#getUserDataId()}, which can be used to check if the
* {@link UserData} is up-to-date without fetching the whole object (through
* {@link AutofillManager#getUserData()}).
- * @param remoteId unique string used to identify a user data value.
+ *
* @param value value of the user data.
+ * @param categoryId string used to identify the category the value is associated with.
*
* @throws IllegalArgumentException if any of the following occurs:
* <ol>
- * <li>{@code id} is empty
- * <li>{@code remoteId} is empty
- * <li>{@code value} is empty
- * <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}
- * <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()}
+ * <li>{@code id} is empty</li>
+ * <li>{@code categoryId} is empty</li>
+ * <li>{@code value} is empty</li>
+ * <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
+ * <li>the length of {@code value} is higher than
+ * {@link UserData#getMaxValueLength()}</li>
* </ol>
+ *
*/
- public Builder(@NonNull String id, @NonNull String remoteId, @NonNull String value) {
+ // TODO(b/70407264): ignore entry instead of throwing exception when settings changed
+ public Builder(@NonNull String id, @NonNull String value, @NonNull String categoryId) {
mId = checkNotEmpty("id", id);
- checkNotEmpty("remoteId", remoteId);
+ checkNotEmpty("categoryId", categoryId);
checkValidValue(value);
- final int capacity = getMaxUserDataSize();
- mRemoteIds = new ArrayList<>(capacity);
- mValues = new ArrayList<>(capacity);
- mRemoteIds.add(remoteId);
- mValues.add(value);
+ final int maxUserDataSize = getMaxUserDataSize();
+ mCategoryIds = new ArrayList<>(maxUserDataSize);
+ mValues = new ArrayList<>(maxUserDataSize);
+ mUniqueCategoryIds = new ArraySet<>(getMaxCategoryCount());
+
+ addMapping(value, categoryId);
}
/**
@@ -190,6 +209,7 @@ public final class UserData implements Parcelable {
*/
public Builder setFieldClassificationAlgorithm(@Nullable String name,
@Nullable Bundle args) {
+ throwIfDestroyed();
mAlgorithm = name;
mAlgorithmArgs = args;
return this;
@@ -198,37 +218,58 @@ public final class UserData implements Parcelable {
/**
* Adds a new value for user data.
*
- * @param remoteId unique string used to identify the user data.
* @param value value of the user data.
+ * @param categoryId string used to identify the category the value is associated with.
*
- * @throws IllegalStateException if {@link #build()} or
- * {@link #add(String, String)} with the same {@code remoteId} has already
- * been called, or if the number of values add (i.e., calls made to this method plus
- * constructor) is more than {@link UserData#getMaxUserDataSize()}.
+ * @throws IllegalStateException if:
+ * <ol>
+ * <li>{@link #build()} already called</li>
+ * <li>the {@code value} has already been added</li>
+ * <li>the number of unique {@code categoryId} values added so far is more than
+ * {@link UserData#getMaxCategoryCount()}</li>
+ * <li>the number of {@code values} added so far is is more than
+ * {@link UserData#getMaxUserDataSize()}</li>
+ * </ol>
*
- * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the
- * length of {@code value} is lower than {@link UserData#getMinValueLength()}
- * or higher than {@link UserData#getMaxValueLength()}.
+ * @throws IllegalArgumentException if any of the following occurs:
+ * <ol>
+ * <li>{@code id} is empty</li>
+ * <li>{@code categoryId} is empty</li>
+ * <li>{@code value} is empty</li>
+ * <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
+ * <li>the length of {@code value} is higher than
+ * {@link UserData#getMaxValueLength()}</li>
+ * </ol>
*/
- public Builder add(@NonNull String remoteId, @NonNull String value) {
+ // TODO(b/70407264): ignore entry instead of throwing exception when settings changed
+ public Builder add(@NonNull String value, @NonNull String categoryId) {
throwIfDestroyed();
- checkNotEmpty("remoteId", remoteId);
+ checkNotEmpty("categoryId", categoryId);
checkValidValue(value);
- Preconditions.checkState(!mRemoteIds.contains(remoteId),
- // Don't include remoteId on message because it could contain PII
- "already has entry with same remoteId");
+ if (!mUniqueCategoryIds.contains(categoryId)) {
+ // New category - check size
+ Preconditions.checkState(mUniqueCategoryIds.size() < getMaxCategoryCount(),
+ "already added " + mUniqueCategoryIds.size() + " unique category ids");
+
+ }
+
Preconditions.checkState(!mValues.contains(value),
- // Don't include remoteId on message because it could contain PII
+ // Don't include value on message because it could contain PII
"already has entry with same value");
- Preconditions.checkState(mRemoteIds.size() < getMaxUserDataSize(),
- "already added " + mRemoteIds.size() + " elements");
- mRemoteIds.add(remoteId);
- mValues.add(value);
+ Preconditions.checkState(mValues.size() < getMaxUserDataSize(),
+ "already added " + mValues.size() + " elements");
+ addMapping(value, categoryId);
return this;
}
+ private void addMapping(@NonNull String value, @NonNull String categoryId) {
+ mCategoryIds.add(categoryId);
+ mValues.add(value);
+ mUniqueCategoryIds.add(categoryId);
+ }
+
private String checkNotEmpty(@NonNull String name, @Nullable String value) {
Preconditions.checkNotNull(value);
Preconditions.checkArgument(!TextUtils.isEmpty(value), "%s cannot be empty", name);
@@ -273,9 +314,9 @@ public final class UserData implements Parcelable {
final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId)
.append(", algorithm=").append(mAlgorithm);
- // Cannot disclose remote ids or values because they could contain PII
- builder.append(", remoteIds=");
- Helper.appendRedacted(builder, mRemoteIds);
+ // Cannot disclose category ids or values because they could contain PII
+ builder.append(", categoryIds=");
+ Helper.appendRedacted(builder, mCategoryIds);
builder.append(", values=");
Helper.appendRedacted(builder, mValues);
return builder.append("]").toString();
@@ -293,7 +334,7 @@ public final class UserData implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mId);
- parcel.writeStringArray(mRemoteIds);
+ parcel.writeStringArray(mCategoryIds);
parcel.writeStringArray(mValues);
parcel.writeString(mAlgorithm);
parcel.writeBundle(mAlgorithmArgs);
@@ -307,12 +348,12 @@ public final class UserData implements Parcelable {
// the system obeys the contract of the builder to avoid attacks
// using specially crafted parcels.
final String id = parcel.readString();
- final String[] remoteIds = parcel.readStringArray();
+ final String[] categoryIds = parcel.readStringArray();
final String[] values = parcel.readStringArray();
- final Builder builder = new Builder(id, remoteIds[0], values[0])
+ final Builder builder = new Builder(id, values[0], categoryIds[0])
.setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle());
- for (int i = 1; i < remoteIds.length; i++) {
- builder.add(remoteIds[i], values[i]);
+ for (int i = 1; i < categoryIds.length; i++) {
+ builder.add(values[i], categoryIds[i]);
}
return builder.build();
}
@@ -340,6 +381,14 @@ public final class UserData implements Parcelable {
}
/**
+ * Gets the maximum number of unique category ids that can be passed to
+ * the builder's constructor and {@link Builder#add(String, String)}.
+ */
+ public static int getMaxCategoryCount() {
+ return getInt(AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, DEFAULT_MAX_CATEGORY_COUNT);
+ }
+
+ /**
* Gets the minimum length of values passed to the builder's constructor or
* or {@link Builder#add(String, String)}.
*/
diff --git a/core/java/android/service/euicc/EuiccProfileInfo.java b/core/java/android/service/euicc/EuiccProfileInfo.java
index 8e752d1c6c1d..cb4f10455ec9 100644
--- a/core/java/android/service/euicc/EuiccProfileInfo.java
+++ b/core/java/android/service/euicc/EuiccProfileInfo.java
@@ -17,6 +17,7 @@ package android.service.euicc;
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.carrier.CarrierIdentifier;
@@ -26,15 +27,15 @@ import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
* Information about an embedded profile (subscription) on an eUICC.
*
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+@SystemApi
public final class EuiccProfileInfo implements Parcelable {
/** Profile policy rules (bit mask) */
@@ -44,6 +45,7 @@ public final class EuiccProfileInfo implements Parcelable {
POLICY_RULE_DO_NOT_DELETE,
POLICY_RULE_DELETE_AFTER_DISABLING
})
+ /** @hide */
public @interface PolicyRule {}
/** Once this profile is enabled, it cannot be disabled. */
public static final int POLICY_RULE_DO_NOT_DISABLE = 1;
@@ -60,6 +62,7 @@ public final class EuiccProfileInfo implements Parcelable {
PROFILE_CLASS_OPERATIONAL,
PROFILE_CLASS_UNSET
})
+ /** @hide */
public @interface ProfileClass {}
/** Testing profiles */
public static final int PROFILE_CLASS_TESTING = 0;
@@ -80,6 +83,7 @@ public final class EuiccProfileInfo implements Parcelable {
PROFILE_STATE_ENABLED,
PROFILE_STATE_UNSET
})
+ /** @hide */
public @interface ProfileState {}
/** Disabled profiles */
public static final int PROFILE_STATE_DISABLED = 0;
@@ -92,34 +96,34 @@ public final class EuiccProfileInfo implements Parcelable {
public static final int PROFILE_STATE_UNSET = -1;
/** The iccid of the subscription. */
- public final String iccid;
+ private final String mIccid;
/** An optional nickname for the subscription. */
- public final @Nullable String nickname;
+ private final @Nullable String mNickname;
/** The service provider name for the subscription. */
- public final String serviceProviderName;
+ private final String mServiceProviderName;
/** The profile name for the subscription. */
- public final String profileName;
+ private final String mProfileName;
/** Profile class for the subscription. */
- @ProfileClass public final int profileClass;
+ @ProfileClass private final int mProfileClass;
/** The profile state of the subscription. */
- @ProfileState public final int state;
+ @ProfileState private final int mState;
/** The operator Id of the subscription. */
- public final CarrierIdentifier carrierIdentifier;
+ private final CarrierIdentifier mCarrierIdentifier;
/** The policy rules of the subscription. */
- @PolicyRule public final int policyRules;
+ @PolicyRule private final int mPolicyRules;
/**
* Optional access rules defining which apps can manage this subscription. If unset, only the
* platform can manage it.
*/
- public final @Nullable UiccAccessRule[] accessRules;
+ private final @Nullable UiccAccessRule[] mAccessRules;
public static final Creator<EuiccProfileInfo> CREATOR = new Creator<EuiccProfileInfo>() {
@Override
@@ -144,51 +148,51 @@ public final class EuiccProfileInfo implements Parcelable {
if (!TextUtils.isDigitsOnly(iccid)) {
throw new IllegalArgumentException("iccid contains invalid characters: " + iccid);
}
- this.iccid = iccid;
- this.accessRules = accessRules;
- this.nickname = nickname;
-
- this.serviceProviderName = null;
- this.profileName = null;
- this.profileClass = PROFILE_CLASS_UNSET;
- this.state = PROFILE_CLASS_UNSET;
- this.carrierIdentifier = null;
- this.policyRules = 0;
+ this.mIccid = iccid;
+ this.mAccessRules = accessRules;
+ this.mNickname = nickname;
+
+ this.mServiceProviderName = null;
+ this.mProfileName = null;
+ this.mProfileClass = PROFILE_CLASS_UNSET;
+ this.mState = PROFILE_STATE_UNSET;
+ this.mCarrierIdentifier = null;
+ this.mPolicyRules = 0;
}
private EuiccProfileInfo(Parcel in) {
- iccid = in.readString();
- nickname = in.readString();
- serviceProviderName = in.readString();
- profileName = in.readString();
- profileClass = in.readInt();
- state = in.readInt();
+ mIccid = in.readString();
+ mNickname = in.readString();
+ mServiceProviderName = in.readString();
+ mProfileName = in.readString();
+ mProfileClass = in.readInt();
+ mState = in.readInt();
byte exist = in.readByte();
if (exist == (byte) 1) {
- carrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in);
+ mCarrierIdentifier = CarrierIdentifier.CREATOR.createFromParcel(in);
} else {
- carrierIdentifier = null;
+ mCarrierIdentifier = null;
}
- policyRules = in.readInt();
- accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
+ mPolicyRules = in.readInt();
+ mAccessRules = in.createTypedArray(UiccAccessRule.CREATOR);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(iccid);
- dest.writeString(nickname);
- dest.writeString(serviceProviderName);
- dest.writeString(profileName);
- dest.writeInt(profileClass);
- dest.writeInt(state);
- if (carrierIdentifier != null) {
+ dest.writeString(mIccid);
+ dest.writeString(mNickname);
+ dest.writeString(mServiceProviderName);
+ dest.writeString(mProfileName);
+ dest.writeInt(mProfileClass);
+ dest.writeInt(mState);
+ if (mCarrierIdentifier != null) {
dest.writeByte((byte) 1);
- carrierIdentifier.writeToParcel(dest, flags);
+ mCarrierIdentifier.writeToParcel(dest, flags);
} else {
dest.writeByte((byte) 0);
}
- dest.writeInt(policyRules);
- dest.writeTypedArray(accessRules, flags);
+ dest.writeInt(mPolicyRules);
+ dest.writeTypedArray(mAccessRules, flags);
}
@Override
@@ -198,45 +202,50 @@ public final class EuiccProfileInfo implements Parcelable {
/** The builder to build a new {@link EuiccProfileInfo} instance. */
public static final class Builder {
- public String iccid;
- public UiccAccessRule[] accessRules;
- public String nickname;
- public String serviceProviderName;
- public String profileName;
- @ProfileClass public int profileClass;
- @ProfileState public int state;
- public CarrierIdentifier carrierIdentifier;
- @PolicyRule public int policyRules;
-
- public Builder() {}
+ private String mIccid;
+ private List<UiccAccessRule> mAccessRules;
+ private String mNickname;
+ private String mServiceProviderName;
+ private String mProfileName;
+ @ProfileClass private int mProfileClass;
+ @ProfileState private int mState;
+ private CarrierIdentifier mCarrierIdentifier;
+ @PolicyRule private int mPolicyRules;
+
+ public Builder(String value) {
+ if (!TextUtils.isDigitsOnly(value)) {
+ throw new IllegalArgumentException("iccid contains invalid characters: " + value);
+ }
+ mIccid = value;
+ }
public Builder(EuiccProfileInfo baseProfile) {
- iccid = baseProfile.iccid;
- nickname = baseProfile.nickname;
- serviceProviderName = baseProfile.serviceProviderName;
- profileName = baseProfile.profileName;
- profileClass = baseProfile.profileClass;
- state = baseProfile.state;
- carrierIdentifier = baseProfile.carrierIdentifier;
- policyRules = baseProfile.policyRules;
- accessRules = baseProfile.accessRules;
+ mIccid = baseProfile.mIccid;
+ mNickname = baseProfile.mNickname;
+ mServiceProviderName = baseProfile.mServiceProviderName;
+ mProfileName = baseProfile.mProfileName;
+ mProfileClass = baseProfile.mProfileClass;
+ mState = baseProfile.mState;
+ mCarrierIdentifier = baseProfile.mCarrierIdentifier;
+ mPolicyRules = baseProfile.mPolicyRules;
+ mAccessRules = Arrays.asList(baseProfile.mAccessRules);
}
/** Builds the profile instance. */
public EuiccProfileInfo build() {
- if (iccid == null) {
+ if (mIccid == null) {
throw new IllegalStateException("ICCID must be set for a profile.");
}
return new EuiccProfileInfo(
- iccid,
- nickname,
- serviceProviderName,
- profileName,
- profileClass,
- state,
- carrierIdentifier,
- policyRules,
- accessRules);
+ mIccid,
+ mNickname,
+ mServiceProviderName,
+ mProfileName,
+ mProfileClass,
+ mState,
+ mCarrierIdentifier,
+ mPolicyRules,
+ mAccessRules);
}
/** Sets the iccId of the subscription. */
@@ -244,55 +253,55 @@ public final class EuiccProfileInfo implements Parcelable {
if (!TextUtils.isDigitsOnly(value)) {
throw new IllegalArgumentException("iccid contains invalid characters: " + value);
}
- iccid = value;
+ mIccid = value;
return this;
}
/** Sets the nickname of the subscription. */
public Builder setNickname(String value) {
- nickname = value;
+ mNickname = value;
return this;
}
/** Sets the service provider name of the subscription. */
public Builder setServiceProviderName(String value) {
- serviceProviderName = value;
+ mServiceProviderName = value;
return this;
}
/** Sets the profile name of the subscription. */
public Builder setProfileName(String value) {
- profileName = value;
+ mProfileName = value;
return this;
}
/** Sets the profile class of the subscription. */
public Builder setProfileClass(@ProfileClass int value) {
- profileClass = value;
+ mProfileClass = value;
return this;
}
/** Sets the state of the subscription. */
public Builder setState(@ProfileState int value) {
- state = value;
+ mState = value;
return this;
}
/** Sets the carrier identifier of the subscription. */
public Builder setCarrierIdentifier(CarrierIdentifier value) {
- carrierIdentifier = value;
+ mCarrierIdentifier = value;
return this;
}
/** Sets the policy rules of the subscription. */
public Builder setPolicyRules(@PolicyRule int value) {
- policyRules = value;
+ mPolicyRules = value;
return this;
}
/** Sets the access rules of the subscription. */
- public Builder setUiccAccessRule(@Nullable UiccAccessRule[] value) {
- accessRules = value;
+ public Builder setUiccAccessRule(@Nullable List<UiccAccessRule> value) {
+ mAccessRules = value;
return this;
}
}
@@ -306,75 +315,81 @@ public final class EuiccProfileInfo implements Parcelable {
@ProfileState int state,
CarrierIdentifier carrierIdentifier,
@PolicyRule int policyRules,
- @Nullable UiccAccessRule[] accessRules) {
- this.iccid = iccid;
- this.nickname = nickname;
- this.serviceProviderName = serviceProviderName;
- this.profileName = profileName;
- this.profileClass = profileClass;
- this.state = state;
- this.carrierIdentifier = carrierIdentifier;
- this.policyRules = policyRules;
- this.accessRules = accessRules;
+ @Nullable List<UiccAccessRule> accessRules) {
+ this.mIccid = iccid;
+ this.mNickname = nickname;
+ this.mServiceProviderName = serviceProviderName;
+ this.mProfileName = profileName;
+ this.mProfileClass = profileClass;
+ this.mState = state;
+ this.mCarrierIdentifier = carrierIdentifier;
+ this.mPolicyRules = policyRules;
+ if (accessRules != null && accessRules.size() > 0) {
+ this.mAccessRules = accessRules.toArray(new UiccAccessRule[accessRules.size()]);
+ } else {
+ this.mAccessRules = null;
+ }
}
/** Gets the ICCID string. */
public String getIccid() {
- return iccid;
+ return mIccid;
}
/** Gets the access rules. */
@Nullable
- public UiccAccessRule[] getUiccAccessRules() {
- return accessRules;
+ public List<UiccAccessRule> getUiccAccessRules() {
+ if (mAccessRules == null) return null;
+ return Arrays.asList(mAccessRules);
}
/** Gets the nickname. */
+ @Nullable
public String getNickname() {
- return nickname;
+ return mNickname;
}
/** Gets the service provider name. */
public String getServiceProviderName() {
- return serviceProviderName;
+ return mServiceProviderName;
}
/** Gets the profile name. */
public String getProfileName() {
- return profileName;
+ return mProfileName;
}
/** Gets the profile class. */
@ProfileClass
public int getProfileClass() {
- return profileClass;
+ return mProfileClass;
}
/** Gets the state of the subscription. */
@ProfileState
public int getState() {
- return state;
+ return mState;
}
/** Gets the carrier identifier. */
public CarrierIdentifier getCarrierIdentifier() {
- return carrierIdentifier;
+ return mCarrierIdentifier;
}
/** Gets the policy rules. */
@PolicyRule
public int getPolicyRules() {
- return policyRules;
+ return mPolicyRules;
}
/** Returns whether any policy rule exists. */
public boolean hasPolicyRules() {
- return policyRules != 0;
+ return mPolicyRules != 0;
}
/** Checks whether a certain policy rule exists. */
public boolean hasPolicyRule(@PolicyRule int policy) {
- return (policyRules & policy) != 0;
+ return (mPolicyRules & policy) != 0;
}
@Override
@@ -387,50 +402,50 @@ public final class EuiccProfileInfo implements Parcelable {
}
EuiccProfileInfo that = (EuiccProfileInfo) obj;
- return Objects.equals(iccid, that.iccid)
- && Objects.equals(nickname, that.nickname)
- && Objects.equals(serviceProviderName, that.serviceProviderName)
- && Objects.equals(profileName, that.profileName)
- && profileClass == that.profileClass
- && state == that.state
- && Objects.equals(carrierIdentifier, that.carrierIdentifier)
- && policyRules == that.policyRules
- && Arrays.equals(accessRules, that.accessRules);
+ return Objects.equals(mIccid, that.mIccid)
+ && Objects.equals(mNickname, that.mNickname)
+ && Objects.equals(mServiceProviderName, that.mServiceProviderName)
+ && Objects.equals(mProfileName, that.mProfileName)
+ && mProfileClass == that.mProfileClass
+ && mState == that.mState
+ && Objects.equals(mCarrierIdentifier, that.mCarrierIdentifier)
+ && mPolicyRules == that.mPolicyRules
+ && Arrays.equals(mAccessRules, that.mAccessRules);
}
@Override
public int hashCode() {
int result = 1;
- result = 31 * result + Objects.hashCode(iccid);
- result = 31 * result + Objects.hashCode(nickname);
- result = 31 * result + Objects.hashCode(serviceProviderName);
- result = 31 * result + Objects.hashCode(profileName);
- result = 31 * result + profileClass;
- result = 31 * result + state;
- result = 31 * result + Objects.hashCode(carrierIdentifier);
- result = 31 * result + policyRules;
- result = 31 * result + Arrays.hashCode(accessRules);
+ result = 31 * result + Objects.hashCode(mIccid);
+ result = 31 * result + Objects.hashCode(mNickname);
+ result = 31 * result + Objects.hashCode(mServiceProviderName);
+ result = 31 * result + Objects.hashCode(mProfileName);
+ result = 31 * result + mProfileClass;
+ result = 31 * result + mState;
+ result = 31 * result + Objects.hashCode(mCarrierIdentifier);
+ result = 31 * result + mPolicyRules;
+ result = 31 * result + Arrays.hashCode(mAccessRules);
return result;
}
@Override
public String toString() {
return "EuiccProfileInfo (nickname="
- + nickname
+ + mNickname
+ ", serviceProviderName="
- + serviceProviderName
+ + mServiceProviderName
+ ", profileName="
- + profileName
+ + mProfileName
+ ", profileClass="
- + profileClass
+ + mProfileClass
+ ", state="
- + state
+ + mState
+ ", CarrierIdentifier="
- + carrierIdentifier.toString()
+ + mCarrierIdentifier.toString()
+ ", policyRules="
- + policyRules
+ + mPolicyRules
+ ", accessRules="
- + Arrays.toString(accessRules)
+ + Arrays.toString(mAccessRules)
+ ")";
}
}
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index be8580074f73..b87faef5bb44 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -17,6 +17,7 @@ package android.service.euicc;
import android.annotation.CallSuper;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
@@ -41,8 +42,11 @@ import java.util.concurrent.atomic.AtomicInteger;
* <p>To implement the LPA backend, you must extend this class and declare this service in your
* manifest file. The service must require the
* {@link android.Manifest.permission#BIND_EUICC_SERVICE} permission and include an intent filter
- * with the {@link #EUICC_SERVICE_INTERFACE} action. The priority of the intent filter must be set
- * to a non-zero value in case multiple implementations are present on the device. For example:
+ * with the {@link #EUICC_SERVICE_INTERFACE} action. It's suggested that the priority of the intent
+ * filter to be set to a non-zero value in case multiple implementations are present on the device.
+ * See the below example. Note that there will be problem if two LPAs are present and they have the
+ * same priority.
+ * Example:
*
* <pre>{@code
* <service android:name=".MyEuiccService"
@@ -65,9 +69,9 @@ import java.util.concurrent.atomic.AtomicInteger;
* filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero
* priority.
*
- * TODO(b/35851809): Make this a SystemApi.
* @hide
*/
+@SystemApi
public abstract class EuiccService extends Service {
/** Action which must be included in this service's intent filter. */
public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
@@ -77,7 +81,10 @@ public abstract class EuiccService extends Service {
// LUI actions. These are passthroughs of the corresponding EuiccManager actions.
- /** @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS */
+ /**
+ * @see android.telephony.euicc.EuiccManager#ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS
+ * The difference is this one is used by system to bring up the LUI.
+ */
public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
"android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
/** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */
@@ -88,7 +95,10 @@ public abstract class EuiccService extends Service {
// require user interaction.
// TODO(b/33075886): Define extras for any input parameters to these dialogs once they are
// more scoped out.
- /** Alert the user that this action will result in an active SIM being deactivated. */
+ /**
+ * Alert the user that this action will result in an active SIM being deactivated.
+ * To implement the LUI triggered by the system, you need to define this in AndroidManifest.xml.
+ */
public static final String ACTION_RESOLVE_DEACTIVATE_SIM =
"android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
/**
@@ -102,7 +112,11 @@ public abstract class EuiccService extends Service {
public static final String ACTION_RESOLVE_CONFIRMATION_CODE =
"android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
- /** Intent extra set for resolution requests containing the package name of the calling app. */
+ /**
+ * Intent extra set for resolution requests containing the package name of the calling app.
+ * This is used by the above actions including ACTION_RESOLVE_DEACTIVATE_SIM,
+ * ACTION_RESOLVE_NO_PRIVILEGES and ACTION_RESOLVE_CONFIRMATION_CODE.
+ */
public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
"android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
@@ -136,10 +150,18 @@ public abstract class EuiccService extends Service {
RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE);
}
- /** Boolean extra for resolution actions indicating whether the user granted consent. */
- public static final String RESOLUTION_EXTRA_CONSENT = "consent";
- /** String extra for resolution actions indicating the carrier confirmation code. */
- public static final String RESOLUTION_EXTRA_CONFIRMATION_CODE = "confirmation_code";
+ /**
+ * Boolean extra for resolution actions indicating whether the user granted consent.
+ * This is used and set by the implementation and used in {@code EuiccOperation}.
+ */
+ public static final String EXTRA_RESOLUTION_CONSENT =
+ "android.service.euicc.extra.RESOLUTION_CONSENT";
+ /**
+ * String extra for resolution actions indicating the carrier confirmation code.
+ * This is used and set by the implementation and used in {@code EuiccOperation}.
+ */
+ public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE =
+ "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE";
private final IEuiccService.Stub mStubWrapper;
@@ -199,9 +221,9 @@ public abstract class EuiccService extends Service {
*
* @see IEuiccService#startOtaIfNecessary
*/
- public interface OtaStatusChangedCallback {
+ public abstract static class OtaStatusChangedCallback {
/** Called when OTA status is changed. */
- void onOtaStatusChanged(int status);
+ public abstract void onOtaStatusChanged(int status);
}
/**
@@ -238,8 +260,7 @@ public abstract class EuiccService extends Service {
/**
* Populate {@link DownloadableSubscription} metadata for the given downloadable subscription.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param subscription A subscription whose metadata needs to be populated.
* @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
* eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM)}
@@ -267,8 +288,7 @@ public abstract class EuiccService extends Service {
/**
* Download the given subscription.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param subscription The subscription to download.
* @param switchAfterDownload If true, the subscription should be enabled upon successful
* download.
@@ -286,8 +306,7 @@ public abstract class EuiccService extends Service {
/**
* Return a list of all @link EuiccProfileInfo}s.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @return The result of the operation.
* @see android.telephony.SubscriptionManager#getAvailableSubscriptionInfoList
* @see android.telephony.SubscriptionManager#getAccessibleSubscriptionInfoList
@@ -297,8 +316,7 @@ public abstract class EuiccService extends Service {
/**
* Return info about the eUICC chip/device.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @return the {@link EuiccInfo} for the eUICC chip/device.
* @see android.telephony.euicc.EuiccManager#getEuiccInfo
*/
@@ -310,8 +328,7 @@ public abstract class EuiccService extends Service {
* <p>If the subscription is currently active, it should be deactivated first (equivalent to a
* physical SIM being ejected).
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param iccid the ICCID of the subscription to delete.
* @return the result of the delete operation. May be one of the predefined {@code RESULT_}
* constants or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
@@ -322,8 +339,7 @@ public abstract class EuiccService extends Service {
/**
* Switch to the given subscription.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param iccid the ICCID of the subscription to enable. May be null, in which case the current
* profile should be deactivated and no profile should be activated to replace it - this is
* equivalent to a physical SIM being ejected.
@@ -340,8 +356,7 @@ public abstract class EuiccService extends Service {
/**
* Update the nickname of the given subscription.
*
- * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
- * but is here to future-proof the APIs.
+ * @param slotId ID of the SIM slot to use for the operation.
* @param iccid the ICCID of the subscription to update.
* @param nickname the new nickname to apply.
* @return the result of the update operation. May be one of the predefined {@code RESULT_}
diff --git a/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java b/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
index 5a24492007f3..e2171ae6bc76 100644
--- a/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
+++ b/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
@@ -16,16 +16,19 @@
package android.service.euicc;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.euicc.DownloadableSubscription;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Result of a {@link EuiccService#onGetDefaultDownloadableSubscriptionList} operation.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+@SystemApi
public final class GetDefaultDownloadableSubscriptionListResult implements Parcelable {
public static final Creator<GetDefaultDownloadableSubscriptionListResult> CREATOR =
@@ -42,20 +45,35 @@ public final class GetDefaultDownloadableSubscriptionListResult implements Parce
};
/**
- * Result of the operation.
+ * @hide
+ * @deprecated - Do no use. Use getResult() instead.
+ */
+ @Deprecated
+ public final int result;
+
+ @Nullable
+ private final DownloadableSubscription[] mSubscriptions;
+
+ /**
+ * Gets the result of the operation.
*
* <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any
* implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}.
*/
- public final int result;
+ public int getResult() {
+ return result;
+ }
/**
- * The available {@link DownloadableSubscription}s (with filled-in metadata).
+ * Gets the available {@link DownloadableSubscription}s (with filled-in metadata).
*
* <p>Only non-null if {@link #result} is {@link EuiccService#RESULT_OK}.
*/
@Nullable
- public final DownloadableSubscription[] subscriptions;
+ public List<DownloadableSubscription> getDownloadableSubscriptions() {
+ if (mSubscriptions == null) return null;
+ return Arrays.asList(mSubscriptions);
+ }
/**
* Construct a new {@link GetDefaultDownloadableSubscriptionListResult}.
@@ -70,25 +88,25 @@ public final class GetDefaultDownloadableSubscriptionListResult implements Parce
@Nullable DownloadableSubscription[] subscriptions) {
this.result = result;
if (this.result == EuiccService.RESULT_OK) {
- this.subscriptions = subscriptions;
+ this.mSubscriptions = subscriptions;
} else {
if (subscriptions != null) {
throw new IllegalArgumentException(
"Error result with non-null subscriptions: " + result);
}
- this.subscriptions = null;
+ this.mSubscriptions = null;
}
}
private GetDefaultDownloadableSubscriptionListResult(Parcel in) {
this.result = in.readInt();
- this.subscriptions = in.createTypedArray(DownloadableSubscription.CREATOR);
+ this.mSubscriptions = in.createTypedArray(DownloadableSubscription.CREATOR);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
- dest.writeTypedArray(subscriptions, flags);
+ dest.writeTypedArray(mSubscriptions, flags);
}
@Override
diff --git a/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java b/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
index de8a30706945..1edb5398add4 100644
--- a/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
+++ b/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
@@ -16,6 +16,7 @@
package android.service.euicc;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.euicc.DownloadableSubscription;
@@ -23,9 +24,8 @@ import android.telephony.euicc.DownloadableSubscription;
/**
* Result of a {@link EuiccService#onGetDownloadableSubscriptionMetadata} operation.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+@SystemApi
public final class GetDownloadableSubscriptionMetadataResult implements Parcelable {
public static final Creator<GetDownloadableSubscriptionMetadataResult> CREATOR =
@@ -42,20 +42,34 @@ public final class GetDownloadableSubscriptionMetadataResult implements Parcelab
};
/**
- * Result of the operation.
+ * @hide
+ * @deprecated - Do no use. Use getResult() instead.
+ */
+ @Deprecated
+ public final int result;
+
+ @Nullable
+ private final DownloadableSubscription mSubscription;
+
+ /**
+ * Gets the result of the operation.
*
* <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any
* implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}.
*/
- public final int result;
+ public int getResult() {
+ return result;
+ }
/**
- * The {@link DownloadableSubscription} with filled-in metadata.
+ * Gets the {@link DownloadableSubscription} with filled-in metadata.
*
* <p>Only non-null if {@link #result} is {@link EuiccService#RESULT_OK}.
*/
@Nullable
- public final DownloadableSubscription subscription;
+ public DownloadableSubscription getDownloadableSubscription() {
+ return mSubscription;
+ }
/**
* Construct a new {@link GetDownloadableSubscriptionMetadataResult}.
@@ -70,25 +84,25 @@ public final class GetDownloadableSubscriptionMetadataResult implements Parcelab
@Nullable DownloadableSubscription subscription) {
this.result = result;
if (this.result == EuiccService.RESULT_OK) {
- this.subscription = subscription;
+ this.mSubscription = subscription;
} else {
if (subscription != null) {
throw new IllegalArgumentException(
"Error result with non-null subscription: " + result);
}
- this.subscription = null;
+ this.mSubscription = null;
}
}
private GetDownloadableSubscriptionMetadataResult(Parcel in) {
this.result = in.readInt();
- this.subscription = in.readTypedObject(DownloadableSubscription.CREATOR);
+ this.mSubscription = in.readTypedObject(DownloadableSubscription.CREATOR);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
- dest.writeTypedObject(this.subscription, flags);
+ dest.writeTypedObject(this.mSubscription, flags);
}
@Override
diff --git a/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java b/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java
index 7ad84888dc82..464d136e70e5 100644
--- a/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java
+++ b/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java
@@ -16,15 +16,18 @@
package android.service.euicc;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+import java.util.List;
+
/**
* Result of a {@link EuiccService#onGetEuiccProfileInfoList} operation.
* @hide
- *
- * TODO(b/35851809): Make this a SystemApi.
*/
+@SystemApi
public final class GetEuiccProfileInfoListResult implements Parcelable {
public static final Creator<GetEuiccProfileInfoListResult> CREATOR =
@@ -41,19 +44,38 @@ public final class GetEuiccProfileInfoListResult implements Parcelable {
};
/**
- * Result of the operation.
+ * @hide
+ * @deprecated - Do no use. Use getResult() instead.
+ */
+ @Deprecated
+ public final int result;
+
+ @Nullable
+ private final EuiccProfileInfo[] mProfiles;
+
+ private final boolean mIsRemovable;
+
+ /**
+ * Gets the result of the operation.
*
* <p>May be one of the predefined {@code RESULT_} constants in EuiccService or any
* implementation-specific code starting with {@link EuiccService#RESULT_FIRST_USER}.
*/
- public final int result;
+ public int getResult() {
+ return result;
+ }
- /** The profile list (only upon success). */
+ /** Gets the profile list (only upon success). */
@Nullable
- public final EuiccProfileInfo[] profiles;
+ public List<EuiccProfileInfo> getProfiles() {
+ if (mProfiles == null) return null;
+ return Arrays.asList(mProfiles);
+ }
- /** Whether the eUICC is removable. */
- public final boolean isRemovable;
+ /** Gets whether the eUICC is removable. */
+ public boolean getIsRemovable() {
+ return mIsRemovable;
+ }
/**
* Construct a new {@link GetEuiccProfileInfoListResult}.
@@ -71,30 +93,29 @@ public final class GetEuiccProfileInfoListResult implements Parcelable {
public GetEuiccProfileInfoListResult(
int result, @Nullable EuiccProfileInfo[] profiles, boolean isRemovable) {
this.result = result;
- this.isRemovable = isRemovable;
+ this.mIsRemovable = isRemovable;
if (this.result == EuiccService.RESULT_OK) {
- this.profiles = profiles;
+ this.mProfiles = profiles;
} else {
if (profiles != null) {
throw new IllegalArgumentException(
"Error result with non-null profiles: " + result);
}
- this.profiles = null;
+ this.mProfiles = null;
}
-
}
private GetEuiccProfileInfoListResult(Parcel in) {
this.result = in.readInt();
- this.profiles = in.createTypedArray(EuiccProfileInfo.CREATOR);
- this.isRemovable = in.readBoolean();
+ this.mProfiles = in.createTypedArray(EuiccProfileInfo.CREATOR);
+ this.mIsRemovable = in.readBoolean();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(result);
- dest.writeTypedArray(profiles, flags);
- dest.writeBoolean(isRemovable);
+ dest.writeTypedArray(mProfiles, flags);
+ dest.writeBoolean(mIsRemovable);
}
@Override
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index b7b2b2de35e5..422e36baee70 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1343,6 +1343,7 @@ public abstract class NotificationListenerService extends Service {
/**
* @hide
*/
+ @GuardedBy("mLock")
public final void applyUpdateLocked(NotificationRankingUpdate update) {
mRankingMap = new RankingMap(update);
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index d66322c3aa89..171d4d938beb 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -94,7 +94,7 @@ public class ZenModeConfig implements Parcelable {
private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true;
private static final boolean DEFAULT_ALLOW_SCREEN_ON = true;
- private static final int XML_VERSION = 2;
+ public static final int XML_VERSION = 3;
public static final String ZEN_TAG = "zen";
private static final String ZEN_ATT_VERSION = "version";
private static final String ZEN_ATT_USER = "user";
@@ -145,6 +145,7 @@ public class ZenModeConfig implements Parcelable {
public int user = UserHandle.USER_SYSTEM;
public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
+ public int version;
public ZenRule manualRule;
public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
@@ -431,6 +432,7 @@ public class ZenModeConfig implements Parcelable {
String tag = parser.getName();
if (!ZEN_TAG.equals(tag)) return null;
final ZenModeConfig rt = new ZenModeConfig();
+ rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
tag = parser.getName();
diff --git a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
index 704a1daf3ec7..2ed618b39f1f 100644
--- a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
@@ -15,17 +15,17 @@
*/
package android.speech.tts;
+import android.media.AudioTrack;
import android.speech.tts.TextToSpeechService.AudioOutputParams;
import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
-import android.media.AudioTrack;
import android.util.Log;
import java.util.LinkedList;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Manages the playback of a list of byte arrays representing audio data that are queued by the
@@ -157,9 +157,16 @@ final class SynthesisPlaybackQueueItem extends PlaybackQueueItem
mStopped = true;
mStatusCode = statusCode;
+ // Wake up the synthesis thread if it was waiting on put(). Its
+ // buffers will no longer be copied since mStopped is true. The
+ // PlaybackSynthesisCallback that this synthesis corresponds to
+ // would also have been stopped, and so all calls to
+ // Callback.onDataAvailable( ) will return errors too.
+ mNotFull.signal();
+
if (mRunState.getAndSet(STOP_CALLED) == NOT_RUN) {
- // Dispatch the status code and just finish without signaling
- // if run() has not even started.
+ // Dispatch the status code and just finish. Signaling audio
+ // playback is not necessary because run() hasn't started.
dispatchEndStatus();
return;
}
@@ -168,13 +175,6 @@ final class SynthesisPlaybackQueueItem extends PlaybackQueueItem
// take() will return null since mStopped was true, and will then
// break out of the data write loop.
mReadReady.signal();
-
- // Wake up the synthesis thread if it was waiting on put(). Its
- // buffers will no longer be copied since mStopped is true. The
- // PlaybackSynthesisCallback that this synthesis corresponds to
- // would also have been stopped, and so all calls to
- // Callback.onDataAvailable( ) will return errors too.
- mNotFull.signal();
} finally {
mListLock.unlock();
}
diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS
index 0f85e1f9c5d9..e56137119c28 100644
--- a/core/java/android/text/OWNERS
+++ b/core/java/android/text/OWNERS
@@ -1,4 +1,5 @@
+set noparent
+
siyamed@google.com
nona@google.com
clarabayarri@google.com
-toki@google.com
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index 162281250208..908de2988be9 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -17,6 +17,8 @@
package android.text.style;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.LeakyTypefaceStorage;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Parcel;
@@ -25,33 +27,69 @@ import android.text.TextPaint;
import android.text.TextUtils;
/**
- * Changes the typeface family of the text to which the span is attached. Examples of typeface
- * family include "monospace", "serif", and "sans-serif".
+ * Span that updates the typeface of the text it's attached to. The <code>TypefaceSpan</code> can
+ * be constructed either based on a font family or based on a <code>Typeface</code>. When
+ * {@link #TypefaceSpan(String)} is used, the previous style of the <code>TextView</code> is kept.
+ * When {@link #TypefaceSpan(Typeface)} is used, the <code>Typeface</code> style replaces the
+ * <code>TextView</code>'s style.
* <p>
- * For example, change the typeface of a text to "monospace" like this:
+ * For example, let's consider a <code>TextView</code> with
+ * <code>android:textStyle="italic"</code> and a typeface created based on a font from resources,
+ * with a bold style. When applying a <code>TypefaceSpan</code> based the typeface, the text will
+ * only keep the bold style, overriding the <code>TextView</code>'s textStyle. When applying a
+ * <code>TypefaceSpan</code> based on a font family: "monospace", the resulted text will keep the
+ * italic style.
* <pre>
- * SpannableString string = new SpannableString("Text with typeface span");
- * string.setSpan(new TypefaceSpan("monospace"), 10, 18, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * Typeface myTypeface = Typeface.create(ResourcesCompat.getFont(context, R.font.acme),
+ * Typeface.BOLD);
+ * SpannableString string = new SpannableString("Text with typeface span.");
+ * string.setSpan(new TypefaceSpan(myTypeface), 10, 18, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ * string.setSpan(new TypefaceSpan("monospace"), 19, 22, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
* </pre>
* <img src="{@docRoot}reference/android/images/text/style/typefacespan.png" />
- * <figcaption>Text with "monospace" typeface family.</figcaption>
+ * <figcaption>Text with <code>TypefaceSpan</code>s constructed based on a font from resource and
+ * from a font family.</figcaption>
*/
public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan {
+ @Nullable
private final String mFamily;
+ @Nullable
+ private final Typeface mTypeface;
+
/**
- * Constructs a {@link TypefaceSpan} based on a font family.
+ * Constructs a {@link TypefaceSpan} based on the font family. The previous style of the
+ * TextPaint is kept. If the font family is null, the text paint is not modified.
*
- * @param family The font family for this typeface. Examples include
- * "monospace", "serif", and "sans-serif".
+ * @param family The font family for this typeface. Examples include
+ * "monospace", "serif", and "sans-serif"
*/
- public TypefaceSpan(String family) {
- mFamily = family;
+ public TypefaceSpan(@Nullable String family) {
+ this(family, null);
}
+ /**
+ * Constructs a {@link TypefaceSpan} from a {@link Typeface}. The previous style of the
+ * TextPaint is overridden and the style of the typeface is used.
+ *
+ * @param typeface the typeface
+ */
+ public TypefaceSpan(@NonNull Typeface typeface) {
+ this(null, typeface);
+ }
+
+ /**
+ * Constructs a {@link TypefaceSpan} from a parcel.
+ */
public TypefaceSpan(@NonNull Parcel src) {
mFamily = src.readString();
+ mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src);
+ }
+
+ private TypefaceSpan(@Nullable String family, @Nullable Typeface typeface) {
+ mFamily = family;
+ mTypeface = typeface;
}
@Override
@@ -79,37 +117,59 @@ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan
@Override
public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
dest.writeString(mFamily);
+ LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest);
}
/**
- * Returns the font family name.
+ * Returns the font family name set in the span.
+ *
+ * @return the font family name
+ * @see #TypefaceSpan(String)
*/
+ @Nullable
public String getFamily() {
return mFamily;
}
+ /**
+ * Returns the typeface set in the span.
+ *
+ * @return the typeface set
+ * @see #TypefaceSpan(Typeface)
+ */
+ @Nullable
+ public Typeface getTypeface() {
+ return mTypeface;
+ }
+
@Override
- public void updateDrawState(@NonNull TextPaint textPaint) {
- apply(textPaint, mFamily);
+ public void updateDrawState(@NonNull TextPaint ds) {
+ updateTypeface(ds);
}
@Override
- public void updateMeasureState(@NonNull TextPaint textPaint) {
- apply(textPaint, mFamily);
+ public void updateMeasureState(@NonNull TextPaint paint) {
+ updateTypeface(paint);
}
- private static void apply(@NonNull Paint paint, String family) {
- int oldStyle;
+ private void updateTypeface(@NonNull Paint paint) {
+ if (mTypeface != null) {
+ paint.setTypeface(mTypeface);
+ } else if (mFamily != null) {
+ applyFontFamily(paint, mFamily);
+ }
+ }
+ private void applyFontFamily(@NonNull Paint paint, @NonNull String family) {
+ int style;
Typeface old = paint.getTypeface();
if (old == null) {
- oldStyle = 0;
+ style = Typeface.NORMAL;
} else {
- oldStyle = old.getStyle();
+ style = old.getStyle();
}
-
- Typeface tf = Typeface.create(family, oldStyle);
- int fake = oldStyle & ~tf.getStyle();
+ final Typeface styledTypeface = Typeface.create(family, style);
+ int fake = style & ~styledTypeface.getStyle();
if ((fake & Typeface.BOLD) != 0) {
paint.setFakeBoldText(true);
@@ -118,7 +178,6 @@ public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan
if ((fake & Typeface.ITALIC) != 0) {
paint.setTextSkewX(-0.25f);
}
-
- paint.setTypeface(tf);
+ paint.setTypeface(styledTypeface);
}
}
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index d973d4ac076c..3a22db2bfb22 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -644,7 +644,13 @@ public class Linkify {
@Nullable Runnable modifyTextView) {
Preconditions.checkNotNull(text);
Preconditions.checkNotNull(classifier);
- final Supplier<TextLinks> supplier = () -> classifier.generateLinks(text, options);
+
+ // The input text may exceed the maximum length the text classifier can handle. In such
+ // cases, we process the text up to the maximum length.
+ final CharSequence truncatedText = text.subSequence(
+ 0, Math.min(text.length(), classifier.getMaxGenerateLinksTextLength()));
+
+ final Supplier<TextLinks> supplier = () -> classifier.generateLinks(truncatedText, options);
final Consumer<TextLinks> consumer = links -> {
if (links.getLinks().isEmpty()) {
if (callback != null) {
@@ -653,7 +659,8 @@ public class Linkify {
return;
}
- final TextLinkSpan[] old = text.getSpans(0, text.length(), TextLinkSpan.class);
+ // Remove spans only for the part of the text we generated links for.
+ final TextLinkSpan[] old = text.getSpans(0, truncatedText.length(), TextLinkSpan.class);
for (int i = old.length - 1; i >= 0; i--) {
text.removeSpan(old[i]);
}
@@ -662,7 +669,8 @@ public class Linkify {
? null : options.getSpanFactory();
final @TextLinks.ApplyStrategy int applyStrategy = (options == null)
? TextLinks.APPLY_STRATEGY_IGNORE : options.getApplyStrategy();
- final @TextLinks.Status int result = links.apply(text, applyStrategy, spanFactory);
+ final @TextLinks.Status int result = links.apply(text, applyStrategy, spanFactory,
+ true /*allowPrefix*/);
if (result == TextLinks.STATUS_LINKS_APPLIED) {
if (modifyTextView != null) {
modifyTextView.run();
diff --git a/core/java/android/transition/ArcMotion.java b/core/java/android/transition/ArcMotion.java
index da1483499220..172c8376c8cc 100644
--- a/core/java/android/transition/ArcMotion.java
+++ b/core/java/android/transition/ArcMotion.java
@@ -216,7 +216,13 @@ public class ArcMotion extends PathMotion {
boolean isMovingUpwards = startY > endY;
- if ((Math.abs(deltaX) < Math.abs(deltaY))) {
+ if (deltaY == 0) {
+ ex = dx;
+ ey = dy + (Math.abs(deltaX) * 0.5f * mMinimumHorizontalTangent);
+ } else if (deltaX == 0) {
+ ex = dx + (Math.abs(deltaY) * 0.5f * mMinimumVerticalTangent);
+ ey = dy;
+ } else if ((Math.abs(deltaX) < Math.abs(deltaY))) {
// Similar triangles bfa and bde mean that (ab/fb = eb/bd)
// Therefore, eb = ab * bd / fb
// ab = hypotenuse
@@ -254,7 +260,7 @@ public class ArcMotion extends PathMotion {
float maximumArcDist2 = midDist2 * mMaximumTangent * mMaximumTangent;
float newArcDistance2 = 0;
- if (arcDist2 < minimumArcDist2) {
+ if (arcDist2 != 0 && arcDist2 < minimumArcDist2) {
newArcDistance2 = minimumArcDist2;
} else if (arcDist2 > maximumArcDist2) {
newArcDistance2 = maximumArcDist2;
diff --git a/core/java/android/transition/TransitionUtils.java b/core/java/android/transition/TransitionUtils.java
index 60b77bc8d426..46c9e0c7202f 100644
--- a/core/java/android/transition/TransitionUtils.java
+++ b/core/java/android/transition/TransitionUtils.java
@@ -20,14 +20,13 @@ import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.TypeEvaluator;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.view.DisplayListCanvas;
-import android.view.RenderNode;
-import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
@@ -128,10 +127,8 @@ public class TransitionUtils {
}
int bitmapWidth = (int) (width * scale);
int bitmapHeight = (int) (height * scale);
- final RenderNode node = RenderNode.create("TransitionUtils", hostView);
- node.setLeftTopRightBottom(0, 0, width, height);
- node.setClipToBounds(false);
- final DisplayListCanvas canvas = node.start(width, height);
+ final Picture picture = new Picture();
+ final Canvas canvas = picture.beginRecording(width, height);
// Do stuff with the canvas
Rect existingBounds = drawable.getBounds();
int left = existingBounds.left;
@@ -141,8 +138,8 @@ public class TransitionUtils {
drawable.setBounds(0, 0, bitmapWidth, bitmapHeight);
drawable.draw(canvas);
drawable.setBounds(left, top, right, bottom);
- node.end(canvas);
- return ThreadedRenderer.createHardwareBitmap(node, width, height);
+ picture.endRecording();
+ return Bitmap.createBitmap(picture);
}
/**
@@ -183,14 +180,12 @@ public class TransitionUtils {
matrix.postTranslate(-bounds.left, -bounds.top);
matrix.postScale(scale, scale);
- final RenderNode node = RenderNode.create("TransitionUtils", view);
- node.setLeftTopRightBottom(0, 0, bitmapWidth, bitmapHeight);
- node.setClipToBounds(false);
- final DisplayListCanvas canvas = node.start(bitmapWidth, bitmapHeight);
+ final Picture picture = new Picture();
+ final Canvas canvas = picture.beginRecording(bitmapWidth, bitmapHeight);
canvas.concat(matrix);
view.draw(canvas);
- node.end(canvas);
- bitmap = ThreadedRenderer.createHardwareBitmap(node, bitmapWidth, bitmapHeight);
+ picture.endRecording();
+ bitmap = Bitmap.createBitmap(picture);
}
if (addToOverlay) {
sceneRoot.getOverlay().remove(view);
diff --git a/core/java/android/util/AttributeSet.java b/core/java/android/util/AttributeSet.java
index eb8c1682fb7c..7f327c7b65ed 100644
--- a/core/java/android/util/AttributeSet.java
+++ b/core/java/android/util/AttributeSet.java
@@ -17,6 +17,8 @@
package android.util;
+import org.xmlpull.v1.XmlPullParser;
+
/**
* A collection of attributes, as found associated with a tag in an XML
* document. Often you will not want to use this interface directly, instead
@@ -54,18 +56,42 @@ package android.util;
* compiled XML resource that is not available in a normal XML file, such
* as {@link #getAttributeNameResource(int)} which returns the resource
* identifier associated with a particular XML attribute name.
+ *
+ * @see XmlPullParser
*/
public interface AttributeSet {
/**
* Returns the number of attributes available in the set.
- *
+ *
+ * <p>See also {@link XmlPullParser#getAttributeCount XmlPullParser.getAttributeCount()},
+ * which this method corresponds to when parsing a compiled XML file.</p>
+ *
* @return A positive integer, or 0 if the set is empty.
*/
public int getAttributeCount();
/**
+ * Returns the namespace of the specified attribute.
+ *
+ * <p>See also {@link XmlPullParser#getAttributeNamespace XmlPullParser.getAttributeNamespace()},
+ * which this method corresponds to when parsing a compiled XML file.</p>
+ *
+ * @param index Index of the desired attribute, 0...count-1.
+ *
+ * @return A String containing the namespace of the attribute, or null if th
+ * attribute cannot be found.
+ */
+ default String getAttributeNamespace (int index) {
+ // This is a new method since the first interface definition, so add stub impl.
+ return null;
+ }
+
+ /**
* Returns the name of the specified attribute.
- *
+ *
+ * <p>See also {@link XmlPullParser#getAttributeName XmlPullParser.getAttributeName()},
+ * which this method corresponds to when parsing a compiled XML file.</p>
+ *
* @param index Index of the desired attribute, 0...count-1.
*
* @return A String containing the name of the attribute, or null if the
diff --git a/core/java/android/util/ByteStringUtils.java b/core/java/android/util/ByteStringUtils.java
index 333208db5f79..f6460ad90f6b 100644
--- a/core/java/android/util/ByteStringUtils.java
+++ b/core/java/android/util/ByteStringUtils.java
@@ -22,61 +22,63 @@ package android.util;
* @hide
*/
public final class ByteStringUtils {
- private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+ private static final char[] HEX_LOWERCASE_ARRAY = "0123456789abcdef".toCharArray();
+ private static final char[] HEX_UPPERCASE_ARRAY = "0123456789ABCDEF".toCharArray();
- private ByteStringUtils() {
+ private ByteStringUtils() {
/* hide constructor */
- }
-
- /**
- * Returns the hex encoded string representation of bytes.
- * @param bytes Byte array to encode.
- * @return Hex encoded string representation of bytes.
- */
- public static String toHexString(byte[] bytes) {
- if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) {
- return null;
}
- final int byteLength = bytes.length;
- final int charCount = 2 * byteLength;
- final char[] chars = new char[charCount];
+ /**
+ * Returns the hex encoded string representation of bytes.
+ * @param bytes Byte array to encode.
+ * @return Hex encoded string representation of bytes.
+ */
+ public static String toHexString(byte[] bytes) {
+ if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) {
+ return null;
+ }
- for (int i = 0; i < byteLength; i++) {
- final int byteHex = bytes[i] & 0xFF;
- chars[i * 2] = HEX_ARRAY[byteHex >>> 4];
- chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F];
- }
- return new String(chars);
- }
+ final int byteLength = bytes.length;
+ final int charCount = 2 * byteLength;
+ final char[] chars = new char[charCount];
- /**
- * Returns the decoded byte array representation of str.
- * @param str Hex encoded string to decode.
- * @return Decoded byte array representation of str.
- */
- public static byte[] fromHexToByteArray(String str) {
- if (str == null || str.length() == 0 || str.length() % 2 != 0) {
- return null;
+ for (int i = 0; i < byteLength; i++) {
+ final int byteHex = bytes[i] & 0xFF;
+ chars[i * 2] = HEX_UPPERCASE_ARRAY[byteHex >>> 4];
+ chars[i * 2 + 1] = HEX_UPPERCASE_ARRAY[byteHex & 0x0F];
+ }
+ return new String(chars);
}
- final char[] chars = str.toCharArray();
- final int charLength = chars.length;
- final byte[] bytes = new byte[charLength / 2];
+ /**
+ * Returns the decoded byte array representation of str.
+ * @param str Hex encoded string to decode.
+ * @return Decoded byte array representation of str.
+ */
+ public static byte[] fromHexToByteArray(String str) {
+ if (str == null || str.length() == 0 || str.length() % 2 != 0) {
+ return null;
+ }
+
+ final char[] chars = str.toCharArray();
+ final int charLength = chars.length;
+ final byte[] bytes = new byte[charLength / 2];
- for (int i = 0; i < bytes.length; i++) {
- bytes[i] =
- (byte)(((getIndex(chars[i * 2]) << 4) & 0xF0) | (getIndex(chars[i * 2 + 1]) & 0x0F));
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] =
+ (byte) (((getIndex(chars[i * 2]) << 4) & 0xF0)
+ | (getIndex(chars[i * 2 + 1]) & 0x0F));
+ }
+ return bytes;
}
- return bytes;
- }
- private static int getIndex(char c) {
- for (int i = 0; i < HEX_ARRAY.length; i++) {
- if (HEX_ARRAY[i] == c) {
- return i;
- }
+ private static int getIndex(char c) {
+ for (int i = 0; i < HEX_UPPERCASE_ARRAY.length; i++) {
+ if (HEX_UPPERCASE_ARRAY[i] == c || HEX_LOWERCASE_ARRAY[i] == c) {
+ return i;
+ }
+ }
+ return -1;
}
- return -1;
- }
}
diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
index da7387fcae70..1a397b39ef3c 100644
--- a/core/java/android/util/ExceptionUtils.java
+++ b/core/java/android/util/ExceptionUtils.java
@@ -86,4 +86,16 @@ public class ExceptionUtils {
while (t.getCause() != null) t = t.getCause();
return t;
}
-}
+
+ /**
+ * Appends {@code cause} at the end of the causal chain of {@code t}
+ *
+ * @return {@code t} for convenience
+ */
+ public static @NonNull Throwable appendCause(@NonNull Throwable t, @Nullable Throwable cause) {
+ if (cause != null) {
+ getRootCause(t).initCause(cause);
+ }
+ return t;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 410cdc6a9bf1..1ead0b49bbed 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -37,13 +37,13 @@ public class FeatureFlagUtils {
private static final Map<String, String> DEFAULT_FLAGS;
static {
DEFAULT_FLAGS = new HashMap<>();
- DEFAULT_FLAGS.put("device_info_v2", "true");
DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
DEFAULT_FLAGS.put("settings_battery_v2", "true");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
- DEFAULT_FLAGS.put("settings_about_phone_v2", "false");
+ DEFAULT_FLAGS.put("settings_about_phone_v2", "true");
DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
+ DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
}
/**
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 597089235e6b..bf335196edef 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -16,18 +16,12 @@
package android.util;
-import static android.os.Process.FIRST_APPLICATION_UID;
-
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import android.os.Process;
-
-import com.android.internal.annotations.GuardedBy;
-
-import dalvik.system.CloseGuard;
import libcore.io.IoUtils;
+import dalvik.system.CloseGuard;
import java.io.Closeable;
import java.io.IOException;
@@ -55,18 +49,13 @@ import java.util.UUID;
*/
public final class MemoryIntArray implements Parcelable, Closeable {
private static final String TAG = "MemoryIntArray";
- private static final boolean DEBUG = Process.myUid() < FIRST_APPLICATION_UID;
private static final int MAX_SIZE = 1024;
- private final Object mLock = new Object();
private final CloseGuard mCloseGuard = CloseGuard.get();
private final boolean mIsOwner;
private final long mMemoryAddr;
-
- /** Fd for the shared memory object, -1 when closed */
- @GuardedBy("mLock")
private int mFd = -1;
/**
@@ -85,7 +74,6 @@ public final class MemoryIntArray implements Parcelable, Closeable {
mFd = nativeCreate(name, size);
mMemoryAddr = nativeOpen(mFd, mIsOwner);
mCloseGuard.open("close");
- if (DEBUG) Log.i(TAG, "created " + getString());
}
private MemoryIntArray(Parcel parcel) throws IOException {
@@ -97,8 +85,6 @@ public final class MemoryIntArray implements Parcelable, Closeable {
mFd = pfd.detachFd();
mMemoryAddr = nativeOpen(mFd, mIsOwner);
mCloseGuard.open("close");
-
- if (DEBUG) Log.i(TAG, "created from parcel " + getString());
}
/**
@@ -155,33 +141,13 @@ public final class MemoryIntArray implements Parcelable, Closeable {
*/
@Override
public void close() throws IOException {
- synchronized (mLock) {
- if (!isClosed()) {
- if (DEBUG) {
- try {
- throw new Exception();
- } catch (Exception here) {
- Log.i(TAG, "closing " + getString(), here);
- }
- }
- nativeClose(mFd, mMemoryAddr, mIsOwner);
- mFd = -1;
- mCloseGuard.close();
- } else {
- try {
- throw new Exception();
- } catch (Exception here) {
- if (DEBUG) Log.i(TAG, getString() + " already closed", here);
- }
- }
+ if (!isClosed()) {
+ nativeClose(mFd, mMemoryAddr, mIsOwner);
+ mFd = -1;
+ mCloseGuard.close();
}
}
- private String getString() {
- return this.getClass().getSimpleName() + "@" + System.identityHashCode(this)
- + " mMemoryAddr=" + mMemoryAddr + " mFd=" + mFd;
- }
-
/**
* @return Whether this array is closed and shouldn't be used.
*/
@@ -196,9 +162,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
mCloseGuard.warnIfOpen();
}
- if (!isClosed()) {
- IoUtils.closeQuietly(this);
- }
+ IoUtils.closeQuietly(this);
} finally {
super.finalize();
}
@@ -242,8 +206,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
private void enforceNotClosed() {
if (isClosed()) {
- throw new IllegalStateException("cannot interact with a closed instance "
- + getString());
+ throw new IllegalStateException("cannot interact with a closed instance");
}
}
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
new file mode 100644
index 000000000000..86ed1229b28e
--- /dev/null
+++ b/core/java/android/util/OWNERS
@@ -0,0 +1,2 @@
+per-file FeatureFlagUtils.java = sbasi@google.com
+per-file FeatureFlagUtils.java = zhfan@google.com
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index 4805318385d5..3350f3e164bc 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -34,7 +34,7 @@ public final class StatsLog extends StatsLogInternal {
*/
public static boolean logStart(int label) {
if (label >= 0 && label < 16) {
- StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__START);
+ StatsLog.write(APP_BREADCRUMB_REPORTED, label, APP_BREADCRUMB_REPORTED__STATE__START);
return true;
}
return false;
@@ -48,7 +48,7 @@ public final class StatsLog extends StatsLogInternal {
*/
public static boolean logStop(int label) {
if (label >= 0 && label < 16) {
- StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__STOP);
+ StatsLog.write(APP_BREADCRUMB_REPORTED, label, APP_BREADCRUMB_REPORTED__STATE__STOP);
return true;
}
return false;
@@ -62,7 +62,8 @@ public final class StatsLog extends StatsLogInternal {
*/
public static boolean logEvent(int label) {
if (label >= 0 && label < 16) {
- StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__UNSPECIFIED);
+ StatsLog.write(APP_BREADCRUMB_REPORTED, label,
+ APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
return true;
}
return false;
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
deleted file mode 100644
index 51fb18a95c4c..000000000000
--- a/core/java/android/util/StatsManager.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright 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.util;
-
-import android.Manifest;
-import android.annotation.RequiresPermission;
-import android.os.IBinder;
-import android.os.IStatsManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-
-
-/*
- *
- *
- *
- *
- * THIS ENTIRE FILE IS ONLY TEMPORARY TO PREVENT BREAKAGES OF DEPENDENCIES ON OLD APIS.
- * The new StatsManager is to be found in android.app.StatsManager.
- * TODO: Delete this file!
- *
- *
- *
- *
- */
-
-
-/**
- * API for StatsD clients to send configurations and retrieve data.
- *
- * @hide
- */
-public class StatsManager {
- IStatsManager mService;
- private static final String TAG = "StatsManager";
-
- /**
- * Constructor for StatsManagerClient.
- *
- * @hide
- */
- public StatsManager() {
- }
-
- /**
- * Temporary to prevent build failures. Will be deleted.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when adding configuration");
- return false;
- }
- return service.addConfiguration(Long.parseLong(configKey), config, pkg, cls);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when adding configuration");
- return false;
- }
- }
- }
-
- /**
- * Clients can send a configuration and simultaneously registers the name of a broadcast
- * receiver that listens for when it should request data.
- *
- * @param configKey An arbitrary integer that allows clients to track the configuration.
- * @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
- * dependencies eg, conditions and matchers).
- * @param pkg The package name to receive the broadcast.
- * @param cls The name of the class that receives the broadcast.
- * @return true if successful
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when adding configuration");
- return false;
- }
- return service.addConfiguration(configKey, config, pkg, cls);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when adding configuration");
- return false;
- }
- }
- }
-
- /**
- * Temporary to prevent build failures. Will be deleted.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean removeConfiguration(String configKey) {
- // To prevent breakages of old dependencies.
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when removing configuration");
- return false;
- }
- return service.removeConfiguration(Long.parseLong(configKey));
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when removing configuration");
- return false;
- }
- }
- }
-
- /**
- * Remove a configuration from logging.
- *
- * @param configKey Configuration key to remove.
- * @return true if successful
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean removeConfiguration(long configKey) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when removing configuration");
- return false;
- }
- return service.removeConfiguration(configKey);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when removing configuration");
- return false;
- }
- }
- }
-
- /**
- * Temporary to prevent build failures. Will be deleted.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public byte[] getData(String configKey) {
- // TODO: remove this and all other methods with String-based config keys.
- // To prevent build breakages of dependencies.
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting data");
- return null;
- }
- return service.getData(Long.parseLong(configKey));
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting data");
- return null;
- }
- }
- }
-
- /**
- * Clients can request data with a binder call. This getter is destructive and also clears
- * the retrieved metrics from statsd memory.
- *
- * @param configKey Configuration key to retrieve data from.
- * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public byte[] getData(long configKey) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting data");
- return null;
- }
- return service.getData(configKey);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting data");
- return null;
- }
- }
- }
-
- /**
- * Clients can request metadata for statsd. Will contain stats across all configurations but not
- * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
- * This getter is not destructive and will not reset any metrics/counters.
- *
- * @return Serialized StatsdStatsReport proto. Returns null on failure.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public byte[] getMetadata() {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting metadata");
- return null;
- }
- return service.getMetadata();
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting metadata");
- return null;
- }
- }
- }
-
- private class StatsdDeathRecipient implements IBinder.DeathRecipient {
- @Override
- public void binderDied() {
- synchronized (this) {
- mService = null;
- }
- }
- }
-
- private IStatsManager getIStatsManagerLocked() throws RemoteException {
- if (mService != null) {
- return mService;
- }
- mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
- if (mService != null) {
- mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
- }
- return mService;
- }
-}
diff --git a/core/java/android/util/XmlPullAttributes.java b/core/java/android/util/XmlPullAttributes.java
index 6c8bb39772f3..cb35eb5c12a3 100644
--- a/core/java/android/util/XmlPullAttributes.java
+++ b/core/java/android/util/XmlPullAttributes.java
@@ -34,6 +34,10 @@ class XmlPullAttributes implements AttributeSet {
return mParser.getAttributeCount();
}
+ public String getAttributeNamespace (int index) {
+ return mParser.getAttributeNamespace(index);
+ }
+
public String getAttributeName(int index) {
return mParser.getAttributeName(index);
}
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index a61c8c1dba68..0b72c22d4e27 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -52,6 +52,16 @@ public final class DisplayCutout {
private static final String TAG = "DisplayCutout";
private static final String DP_MARKER = "@dp";
+ /**
+ * Category for overlays that allow emulating a display cutout on devices that don't have
+ * one.
+ *
+ * @see android.content.om.IOverlayManager
+ * @hide
+ */
+ public static final String EMULATION_OVERLAY_CATEGORY =
+ "com.android.internal.display_cutout_emulation";
+
private static final Rect ZERO_RECT = new Rect();
private static final Region EMPTY_REGION = new Region();
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 37e9815c93c5..7251b71ac35d 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -20,6 +20,7 @@ import static android.view.DisplayInfoProto.APP_HEIGHT;
import static android.view.DisplayInfoProto.APP_WIDTH;
import static android.view.DisplayInfoProto.LOGICAL_HEIGHT;
import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
+import static android.view.DisplayInfoProto.NAME;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -685,6 +686,7 @@ public final class DisplayInfo implements Parcelable {
protoOutputStream.write(LOGICAL_HEIGHT, logicalHeight);
protoOutputStream.write(APP_WIDTH, appWidth);
protoOutputStream.write(APP_HEIGHT, appHeight);
+ protoOutputStream.write(NAME, name);
protoOutputStream.end(token);
}
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
index ea6226b3ea69..69973e6d367a 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -16,6 +16,7 @@
package android.view;
+import android.graphics.Rect;
import android.view.RemoteAnimationTarget;
import android.view.IRecentsAnimationController;
@@ -28,15 +29,26 @@ import android.view.IRecentsAnimationController;
oneway interface IRecentsAnimationRunner {
/**
- * Called when the system is ready for the handler to start animating all the visible tasks.
+ * Deprecated, to be removed once Launcher updates
*/
void onAnimationStart(in IRecentsAnimationController controller,
- in RemoteAnimationTarget[] apps);
+ in RemoteAnimationTarget[] apps) = 0;
/**
* Called when the system needs to cancel the current animation. This can be due to the
* wallpaper not drawing in time, or the handler not finishing the animation within a predefined
* amount of time.
*/
- void onAnimationCanceled();
+ void onAnimationCanceled() = 1;
+
+ /**
+ * Called when the system is ready for the handler to start animating all the visible tasks.
+ *
+ * @param homeContentInsets The current home app content insets
+ * @param minimizedHomeBounds Specifies the bounds of the minimized home app, will be
+ * {@code null} if the device is not currently in split screen
+ */
+ void onAnimationStart_New(in IRecentsAnimationController controller,
+ in RemoteAnimationTarget[] apps, in Rect homeContentInsets,
+ in Rect minimizedHomeBounds) = 2;
}
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index c28c3894482d..facf575872ed 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -79,6 +79,11 @@ public class RemoteAnimationTarget implements Parcelable {
public final Rect clipRect;
/**
+ * The insets of the main app window.
+ */
+ public final Rect contentInsets;
+
+ /**
* The index of the element in the tree in prefix order. This should be used for z-layering
* to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to
* happen.
@@ -105,13 +110,14 @@ public class RemoteAnimationTarget implements Parcelable {
public final WindowConfiguration windowConfiguration;
public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
- Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds,
- WindowConfiguration windowConfig) {
+ Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
+ Rect sourceContainerBounds, WindowConfiguration windowConfig) {
this.mode = mode;
this.taskId = taskId;
this.leash = leash;
this.isTranslucent = isTranslucent;
this.clipRect = new Rect(clipRect);
+ this.contentInsets = new Rect(contentInsets);
this.prefixOrderIndex = prefixOrderIndex;
this.position = new Point(position);
this.sourceContainerBounds = new Rect(sourceContainerBounds);
@@ -124,6 +130,7 @@ public class RemoteAnimationTarget implements Parcelable {
leash = in.readParcelable(null);
isTranslucent = in.readBoolean();
clipRect = in.readParcelable(null);
+ contentInsets = in.readParcelable(null);
prefixOrderIndex = in.readInt();
position = in.readParcelable(null);
sourceContainerBounds = in.readParcelable(null);
@@ -142,6 +149,7 @@ public class RemoteAnimationTarget implements Parcelable {
dest.writeParcelable(leash, 0 /* flags */);
dest.writeBoolean(isTranslucent);
dest.writeParcelable(clipRect, 0 /* flags */);
+ dest.writeParcelable(contentInsets, 0 /* flags */);
dest.writeInt(prefixOrderIndex);
dest.writeParcelable(position, 0 /* flags */);
dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index c4a716011765..d26a2f643ed4 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -158,7 +158,7 @@ public class RenderNodeAnimator extends Animator {
}
private void applyInterpolator() {
- if (mInterpolator == null) return;
+ if (mInterpolator == null || mNativePtr == null) return;
long ni;
if (isNativeInterpolator(mInterpolator)) {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 370c97e37262..e50d40ef098f 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -331,6 +331,7 @@ public final class ThreadedRenderer {
private static final int FLAG_DUMP_FRAMESTATS = 1 << 0;
private static final int FLAG_DUMP_RESET = 1 << 1;
+ private static final int FLAG_DUMP_ALL = FLAG_DUMP_FRAMESTATS;
@IntDef(flag = true, prefix = { "FLAG_DUMP_" }, value = {
FLAG_DUMP_FRAMESTATS,
@@ -636,7 +637,10 @@ public final class ThreadedRenderer {
*/
void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) {
pw.flush();
- int flags = 0;
+ // If there's no arguments, eg 'dumpsys gfxinfo', then dump everything.
+ // If there's a targetted package, eg 'dumpsys gfxinfo com.android.systemui', then only
+ // dump the summary information
+ int flags = (args == null || args.length == 0) ? FLAG_DUMP_ALL : 0;
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case "framestats":
@@ -645,6 +649,9 @@ public final class ThreadedRenderer {
case "reset":
flags |= FLAG_DUMP_RESET;
break;
+ case "-a": // magic option passed when dumping a bugreport.
+ flags = FLAG_DUMP_ALL;
+ break;
}
}
nDumpProfileInfo(mNativeProxy, fd, flags);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 79fc13424bee..3ff3c97620f0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
+
import static java.lang.Math.max;
import android.animation.AnimatorInflater;
@@ -906,6 +907,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private static boolean sThrowOnInvalidFloatProperties;
+ /**
+ * Prior to P, {@code #startDragAndDrop} accepts a builder which produces an empty drag shadow.
+ * Currently zero size SurfaceControl cannot be created thus we create a dummy 1x1 surface
+ * instead.
+ */
+ private static boolean sAcceptZeroSizeDragShadow;
+
/** @hide */
@IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
@Retention(RetentionPolicy.SOURCE)
@@ -4798,6 +4806,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
Canvas.sCompatibilityRestore = targetSdkVersion < Build.VERSION_CODES.M;
Canvas.sCompatibilitySetBitmap = targetSdkVersion < Build.VERSION_CODES.O;
+ Canvas.setCompatibilityVersion(targetSdkVersion);
// In M and newer, our widgets can pass a "hint" value in the size
// for UNSPECIFIED MeasureSpecs. This lets child views of scrolling containers
@@ -4840,6 +4849,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P;
+ sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
+
sCompatibilityDone = true;
}
}
@@ -7261,7 +7272,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// becomes true where it should issue notifyViewEntered().
afm.notifyViewEntered(this);
}
- } else if (!isFocused()) {
+ } else if (!enter && !isFocused()) {
afm.notifyViewExited(this);
}
}
@@ -7297,6 +7308,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return mAccessibilityPaneTitle;
}
+ private boolean isAccessibilityPane() {
+ return !TextUtils.isEmpty(mAccessibilityPaneTitle);
+ }
+
/**
* Sends an accessibility event of the given type. If accessibility is
* not enabled this method has no effect. The default implementation calls
@@ -7860,6 +7875,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
structure.setAutofillHints(getAutofillHints());
structure.setAutofillValue(getAutofillValue());
}
+ structure.setImportantForAutofill(getImportantForAutofill());
}
int ignoredParentLeft = 0;
@@ -7994,6 +8010,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* <li>Call {@link
* android.view.autofill.AutofillManager#notifyViewVisibilityChanged(View, int, boolean)}
* when the visibility of a virtual child changed.
+ * <li>Call
+ * {@link android.view.autofill.AutofillManager#notifyViewClicked(View, int)} when a virtual
+ * child is clicked.
* <li>Call {@link AutofillManager#commit()} when the autofill context of the view structure
* changed and the current context should be committed (for example, when the user tapped
* a {@code SUBMIT} button in an HTML page).
@@ -8355,6 +8374,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
+ // If the app developer explicitly set hints for it, it's important.
+ if (getAutofillHints() != null) {
+ return true;
+ }
+
// Otherwise, assume it's not important...
return false;
}
@@ -8378,7 +8402,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityNodeProvider provider, AccessibilityNodeInfo info,
boolean forAutofill) {
structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
- null, null, null);
+ null, null, info.getViewIdResourceName());
Rect rect = structure.getTempRect();
info.getBoundsInParent(rect);
structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
@@ -8418,6 +8442,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
CharSequence cname = info.getClassName();
structure.setClassName(cname != null ? cname.toString() : null);
structure.setContentDescription(info.getContentDescription());
+ if (forAutofill) {
+ final int maxTextLength = info.getMaxTextLength();
+ if (maxTextLength != -1) {
+ structure.setMaxTextLength(maxTextLength);
+ }
+ structure.setHint(info.getHintText());
+ }
if ((info.getText() != null || info.getError() != null)) {
structure.setText(info.getText(), info.getTextSelectionStart(),
info.getTextSelectionEnd());
@@ -8428,7 +8459,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final AutofillValue autofillValue = AutofillValue.forText(structure.getText());
structure.setAutofillValue(autofillValue);
if (info.isPassword()) {
- structure.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ structure.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
} else {
structure.setDataIsSensitive(false);
@@ -8778,6 +8810,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Computes whether this virtual autofill view is visible to the user.
*
+ * <p><b>Note: </b>By default it returns {@code true}, but views providing a virtual hierarchy
+ * view must override it.
+ *
* @return Whether the view is visible on the screen.
*/
public boolean isVisibleToUserForAutofill(int virtualId) {
@@ -8790,7 +8825,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
}
- return false;
+ return true;
}
/**
@@ -11612,7 +11647,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return mode == IMPORTANT_FOR_ACCESSIBILITY_YES || isActionableForAccessibility()
|| hasListenersForAccessibility() || getAccessibilityNodeProvider() != null
|| getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE
- || (mAccessibilityPaneTitle != null);
+ || isAccessibilityPane();
}
/**
@@ -11709,18 +11744,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Changes to views with a pane title count as window state changes, as the pane title
// marks them as significant parts of the UI.
- if (!TextUtils.isEmpty(getAccessibilityPaneTitle())) {
- final AccessibilityEvent event = AccessibilityEvent.obtain();
- event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- event.setContentChangeTypes(changeType);
- onPopulateAccessibilityEvent(event);
- if (mParent != null) {
- try {
- mParent.requestSendAccessibilityEvent(this, event);
- } catch (AbstractMethodError e) {
- Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()
- + " does not fully implement ViewParent", e);
+ if ((changeType != AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE)
+ && isAccessibilityPane()) {
+ // If the pane isn't visible, content changed events are sufficient unless we're
+ // reporting that the view just disappeared
+ if ((getVisibility() == VISIBLE)
+ || (changeType == AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED)) {
+ final AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ event.setContentChangeTypes(changeType);
+ event.setSource(this);
+ onPopulateAccessibilityEvent(event);
+ if (mParent != null) {
+ try {
+ mParent.requestSendAccessibilityEvent(this, event);
+ } catch (AbstractMethodError e) {
+ Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName()
+ + " does not fully implement ViewParent", e);
+ }
}
+ return;
}
}
@@ -14010,6 +14053,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
if (accessibilityEnabled) {
+ // If we're an accessibility pane and the visibility changed, we already have sent
+ // a state change, so we really don't need to report other changes.
+ if (isAccessibilityPane()) {
+ changed &= ~VISIBILITY_MASK;
+ }
if ((changed & FOCUSABLE) != 0 || (changed & VISIBILITY_MASK) != 0
|| (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0
|| (changed & CONTEXT_CLICKABLE) != 0) {
@@ -23619,8 +23667,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* constructor variant is only useful when the {@link #onProvideShadowMetrics(Point, Point)}
* and {@link #onDrawShadow(Canvas)} methods are also overridden in order
* to supply the drag shadow's dimensions and appearance without
- * reference to any View object. If they are not overridden, then the result is an
- * invisible drag shadow.
+ * reference to any View object.
*/
public DragShadowBuilder() {
mView = new WeakReference<View>(null);
@@ -23774,6 +23821,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder
// does not accept zero size surface.
if (shadowSize.x == 0 || shadowSize.y == 0) {
+ if (!sAcceptZeroSizeDragShadow) {
+ throw new IllegalStateException("Drag shadow dimensions must be positive");
+ }
shadowSize.x = 1;
shadowSize.y = 1;
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index b09934e3e55f..276f50a51e66 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -21,7 +21,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Point;
+import android.graphics.Picture;
import android.graphics.Rect;
import android.os.Debug;
import android.os.Handler;
@@ -1782,27 +1782,18 @@ public class ViewDebug {
* @hide
*/
public static class HardwareCanvasProvider implements CanvasProvider {
-
- private View mView;
- private Point mSize;
- private RenderNode mNode;
- private DisplayListCanvas mCanvas;
+ private Picture mPicture;
@Override
public Canvas getCanvas(View view, int width, int height) {
- mView = view;
- mSize = new Point(width, height);
- mNode = RenderNode.create("ViewDebug", mView);
- mNode.setLeftTopRightBottom(0, 0, width, height);
- mNode.setClipToBounds(false);
- mCanvas = mNode.start(width, height);
- return mCanvas;
+ mPicture = new Picture();
+ return mPicture.beginRecording(width, height);
}
@Override
public Bitmap createBitmap() {
- mNode.end(mCanvas);
- return ThreadedRenderer.createHardwareBitmap(mNode, mSize.x, mSize.y);
+ mPicture.endRecording();
+ return Bitmap.createBitmap(mPicture);
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 273f097edc63..8e60a720f729 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3971,15 +3971,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
- * Layout debugging code which draws rectangles around layout params.
- *
- * <p>This function is called automatically when the developer setting is enabled.<p/>
- *
- * <p>It is strongly advised to only call this function from debug builds as there is
- * a risk of leaking unwanted layout information.<p/>
- *
- * @param canvas the canvas on which to draw
- * @param paint the paint used to draw through
+ * @hide
*/
protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
for (int i = 0; i < getChildCount(); i++) {
@@ -3989,19 +3981,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
- * Layout debugging code which draws rectangles around:
- * <ul>
- * <li>optical bounds<li/>
- * <li>margins<li/>
- * <li>clip bounds<li/>
- * <ul/>
- *
- * <p>This function is called automatically when the developer setting is enabled.<p/>
- *
- * <p>It is strongly advised to only call this function from debug builds as there is
- * a risk of leaking unwanted layout information.<p/>
- *
- * @param canvas the canvas on which to draw
+ * @hide
*/
protected void onDebugDraw(Canvas canvas) {
Paint paint = getDebugPaint();
@@ -7732,14 +7712,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/**
* Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
*
- * <p>This function is called automatically when the developer setting is enabled.<p/>
- *
- * <p>It is strongly advised to only call this function from debug builds as there is
- * a risk of leaking unwanted layout information.<p/>
- *
* @param view the view that contains these layout parameters
* @param canvas the canvas on which to draw
- * @param paint the paint used to draw through
+ *
+ * @hide
*/
public void onDebugDraw(View view, Canvas canvas, Paint paint) {
}
@@ -8243,6 +8219,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
}
+ /**
+ * @hide
+ */
@Override
public void onDebugDraw(View view, Canvas canvas, Paint paint) {
Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 1d94abeb51a2..3f7ab2aed4af 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -23,6 +23,8 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.LocaleList;
import android.util.Pair;
+import android.view.View.AutofillImportance;
+import android.view.ViewStructure.HtmlInfo;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
@@ -347,6 +349,12 @@ public abstract class ViewStructure {
public abstract void setAutofillOptions(CharSequence[] options);
/**
+ * Sets the {@link View#setImportantForAutofill(int) importantForAutofill mode} of the
+ * view associated with this node.
+ */
+ public void setImportantForAutofill(@AutofillImportance int mode) {}
+
+ /**
* Sets the {@link android.text.InputType} bits of this node.
*
* @param inputType inputType bits as defined by {@link android.text.InputType}.
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index bb9e391ddcb4..7bae28a4e817 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -21,6 +21,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pools;
+import android.view.accessibility.AccessibilityNodeInfo;
import java.util.ArrayList;
import java.util.List;
@@ -46,7 +47,7 @@ public class WindowInfo implements Parcelable {
public final Rect boundsInScreen = new Rect();
public List<IBinder> childTokens;
public CharSequence title;
- public int accessibilityIdOfAnchor = View.NO_ID;
+ public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
public boolean inPictureInPicture;
private WindowInfo() {
@@ -105,7 +106,7 @@ public class WindowInfo implements Parcelable {
parcel.writeInt(focused ? 1 : 0);
boundsInScreen.writeToParcel(parcel, flags);
parcel.writeCharSequence(title);
- parcel.writeInt(accessibilityIdOfAnchor);
+ parcel.writeLong(accessibilityIdOfAnchor);
parcel.writeInt(inPictureInPicture ? 1 : 0);
if (childTokens != null && !childTokens.isEmpty()) {
@@ -142,7 +143,7 @@ public class WindowInfo implements Parcelable {
focused = (parcel.readInt() == 1);
boundsInScreen.readFromParcel(parcel);
title = parcel.readCharSequence();
- accessibilityIdOfAnchor = parcel.readInt();
+ accessibilityIdOfAnchor = parcel.readLong();
inPictureInPicture = (parcel.readInt() == 1);
final boolean hasChildren = (parcel.readInt() == 1);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1c5e87197750..c0a966602b0a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -63,6 +63,7 @@ import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import android.view.accessibility.AccessibilityNodeInfo;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2344,7 +2345,7 @@ public interface WindowManager extends ViewManager {
*
* @hide
*/
- public int accessibilityIdOfAnchor = -1;
+ public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
/**
* The window title isn't kept in sync with what is displayed in the title bar, so we
@@ -2538,7 +2539,7 @@ public interface WindowManager extends ViewManager {
out.writeInt(hasManualSurfaceInsets ? 1 : 0);
out.writeInt(preservePreviousSurfaceInsets ? 1 : 0);
out.writeInt(needsMenuKey);
- out.writeInt(accessibilityIdOfAnchor);
+ out.writeLong(accessibilityIdOfAnchor);
TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
out.writeInt(mColorMode);
out.writeLong(hideTimeoutMilliseconds);
@@ -2594,7 +2595,7 @@ public interface WindowManager extends ViewManager {
hasManualSurfaceInsets = in.readInt() != 0;
preservePreviousSurfaceInsets = in.readInt() != 0;
needsMenuKey = in.readInt();
- accessibilityIdOfAnchor = in.readInt();
+ accessibilityIdOfAnchor = in.readLong();
accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mColorMode = in.readInt();
hideTimeoutMilliseconds = in.readLong();
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 191c27073782..dee267dfea26 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -774,8 +774,9 @@ public final class AccessibilityManager {
*/
public void removeAccessibilityServicesStateChangeListener(
@NonNull AccessibilityServicesStateChangeListener listener) {
- // Final CopyOnWriteArrayList - no lock needed.
- mServicesStateChangeListeners.remove(listener);
+ synchronized (mLock) {
+ mServicesStateChangeListeners.remove(listener);
+ }
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 23e7d6191276..417a72530b72 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3173,6 +3173,15 @@ public class AccessibilityNodeInfo implements Parcelable {
*/
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ writeToParcelNoRecycle(parcel, flags);
+ // Since instances of this class are fetched via synchronous i.e. blocking
+ // calls in IPCs we always recycle as soon as the instance is marshaled.
+ recycle();
+ }
+
+ /** @hide */
+ @TestApi
+ public void writeToParcelNoRecycle(Parcel parcel, int flags) {
// Write bit set of indices of fields with values differing from default
long nonDefaultFields = 0;
int fieldIndex = 0; // index of the current field
@@ -3406,10 +3415,6 @@ public class AccessibilityNodeInfo implements Parcelable {
+ " vs " + fieldIndex);
}
}
-
- // Since instances of this class are fetched via synchronous i.e. blocking
- // calls in IPCs we always recycle as soon as the instance is marshaled.
- recycle();
}
/**
@@ -3557,7 +3562,7 @@ public class AccessibilityNodeInfo implements Parcelable {
if (isBitSet(nonDefaultFields, fieldIndex++)) {
mContentDescription = parcel.readCharSequence();
}
- if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readString();
+ if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 990fbdb019d6..29f8442b2b46 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -18,6 +18,7 @@ package android.view.animation;
import android.annotation.AnimRes;
import android.annotation.InterpolatorRes;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -58,14 +59,43 @@ public class AnimationUtils {
}
};
- /** @hide */
+ /**
+ * Locks AnimationUtils{@link #currentAnimationTimeMillis()} to a fixed value for the current
+ * thread. This is used by {@link android.view.Choreographer} to ensure that all accesses
+ * during a vsync update are synchronized to the timestamp of the vsync.
+ *
+ * It is also exposed to tests to allow for rapid, flake-free headless testing.
+ *
+ * Must be followed by a call to {@link #unlockAnimationClock()} to allow time to
+ * progress. Failing to do this will result in stuck animations, scrolls, and flings.
+ *
+ * Note that time is not allowed to "rewind" and must perpetually flow forward. So the
+ * lock may fail if the time is in the past from a previously returned value, however
+ * time will be frozen for the duration of the lock. The clock is a thread-local, so
+ * ensure that {@link #lockAnimationClock(long)}, {@link #unlockAnimationClock()}, and
+ * {@link #currentAnimationTimeMillis()} are all called on the same thread.
+ *
+ * This is also not reference counted in any way. Any call to {@link #unlockAnimationClock()}
+ * will unlock the clock for everyone on the same thread. It is therefore recommended
+ * for tests to use their own thread to ensure that there is no collision with any existing
+ * {@link android.view.Choreographer} instance.
+ *
+ * @hide
+ * */
+ @TestApi
public static void lockAnimationClock(long vsyncMillis) {
AnimationState state = sAnimationState.get();
state.animationClockLocked = true;
state.currentVsyncTimeMillis = vsyncMillis;
}
- /** @hide */
+ /**
+ * Frees the time lock set in place by {@link #lockAnimationClock(long)}. Must be called
+ * to allow the animation clock to self-update.
+ *
+ * @hide
+ */
+ @TestApi
public static void unlockAnimationClock() {
sAnimationState.get().animationClockLocked = false;
}
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 5ce2421aac87..cb1d89c54d9a 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -38,6 +38,7 @@ public final class AutofillId implements Parcelable {
}
/** @hide */
+ @TestApi
public AutofillId(AutofillId parent, int virtualChildId) {
mVirtual = true;
mViewId = parent.mViewId;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 63a9990cf839..7792fa640015 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -16,10 +16,15 @@
package android.view.autofill;
+import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
+import static android.view.autofill.Helper.sDebug;
+import static android.view.autofill.Helper.sVerbose;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.SystemService;
import android.content.ComponentName;
import android.content.Context;
@@ -47,13 +52,14 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+
import org.xmlpull.v1.XmlPullParserException;
-import sun.misc.Cleaner;
import java.io.IOException;
import java.io.PrintWriter;
@@ -66,11 +72,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
-import static android.view.autofill.Helper.sDebug;
-import static android.view.autofill.Helper.sVerbose;
-
-// TODO: use java.lang.ref.Cleaner once Android supports Java 9
+//TODO: use java.lang.ref.Cleaner once Android supports Java 9
+import sun.misc.Cleaner;
/**
* The {@link AutofillManager} provides ways for apps and custom views to integrate with the
@@ -133,6 +136,7 @@ import static android.view.autofill.Helper.sVerbose;
* <p>It is safe to call into its methods from any thread.
*/
@SystemService(Context.AUTOFILL_MANAGER_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_AUTOFILL)
public final class AutofillManager {
private static final String TAG = "AutofillManager";
@@ -353,6 +357,13 @@ public final class AutofillManager {
@GuardedBy("mLock")
@Nullable private ArraySet<AutofillId> mFillableIds;
+ /**
+ * Views that were already "entered" - if they're entered again when the session is not active,
+ * they're ignored
+ * */
+ @GuardedBy("mLock")
+ @Nullable private ArraySet<AutofillId> mEnteredIds;
+
/** If set, session is commited when the field is clicked. */
@GuardedBy("mLock")
@Nullable private AutofillId mSaveTriggerId;
@@ -616,10 +627,9 @@ public final class AutofillManager {
/**
* @hide
*/
- public boolean isCompatibilityModeEnabled() {
- synchronized (mLock) {
- return mCompatibilityBridge != null;
- }
+ @GuardedBy("mLock")
+ public boolean isCompatibilityModeEnabledLocked() {
+ return mCompatibilityBridge != null;
}
/**
@@ -708,17 +718,30 @@ public final class AutofillManager {
notifyViewEntered(view, 0);
}
- private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
+ @GuardedBy("mLock")
+ private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
if (isDisabledByServiceLocked()) {
if (sVerbose) {
- Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
- + ") on state " + getStateAsStringLocked());
+ Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+ + ") on state " + getStateAsStringLocked() + " because disabled by svc");
}
return true;
}
- if (sVerbose && isFinishedLocked()) {
- Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
- + ") on state " + getStateAsStringLocked());
+ if (isFinishedLocked()) {
+ // Session already finished: ignore if automatic request and view already entered
+ if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
+ && mEnteredIds.contains(id)) {
+ if (sVerbose) {
+ Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+ + ") on state " + getStateAsStringLocked()
+ + " because view was already entered: " + mEnteredIds);
+ }
+ return true;
+ }
+ }
+ if (sVerbose) {
+ Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+ + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
}
return false;
}
@@ -748,8 +771,10 @@ public final class AutofillManager {
}
/** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+ @GuardedBy("mLock")
private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
- if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
+ final AutofillId id = getAutofillId(view);
+ if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
AutofillCallback callback = null;
@@ -762,7 +787,6 @@ public final class AutofillManager {
} else {
// don't notify entered when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
- final AutofillId id = getAutofillId(view);
final AutofillValue value = view.getAutofillValue();
if (!isActiveLocked()) {
@@ -772,6 +796,7 @@ public final class AutofillManager {
// Update focus on existing session.
updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
}
+ addEnteredIdLocked(id);
}
}
return callback;
@@ -791,6 +816,7 @@ public final class AutofillManager {
}
}
+ @GuardedBy("mLock")
void notifyViewExitedLocked(@NonNull View view) {
ensureServiceClientAddedIfNeededLocked();
@@ -892,10 +918,12 @@ public final class AutofillManager {
}
/** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+ @GuardedBy("mLock")
private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
int flags) {
+ final AutofillId id = getAutofillId(view, virtualId);
AutofillCallback callback = null;
- if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
+ if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
ensureServiceClientAddedIfNeededLocked();
@@ -906,8 +934,6 @@ public final class AutofillManager {
} else {
// don't notify entered when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
- final AutofillId id = getAutofillId(view, virtualId);
-
if (!isActiveLocked()) {
// Starts new session.
startSessionLocked(id, bounds, null, flags);
@@ -915,11 +941,20 @@ public final class AutofillManager {
// Update focus on existing session.
updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
}
+ addEnteredIdLocked(id);
}
}
return callback;
}
+ @GuardedBy("mLock")
+ private void addEnteredIdLocked(@NonNull AutofillId id) {
+ if (mEnteredIds == null) {
+ mEnteredIds = new ArraySet<>(1);
+ }
+ mEnteredIds.add(id);
+ }
+
/**
* Called when a virtual view that supports autofill is exited.
*
@@ -935,6 +970,7 @@ public final class AutofillManager {
}
}
+ @GuardedBy("mLock")
private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
ensureServiceClientAddedIfNeededLocked();
@@ -985,9 +1021,9 @@ public final class AutofillManager {
}
if (!mEnabled || !isActiveLocked()) {
- if (sVerbose && mEnabled) {
- Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
- + getStateAsStringLocked());
+ if (sVerbose) {
+ Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
+ + "): ignoring on state " + getStateAsStringLocked());
}
return;
}
@@ -1017,6 +1053,10 @@ public final class AutofillManager {
}
synchronized (mLock) {
if (!mEnabled || !isActiveLocked()) {
+ if (sVerbose) {
+ Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
+ + "): ignoring on state " + getStateAsStringLocked());
+ }
return;
}
@@ -1025,18 +1065,35 @@ public final class AutofillManager {
}
}
+ /**
+ * Called to indicate a {@link View} is clicked.
+ *
+ * @param view view that has been clicked.
+ */
+ public void notifyViewClicked(@NonNull View view) {
+ notifyViewClicked(view.getAutofillId());
+ }
/**
- * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
+ * Called to indicate a virtual view has been clicked.
*
- * @hide
+ * @param view the virtual view parent.
+ * @param virtualId id identifying the virtual child inside the parent view.
*/
- public void notifyViewClicked(View view) {
- final AutofillId id = view.getAutofillId();
+ public void notifyViewClicked(@NonNull View view, int virtualId) {
+ notifyViewClicked(getAutofillId(view, virtualId));
+ }
+ private void notifyViewClicked(AutofillId id) {
+ if (!hasAutofillFeature()) {
+ return;
+ }
if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
synchronized (mLock) {
+ if (!mEnabled || !isActiveLocked()) {
+ return;
+ }
if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
commitLocked();
@@ -1051,16 +1108,16 @@ public final class AutofillManager {
*
* @hide
*/
- public void onActivityFinished() {
+ public void onActivityFinishing() {
if (!hasAutofillFeature()) {
return;
}
synchronized (mLock) {
if (mSaveOnFinish) {
- if (sDebug) Log.d(TAG, "Committing session on finish() as requested by service");
+ if (sDebug) Log.d(TAG, "onActivityFinishing(): calling commitLocked()");
commitLocked();
} else {
- if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
+ if (sDebug) Log.d(TAG, "onActivityFinishing(): calling cancelLocked()");
cancelLocked();
}
}
@@ -1081,11 +1138,13 @@ public final class AutofillManager {
if (!hasAutofillFeature()) {
return;
}
+ if (sVerbose) Log.v(TAG, "commit() called by app");
synchronized (mLock) {
commitLocked();
}
}
+ @GuardedBy("mLock")
private void commitLocked() {
if (!mEnabled && !isActiveLocked()) {
return;
@@ -1114,6 +1173,7 @@ public final class AutofillManager {
}
}
+ @GuardedBy("mLock")
private void cancelLocked() {
if (!mEnabled && !isActiveLocked()) {
return;
@@ -1377,11 +1437,14 @@ public final class AutofillManager {
return new AutofillId(parent.getAutofillViewId(), virtualId);
}
+ @GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
if (sVerbose) {
Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
- + ", flags=" + flags + ", state=" + getStateAsStringLocked());
+ + ", flags=" + flags + ", state=" + getStateAsStringLocked()
+ + ", compatMode=" + isCompatibilityModeEnabledLocked()
+ + ", enteredIds=" + mEnteredIds);
}
if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
if (sVerbose) {
@@ -1392,11 +1455,12 @@ public final class AutofillManager {
}
try {
final AutofillClient client = getClient();
- if (client == null) return; // NOTE: getClient() already logd it..
+ if (client == null) return; // NOTE: getClient() already logged it..
mSessionId = mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
- mCallback != null, flags, client.autofillClientGetComponentName());
+ mCallback != null, flags, client.autofillClientGetComponentName(),
+ isCompatibilityModeEnabledLocked());
if (mSessionId != NO_SESSION) {
mState = STATE_ACTIVE;
}
@@ -1406,6 +1470,7 @@ public final class AutofillManager {
}
}
+ @GuardedBy("mLock")
private void finishSessionLocked() {
if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
@@ -1417,9 +1482,10 @@ public final class AutofillManager {
throw e.rethrowFromSystemServer();
}
- resetSessionLocked();
+ resetSessionLocked(/* resetEnteredIds= */ true);
}
+ @GuardedBy("mLock")
private void cancelSessionLocked() {
if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
@@ -1431,20 +1497,25 @@ public final class AutofillManager {
throw e.rethrowFromSystemServer();
}
- resetSessionLocked();
+ resetSessionLocked(/* resetEnteredIds= */ true);
}
- private void resetSessionLocked() {
+ @GuardedBy("mLock")
+ private void resetSessionLocked(boolean resetEnteredIds) {
mSessionId = NO_SESSION;
mState = STATE_UNKNOWN;
mTrackedViews = null;
mFillableIds = null;
mSaveTriggerId = null;
+ if (resetEnteredIds) {
+ mEnteredIds = null;
+ }
}
+ @GuardedBy("mLock")
private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
int flags) {
- if (sVerbose && action != ACTION_VIEW_EXITED) {
+ if (sVerbose) {
Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
+ ", value=" + value + ", action=" + action + ", flags=" + flags);
}
@@ -1459,7 +1530,7 @@ public final class AutofillManager {
client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, client.autofillClientGetComponentName(),
- mSessionId, action);
+ mSessionId, action, isCompatibilityModeEnabledLocked());
if (newId != mSessionId) {
if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
mSessionId = newId;
@@ -1476,6 +1547,7 @@ public final class AutofillManager {
}
}
+ @GuardedBy("mLock")
private void ensureServiceClientAddedIfNeededLocked() {
if (getClient() == null) {
return;
@@ -1613,7 +1685,7 @@ public final class AutofillManager {
mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
// Reset the session state
- resetSessionLocked();
+ resetSessionLocked(/* resetEnteredIds= */ true);
}
if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
// Reset connection to system
@@ -1809,7 +1881,7 @@ public final class AutofillManager {
private void setSessionFinished(int newState) {
synchronized (mLock) {
if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
- resetSessionLocked();
+ resetSessionLocked(/* resetEnteredIds= */ false);
mState = newState;
}
}
@@ -1937,13 +2009,16 @@ public final class AutofillManager {
pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
}
pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
+ pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
- pw.print(pfx); pw.print("compat mode enabled: "); pw.println(isCompatibilityModeEnabled());
+ pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
+ isCompatibilityModeEnabledLocked());
pw.print(pfx); pw.print("debug: "); pw.print(sDebug);
pw.print(" verbose: "); pw.println(sVerbose);
}
+ @GuardedBy("mLock")
private String getStateAsStringLocked() {
switch (mState) {
case STATE_UNKNOWN:
@@ -1961,14 +2036,17 @@ public final class AutofillManager {
}
}
+ @GuardedBy("mLock")
private boolean isActiveLocked() {
return mState == STATE_ACTIVE;
}
+ @GuardedBy("mLock")
private boolean isDisabledByServiceLocked() {
return mState == STATE_DISABLED_BY_SERVICE;
}
+ @GuardedBy("mLock")
private boolean isFinishedLocked() {
return mState == STATE_FINISHED;
}
@@ -2164,6 +2242,7 @@ public final class AutofillManager {
AutofillValue.forText(node.getText()));
}
+ @GuardedBy("mLock")
private void updateTrackedViewsLocked() {
if (mTrackedViews != null) {
mTrackedViews.onVisibleForAutofillChangedLocked();
@@ -2273,6 +2352,7 @@ public final class AutofillManager {
final boolean[] isVisible;
if (client.autofillClientIsVisibleForAutofill()) {
+ if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
isVisible = client.autofillClientGetViewVisibility(trackedIds);
} else {
// All false
@@ -2292,7 +2372,7 @@ public final class AutofillManager {
}
if (sVerbose) {
- Log.v(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
+ Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
+ " mVisibleTrackedIds=" + mVisibleTrackedIds
+ " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
}
@@ -2308,6 +2388,7 @@ public final class AutofillManager {
* @param id the id of the view/virtual view whose visibility changed.
* @param isVisible visible if the view is visible in the view hierarchy.
*/
+ @GuardedBy("mLock")
void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
if (sDebug) {
Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
@@ -2341,6 +2422,7 @@ public final class AutofillManager {
*
* @see AutofillClient#autofillClientIsVisibleForAutofill()
*/
+ @GuardedBy("mLock")
void onVisibleForAutofillChangedLocked() {
// The visibility of the views might have changed while the client was not be visible,
// hence update the visibility state for all views.
@@ -2396,6 +2478,9 @@ public final class AutofillManager {
}
if (mVisibleTrackedIds == null) {
+ if (sVerbose) {
+ Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
+ }
finishSessionLocked();
}
}
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index e80fdd93542c..1da998d01ba3 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -46,6 +46,7 @@ public class AutofillPopupWindow extends PopupWindow {
private final WindowPresenter mWindowPresenter;
private WindowManager.LayoutParams mWindowLayoutParams;
+ private boolean mFullScreen;
private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
new View.OnAttachStateChangeListener() {
@@ -104,12 +105,17 @@ public class AutofillPopupWindow extends PopupWindow {
*/
public void update(View anchor, int offsetX, int offsetY, int width, int height,
Rect virtualBounds) {
+ mFullScreen = width == LayoutParams.MATCH_PARENT && height == LayoutParams.MATCH_PARENT;
// If we are showing the popup for a virtual view we use a fake view which
// delegates to the anchor but present itself with the same bounds as the
// virtual view. This ensures that the location logic in popup works
// symmetrically when the dropdown is below and above the anchor.
final View actualAnchor;
- if (virtualBounds != null) {
+ if (mFullScreen) {
+ offsetX = 0;
+ offsetY = 0;
+ actualAnchor = anchor;
+ } else if (virtualBounds != null) {
final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top};
actualAnchor = new View(anchor.getContext()) {
@Override
@@ -209,6 +215,17 @@ public class AutofillPopupWindow extends PopupWindow {
}
@Override
+ protected boolean findDropDownPosition(View anchor, LayoutParams outParams,
+ int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
+ if (mFullScreen) {
+ // Do not patch LayoutParams if force full screen
+ return false;
+ }
+ return super.findDropDownPosition(anchor, outParams, xOffset, yOffset,
+ width, height, gravity, allowScroll);
+ }
+
+ @Override
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
if (sVerbose) {
Log.v(TAG, "showAsDropDown(): anchor=" + anchor + ", xoff=" + xoff + ", yoff=" + yoff
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 001854716715..56f79abf6c19 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -38,7 +38,7 @@ interface IAutoFillManager {
void removeClient(in IAutoFillManagerClient client, int userId);
int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
- in ComponentName componentName);
+ in ComponentName componentName, boolean compatMode);
FillEventHistory getFillEventHistory();
boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
void updateSession(int sessionId, in AutofillId id, in Rect bounds,
@@ -46,7 +46,7 @@ interface IAutoFillManager {
int updateOrRestartSession(IBinder activityToken, in IBinder appCallback,
in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId,
boolean hasCallback, int flags, in ComponentName componentName, int sessionId,
- int action);
+ int action, boolean compatMode);
void finishSession(int sessionId, int userId);
void cancelSession(int sessionId, int userId);
void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 9de26a86f20f..a2280a4acd11 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -20,10 +20,12 @@ import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
@@ -213,6 +215,7 @@ import java.util.concurrent.TimeUnit;
* </ul>
*/
@SystemService(Context.INPUT_METHOD_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_INPUT_METHODS)
public final class InputMethodManager {
static final boolean DEBUG = false;
static final String TAG = "InputMethodManager";
diff --git a/core/java/android/view/textclassifier/SmartSelection.java b/core/java/android/view/textclassifier/SmartSelection.java
index 8edf97ea0336..69c38ee4db4f 100644
--- a/core/java/android/view/textclassifier/SmartSelection.java
+++ b/core/java/android/view/textclassifier/SmartSelection.java
@@ -108,9 +108,9 @@ final class SmartSelection {
}
/**
- * Returns the language of the model.
+ * Returns a comma separated list of locales supported by the model as BCP 47 tags.
*/
- public static String getLanguage(int fd) {
+ public static String getLanguages(int fd) {
return nativeGetLanguage(fd);
}
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index af55dcd0ed72..cbc3828ba56e 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -121,6 +121,15 @@ final class SystemTextClassifier implements TextClassifier {
return mFallback.generateLinks(text, options);
}
+ /**
+ * @inheritDoc
+ */
+ @Override
+ public int getMaxGenerateLinksTextLength() {
+ // TODO: retrieve this from the bound service.
+ return mFallback.getMaxGenerateLinksTextLength();
+ }
+
private static final class TextSelectionCallback extends ITextSelectionCallback.Stub {
final ResponseReceiver<TextSelection> mReceiver = new ResponseReceiver<>();
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index a6a2a945e774..8fe1d8fcc805 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
@@ -378,7 +379,7 @@ public final class TextClassification implements Parcelable {
final List<Drawable> drawables = new ArrayList<>(bitmaps.size());
for (Bitmap bitmap : bitmaps) {
if (bitmap != null) {
- drawables.add(new BitmapDrawable(null, bitmap));
+ drawables.add(new BitmapDrawable(Resources.getSystem(), bitmap));
} else {
drawables.add(null);
}
@@ -681,7 +682,8 @@ public final class TextClassification implements Parcelable {
private TextClassification(Parcel in) {
mText = in.readString();
mPrimaryIcon = in.readInt() == 0
- ? null : new BitmapDrawable(null, Bitmap.CREATOR.createFromParcel(in));
+ ? null
+ : new BitmapDrawable(Resources.getSystem(), Bitmap.CREATOR.createFromParcel(in));
mPrimaryLabel = in.readString();
mPrimaryIntent = in.readInt() == 0 ? null : Intent.CREATOR.createFromParcel(in);
mPrimaryOnClickListener = null; // not parcelable
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 9f75c4a80ca2..2a62f23f19c7 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -276,9 +276,11 @@ public interface TextClassifier {
* @param text the text to generate annotations for
* @param options configuration for link generation
*
- * @throws IllegalArgumentException if text is null
+ * @throws IllegalArgumentException if text is null or the text is too long for the
+ * TextClassifier implementation.
*
* @see #generateLinks(CharSequence)
+ * @see #getMaxGenerateLinksTextLength()
*/
@WorkerThread
default TextLinks generateLinks(
@@ -299,9 +301,11 @@ public interface TextClassifier {
*
* @param text the text to generate annotations for
*
- * @throws IllegalArgumentException if text is null
+ * @throws IllegalArgumentException if text is null or the text is too long for the
+ * TextClassifier implementation.
*
* @see #generateLinks(CharSequence, TextLinks.Options)
+ * @see #getMaxGenerateLinksTextLength()
*/
@WorkerThread
default TextLinks generateLinks(@NonNull CharSequence text) {
@@ -309,6 +313,16 @@ public interface TextClassifier {
}
/**
+ * Returns the maximal length of text that can be processed by generateLinks.
+ *
+ * @see #generateLinks(CharSequence)
+ * @see #generateLinks(CharSequence, TextLinks.Options)
+ */
+ default int getMaxGenerateLinksTextLength() {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
* Returns a {@link Collection} of the entity types in the specified preset.
*
* @see #ENTITY_PRESET_ALL
@@ -461,6 +475,15 @@ public interface TextClassifier {
checkMainThread(allowInMainThread);
}
+ /**
+ * @throws IllegalArgumentException if text is null; the text is too long or options is null
+ */
+ public static void validate(@NonNull CharSequence text, int maxLength,
+ boolean allowInMainThread) {
+ validate(text, allowInMainThread);
+ Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()");
+ }
+
private static void checkMainThread(boolean allowInMainThread) {
if (!allowInMainThread && Looper.myLooper() == Looper.getMainLooper()) {
Slog.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread");
diff --git a/core/java/android/view/textclassifier/TextClassifierConstants.java b/core/java/android/view/textclassifier/TextClassifierConstants.java
index 00695b797cb3..efa69488521f 100644
--- a/core/java/android/view/textclassifier/TextClassifierConstants.java
+++ b/core/java/android/view/textclassifier/TextClassifierConstants.java
@@ -47,10 +47,19 @@ public final class TextClassifierConstants {
"smart_selection_enabled_for_edit_text";
private static final String SMART_LINKIFY_ENABLED =
"smart_linkify_enabled";
+ private static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
+ "suggest_selection_max_range_length";
+ private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH =
+ "classify_text_max_range_length";
+ private static final String GENERATE_LINKS_MAX_TEXT_LENGTH =
+ "generate_links_max_text_length";
private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
+ private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
+ private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
+ private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
/** Default settings. */
static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
@@ -58,11 +67,17 @@ public final class TextClassifierConstants {
private final boolean mDarkLaunch;
private final boolean mSuggestSelectionEnabledForEditableText;
private final boolean mSmartLinkifyEnabled;
+ private final int mSuggestSelectionMaxRangeLength;
+ private final int mClassifyTextMaxRangeLength;
+ private final int mGenerateLinksMaxTextLength;
private TextClassifierConstants() {
mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
mSmartLinkifyEnabled = SMART_LINKIFY_ENABLED_DEFAULT;
+ mSuggestSelectionMaxRangeLength = SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT;
+ mClassifyTextMaxRangeLength = CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT;
+ mGenerateLinksMaxTextLength = GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT;
}
private TextClassifierConstants(@Nullable String settings) {
@@ -82,6 +97,15 @@ public final class TextClassifierConstants {
mSmartLinkifyEnabled = parser.getBoolean(
SMART_LINKIFY_ENABLED,
SMART_LINKIFY_ENABLED_DEFAULT);
+ mSuggestSelectionMaxRangeLength = parser.getInt(
+ SUGGEST_SELECTION_MAX_RANGE_LENGTH,
+ SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
+ mClassifyTextMaxRangeLength = parser.getInt(
+ CLASSIFY_TEXT_MAX_RANGE_LENGTH,
+ CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
+ mGenerateLinksMaxTextLength = parser.getInt(
+ GENERATE_LINKS_MAX_TEXT_LENGTH,
+ GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
}
static TextClassifierConstants loadFromString(String settings) {
@@ -99,4 +123,16 @@ public final class TextClassifierConstants {
public boolean isSmartLinkifyEnabled() {
return mSmartLinkifyEnabled;
}
+
+ public int getSuggestSelectionMaxRangeLength() {
+ return mSuggestSelectionMaxRangeLength;
+ }
+
+ public int getClassifyTextMaxRangeLength() {
+ return mClassifyTextMaxRangeLength;
+ }
+
+ public int getGenerateLinksMaxTextLength() {
+ return mGenerateLinksMaxTextLength;
+ }
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 9f389ba0c140..795caffd04d5 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -27,8 +27,10 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Bundle;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
+import android.os.UserManager;
import android.provider.Browser;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
@@ -56,6 +58,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -99,11 +102,9 @@ public final class TextClassifierImpl implements TextClassifier {
private final Object mLock = new Object();
@GuardedBy("mLock") // Do not access outside this lock.
- private Map<Locale, String> mModelFilePaths;
+ private List<ModelFile> mAllModelFiles;
@GuardedBy("mLock") // Do not access outside this lock.
- private Locale mLocale;
- @GuardedBy("mLock") // Do not access outside this lock.
- private int mVersion;
+ private ModelFile mModel;
@GuardedBy("mLock") // Do not access outside this lock.
private SmartSelection mSmartSelection;
@@ -127,7 +128,9 @@ public final class TextClassifierImpl implements TextClassifier {
@Nullable TextSelection.Options options) {
Utils.validate(text, selectionStartIndex, selectionEndIndex, false /* allowInMainThread */);
try {
- if (text.length() > 0) {
+ final int rangeLength = selectionEndIndex - selectionStartIndex;
+ if (text.length() > 0
+ && rangeLength <= getSettings().getSuggestSelectionMaxRangeLength()) {
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final boolean darkLaunchAllowed = options != null && options.isDarkLaunchAllowed();
final SmartSelection smartSelection = getSmartSelection(locales);
@@ -182,7 +185,8 @@ public final class TextClassifierImpl implements TextClassifier {
@Nullable TextClassification.Options options) {
Utils.validate(text, startIndex, endIndex, false /* allowInMainThread */);
try {
- if (text.length() > 0) {
+ final int rangeLength = endIndex - startIndex;
+ if (text.length() > 0 && rangeLength <= getSettings().getClassifyTextMaxRangeLength()) {
final String string = text.toString();
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final Calendar refTime = (options == null) ? null : options.getReferenceTime();
@@ -206,7 +210,7 @@ public final class TextClassifierImpl implements TextClassifier {
@Override
public TextLinks generateLinks(
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
- Utils.validate(text, false /* allowInMainThread */);
+ Utils.validate(text, getMaxGenerateLinksTextLength(), false /* allowInMainThread */);
final String textString = text.toString();
final TextLinks.Builder builder = new TextLinks.Builder(textString);
@@ -240,6 +244,12 @@ public final class TextClassifierImpl implements TextClassifier {
return mFallback.generateLinks(text, options);
}
+ /** @inheritDoc */
+ @Override
+ public int getMaxGenerateLinksTextLength() {
+ return getSettings().getGenerateLinksMaxTextLength();
+ }
+
@Override
public Collection<String> getEntitiesForPreset(@TextClassifier.EntityPreset int entityPreset) {
switch (entityPreset) {
@@ -279,18 +289,18 @@ public final class TextClassifierImpl implements TextClassifier {
private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
synchronized (mLock) {
localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
- final Locale locale = findBestSupportedLocaleLocked(localeList);
- if (locale == null) {
- throw new FileNotFoundException("No file for null locale");
+ final ModelFile bestModel = findBestModelLocked(localeList);
+ if (bestModel == null) {
+ throw new FileNotFoundException("No model for " + localeList.toLanguageTags());
}
- if (mSmartSelection == null || !Objects.equals(mLocale, locale)) {
+ if (mSmartSelection == null || !Objects.equals(mModel, bestModel)) {
+ Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
destroySmartSelectionIfExistsLocked();
- final ParcelFileDescriptor fd = getFdLocked(locale);
- final int modelFd = fd.getFd();
- mVersion = SmartSelection.getVersion(modelFd);
- mSmartSelection = new SmartSelection(modelFd);
+ final ParcelFileDescriptor fd = ParcelFileDescriptor.open(
+ new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
+ mSmartSelection = new SmartSelection(fd.getFd());
closeAndLogError(fd);
- mLocale = locale;
+ mModel = bestModel;
}
return mSmartSelection;
}
@@ -298,74 +308,8 @@ public final class TextClassifierImpl implements TextClassifier {
private String getSignature(String text, int start, int end) {
synchronized (mLock) {
- return DefaultLogger.createSignature(text, start, end, mContext, mVersion, mLocale);
- }
- }
-
- @GuardedBy("mLock") // Do not call outside this lock.
- private ParcelFileDescriptor getFdLocked(Locale locale) throws FileNotFoundException {
- ParcelFileDescriptor updateFd;
- int updateVersion = -1;
- try {
- updateFd = ParcelFileDescriptor.open(
- new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY);
- if (updateFd != null) {
- updateVersion = SmartSelection.getVersion(updateFd.getFd());
- }
- } catch (FileNotFoundException e) {
- updateFd = null;
- }
- ParcelFileDescriptor factoryFd;
- int factoryVersion = -1;
- try {
- final String factoryModelFilePath = getFactoryModelFilePathsLocked().get(locale);
- if (factoryModelFilePath != null) {
- factoryFd = ParcelFileDescriptor.open(
- new File(factoryModelFilePath), ParcelFileDescriptor.MODE_READ_ONLY);
- if (factoryFd != null) {
- factoryVersion = SmartSelection.getVersion(factoryFd.getFd());
- }
- } else {
- factoryFd = null;
- }
- } catch (FileNotFoundException e) {
- factoryFd = null;
- }
-
- if (updateFd == null) {
- if (factoryFd != null) {
- return factoryFd;
- } else {
- throw new FileNotFoundException(
- String.format(Locale.US, "No model file found for %s", locale));
- }
- }
-
- final int updateFdInt = updateFd.getFd();
- final boolean localeMatches = Objects.equals(
- locale.getLanguage().trim().toLowerCase(),
- SmartSelection.getLanguage(updateFdInt).trim().toLowerCase());
- if (factoryFd == null) {
- if (localeMatches) {
- return updateFd;
- } else {
- closeAndLogError(updateFd);
- throw new FileNotFoundException(
- String.format(Locale.US, "No model file found for %s", locale));
- }
- }
-
- if (!localeMatches) {
- closeAndLogError(updateFd);
- return factoryFd;
- }
-
- if (updateVersion > factoryVersion) {
- closeAndLogError(factoryFd);
- return updateFd;
- } else {
- closeAndLogError(updateFd);
- return factoryFd;
+ return DefaultLogger.createSignature(text, start, end, mContext, mModel.getVersion(),
+ mModel.getSupportedLocales());
}
}
@@ -377,60 +321,66 @@ public final class TextClassifierImpl implements TextClassifier {
}
}
+ /**
+ * Finds the most appropriate model to use for the given target locale list.
+ *
+ * The basic logic is: we ignore all models that don't support any of the target locales. For
+ * the remaining candidates, we take the update model unless its version number is lower than
+ * the factory version. It's assumed that factory models do not have overlapping locale ranges
+ * and conflict resolution between these models hence doesn't matter.
+ */
@GuardedBy("mLock") // Do not call outside this lock.
@Nullable
- private Locale findBestSupportedLocaleLocked(LocaleList localeList) {
+ private ModelFile findBestModelLocked(LocaleList localeList) {
// Specified localeList takes priority over the system default, so it is listed first.
final String languages = localeList.isEmpty()
? LocaleList.getDefault().toLanguageTags()
: localeList.toLanguageTags() + "," + LocaleList.getDefault().toLanguageTags();
final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
- final List<Locale> supportedLocales =
- new ArrayList<>(getFactoryModelFilePathsLocked().keySet());
- final Locale updatedModelLocale = getUpdatedModelLocale();
- if (updatedModelLocale != null) {
- supportedLocales.add(updatedModelLocale);
+ ModelFile bestModel = null;
+ int bestModelVersion = -1;
+ for (ModelFile model : listAllModelsLocked()) {
+ if (model.isAnyLanguageSupported(languageRangeList)) {
+ if (model.getVersion() >= bestModelVersion) {
+ bestModel = model;
+ bestModelVersion = model.getVersion();
+ }
+ }
}
- return Locale.lookup(languageRangeList, supportedLocales);
+ return bestModel;
}
+ /** Returns a list of all model files available, in order of precedence. */
@GuardedBy("mLock") // Do not call outside this lock.
- private Map<Locale, String> getFactoryModelFilePathsLocked() {
- if (mModelFilePaths == null) {
- final Map<Locale, String> modelFilePaths = new HashMap<>();
+ private List<ModelFile> listAllModelsLocked() {
+ if (mAllModelFiles == null) {
+ final List<ModelFile> allModels = new ArrayList<>();
+ // The update model has the highest precedence.
+ if (new File(UPDATED_MODEL_FILE_PATH).exists()) {
+ final ModelFile updatedModel = ModelFile.fromPath(UPDATED_MODEL_FILE_PATH);
+ if (updatedModel != null) {
+ allModels.add(updatedModel);
+ }
+ }
+ // Factory models should never have overlapping locales, so the order doesn't matter.
final File modelsDir = new File(MODEL_DIR);
if (modelsDir.exists() && modelsDir.isDirectory()) {
- final File[] models = modelsDir.listFiles();
+ final File[] modelFiles = modelsDir.listFiles();
final Pattern modelFilenamePattern = Pattern.compile(MODEL_FILE_REGEX);
- final int size = models.length;
- for (int i = 0; i < size; i++) {
- final File modelFile = models[i];
+ for (File modelFile : modelFiles) {
final Matcher matcher = modelFilenamePattern.matcher(modelFile.getName());
if (matcher.matches() && modelFile.isFile()) {
- final String language = matcher.group(1);
- final Locale locale = Locale.forLanguageTag(language);
- modelFilePaths.put(locale, modelFile.getAbsolutePath());
+ final ModelFile model = ModelFile.fromPath(modelFile.getAbsolutePath());
+ if (model != null) {
+ allModels.add(model);
+ }
}
}
}
- mModelFilePaths = modelFilePaths;
- }
- return mModelFilePaths;
- }
-
- @Nullable
- private Locale getUpdatedModelLocale() {
- try {
- final ParcelFileDescriptor updateFd = ParcelFileDescriptor.open(
- new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY);
- final Locale locale = Locale.forLanguageTag(
- SmartSelection.getLanguage(updateFd.getFd()));
- closeAndLogError(updateFd);
- return locale;
- } catch (FileNotFoundException e) {
- return null;
+ mAllModelFiles = allModels;
}
+ return mAllModelFiles;
}
private TextClassification createClassificationResult(
@@ -520,6 +470,95 @@ public final class TextClassifierImpl implements TextClassifier {
}
/**
+ * Describes TextClassifier model files on disk.
+ */
+ private static final class ModelFile {
+
+ private final String mPath;
+ private final String mName;
+ private final int mVersion;
+ private final List<Locale> mSupportedLocales;
+
+ /** Returns null if the path did not point to a compatible model. */
+ static @Nullable ModelFile fromPath(String path) {
+ final File file = new File(path);
+ try {
+ final ParcelFileDescriptor modelFd = ParcelFileDescriptor.open(
+ file, ParcelFileDescriptor.MODE_READ_ONLY);
+ final int version = SmartSelection.getVersion(modelFd.getFd());
+ final String supportedLocalesStr = SmartSelection.getLanguages(modelFd.getFd());
+ if (supportedLocalesStr.isEmpty()) {
+ Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
+ return null;
+ }
+ final List<Locale> supportedLocales = new ArrayList<>();
+ for (String langTag : supportedLocalesStr.split(",")) {
+ supportedLocales.add(Locale.forLanguageTag(langTag));
+ }
+ closeAndLogError(modelFd);
+ return new ModelFile(path, file.getName(), version, supportedLocales);
+ } catch (FileNotFoundException e) {
+ Log.e(DEFAULT_LOG_TAG, "Failed to peek " + file.getAbsolutePath(), e);
+ return null;
+ }
+ }
+
+ /** The absolute path to the model file. */
+ String getPath() {
+ return mPath;
+ }
+
+ /** A name to use for signature generation. Effectively the name of the model file. */
+ String getName() {
+ return mName;
+ }
+
+ /** Returns the version tag in the model's metadata. */
+ int getVersion() {
+ return mVersion;
+ }
+
+ /** Returns whether the language supports any language in the given ranges. */
+ boolean isAnyLanguageSupported(List<Locale.LanguageRange> languageRanges) {
+ return Locale.lookup(languageRanges, mSupportedLocales) != null;
+ }
+
+ /** All locales supported by the model. */
+ List<Locale> getSupportedLocales() {
+ return Collections.unmodifiableList(mSupportedLocales);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ } else if (other == null || !ModelFile.class.isAssignableFrom(other.getClass())) {
+ return false;
+ } else {
+ final ModelFile otherModel = (ModelFile) other;
+ return mPath.equals(otherModel.mPath);
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringJoiner localesJoiner = new StringJoiner(",");
+ for (Locale locale : mSupportedLocales) {
+ localesJoiner.add(locale.toLanguageTag());
+ }
+ return String.format(Locale.US, "ModelFile { path=%s name=%s version=%d locales=%s }",
+ mPath, mName, mVersion, localesJoiner.toString());
+ }
+
+ private ModelFile(String path, String name, int version, List<Locale> supportedLocales) {
+ mPath = path;
+ mName = name;
+ mVersion = version;
+ mSupportedLocales = supportedLocales;
+ }
+ }
+
+ /**
* Creates intents based on the classification type.
*/
static final class IntentFactory {
@@ -541,7 +580,7 @@ public final class TextClassifierImpl implements TextClassifier {
case TextClassifier.TYPE_EMAIL:
return createForEmail(text);
case TextClassifier.TYPE_PHONE:
- return createForPhone(text);
+ return createForPhone(context, text);
case TextClassifier.TYPE_ADDRESS:
return createForAddress(text);
case TextClassifier.TYPE_URL:
@@ -573,15 +612,23 @@ public final class TextClassifierImpl implements TextClassifier {
}
@NonNull
- private static List<Intent> createForPhone(String text) {
- return Arrays.asList(
- new Intent(Intent.ACTION_DIAL)
- .setData(Uri.parse(String.format("tel:%s", text))),
- new Intent(Intent.ACTION_INSERT_OR_EDIT)
- .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
- .putExtra(ContactsContract.Intents.Insert.PHONE, text),
- new Intent(Intent.ACTION_SENDTO)
- .setData(Uri.parse(String.format("smsto:%s", text))));
+ private static List<Intent> createForPhone(Context context, String text) {
+ final List<Intent> intents = new ArrayList<>();
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ final Bundle userRestrictions = userManager != null
+ ? userManager.getUserRestrictions() : new Bundle();
+ if (!userRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS, false)) {
+ intents.add(new Intent(Intent.ACTION_DIAL)
+ .setData(Uri.parse(String.format("tel:%s", text))));
+ }
+ intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT)
+ .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+ .putExtra(ContactsContract.Intents.Insert.PHONE, text));
+ if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
+ intents.add(new Intent(Intent.ACTION_SENDTO)
+ .setData(Uri.parse(String.format("smsto:%s", text))));
+ }
+ return intents;
}
@NonNull
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index d866d1305172..3d252f293663 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -108,6 +108,7 @@ public final class TextLinks implements Parcelable {
* @param text the text to apply the links to. Must match the original text
* @param applyStrategy strategy for resolving link conflicts
* @param spanFactory a factory to generate spans from TextLinks. Will use a default if null
+ * @param allowPrefix whether to allow applying links only to a prefix of the text.
*
* @return a status code indicating whether or not the links were successfully applied
*
@@ -117,10 +118,12 @@ public final class TextLinks implements Parcelable {
public int apply(
@NonNull Spannable text,
@ApplyStrategy int applyStrategy,
- @Nullable Function<TextLink, TextLinkSpan> spanFactory) {
+ @Nullable Function<TextLink, TextLinkSpan> spanFactory,
+ boolean allowPrefix) {
Preconditions.checkNotNull(text);
checkValidApplyStrategy(applyStrategy);
- if (!mFullText.equals(text.toString())) {
+ final String textString = text.toString();
+ if (!mFullText.equals(textString) && !(allowPrefix && textString.startsWith(mFullText))) {
return STATUS_DIFFERENT_TEXT;
}
if (mLinks.isEmpty()) {
diff --git a/core/java/android/view/textclassifier/logging/DefaultLogger.java b/core/java/android/view/textclassifier/logging/DefaultLogger.java
index 6b848351cbf6..f510879cf401 100644
--- a/core/java/android/view/textclassifier/logging/DefaultLogger.java
+++ b/core/java/android/view/textclassifier/logging/DefaultLogger.java
@@ -17,7 +17,6 @@
package android.view.textclassifier.logging;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.metrics.LogMaker;
import android.util.Log;
@@ -27,8 +26,10 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.Preconditions;
+import java.util.List;
import java.util.Locale;
import java.util.Objects;
+import java.util.StringJoiner;
/**
* Default Logger.
@@ -79,7 +80,7 @@ public final class DefaultLogger extends Logger {
Preconditions.checkNotNull(event);
final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
.setType(getLogType(event))
- .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL)
+ .setSubtype(getLogSubType(event))
.setPackageName(event.getPackageName())
.addTaggedData(START_EVENT_DELTA, event.getDurationSinceSessionStart())
.addTaggedData(PREV_EVENT_DELTA, event.getDurationSincePreviousEvent())
@@ -136,6 +137,17 @@ public final class DefaultLogger extends Logger {
}
}
+ private static int getLogSubType(SelectionEvent event) {
+ switch (event.getInvocationMethod()) {
+ case SelectionEvent.INVOCATION_MANUAL:
+ return MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL;
+ case SelectionEvent.INVOCATION_LINK:
+ return MetricsEvent.TEXT_SELECTION_INVOCATION_LINK;
+ default:
+ return MetricsEvent.TEXT_SELECTION_INVOCATION_UNKNOWN;
+ }
+ }
+
private static String getLogTypeString(int logType) {
switch (logType) {
case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
@@ -175,6 +187,17 @@ public final class DefaultLogger extends Logger {
}
}
+ private static String getLogSubTypeString(int logSubType) {
+ switch (logSubType) {
+ case MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL:
+ return "MANUAL";
+ case MetricsEvent.TEXT_SELECTION_INVOCATION_LINK:
+ return "LINK";
+ default:
+ return UNKNOWN;
+ }
+ }
+
private static void debugLog(LogMaker log) {
if (!DEBUG_LOG_ENABLED) return;
@@ -192,6 +215,7 @@ public final class DefaultLogger extends Logger {
final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
final String type = getLogTypeString(log.getType());
+ final String subType = getLogSubTypeString(log.getSubtype());
final int smartStart = Integer.parseInt(
Objects.toString(log.getTaggedData(SMART_START), ZERO));
final int smartEnd = Integer.parseInt(
@@ -201,8 +225,9 @@ public final class DefaultLogger extends Logger {
final int eventEnd = Integer.parseInt(
Objects.toString(log.getTaggedData(EVENT_END), ZERO));
- Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
- index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model));
+ Log.d(LOG_TAG, String.format("%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
+ index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd, widget,
+ model));
}
/**
@@ -210,12 +235,16 @@ public final class DefaultLogger extends Logger {
*/
public static String createSignature(
String text, int start, int end, Context context, int modelVersion,
- @Nullable Locale locale) {
+ List<Locale> locales) {
Preconditions.checkNotNull(text);
Preconditions.checkNotNull(context);
- final String modelName = (locale != null)
- ? String.format(Locale.US, "%s_v%d", locale.toLanguageTag(), modelVersion)
- : "";
+ Preconditions.checkNotNull(locales);
+ final StringJoiner localesJoiner = new StringJoiner(",");
+ for (Locale locale : locales) {
+ localesJoiner.add(locale.toLanguageTag());
+ }
+ final String modelName = String.format(Locale.US, "%s_v%d", localesJoiner.toString(),
+ modelVersion);
final int hash = Objects.hash(text, start, end, context.getPackageName());
return SignatureParser.createSignature(CLASSIFIER_ID, modelName, hash);
}
@@ -242,9 +271,9 @@ public final class DefaultLogger extends Logger {
static String getModelName(String signature) {
Preconditions.checkNotNull(signature);
- final int start = signature.indexOf("|");
+ final int start = signature.indexOf("|") + 1;
final int end = signature.indexOf("|", start);
- if (start >= 0 && end >= start) {
+ if (start >= 1 && end >= start) {
return signature.substring(start, end);
}
return "";
diff --git a/core/java/android/view/textclassifier/logging/Logger.java b/core/java/android/view/textclassifier/logging/Logger.java
index 40e4d8ce1a77..4448b2b5b494 100644
--- a/core/java/android/view/textclassifier/logging/Logger.java
+++ b/core/java/android/view/textclassifier/logging/Logger.java
@@ -71,6 +71,7 @@ public abstract class Logger {
public static final String WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
public static final String WIDGET_UNKNOWN = "unknown";
+ private @SelectionEvent.InvocationMethod int mInvocationMethod;
private SelectionEvent mPrevEvent;
private SelectionEvent mSmartEvent;
private SelectionEvent mStartEvent;
@@ -124,16 +125,19 @@ public abstract class Logger {
/**
* Logs a "selection started" event.
*
+ * @param invocationMethod the way the selection was triggered
* @param start the token index of the selected token
*/
- public final void logSelectionStartedEvent(int start) {
+ public final void logSelectionStartedEvent(
+ @SelectionEvent.InvocationMethod int invocationMethod, int start) {
if (mConfig == null) {
return;
}
+ mInvocationMethod = invocationMethod;
logEvent(new SelectionEvent(
start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED,
- TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig));
+ TextClassifier.TYPE_UNKNOWN, mInvocationMethod, NO_SIGNATURE, mConfig));
}
/**
@@ -152,7 +156,7 @@ public abstract class Logger {
logEvent(new SelectionEvent(
start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
- TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig));
+ TextClassifier.TYPE_UNKNOWN, mInvocationMethod, NO_SIGNATURE, mConfig));
}
/**
@@ -179,7 +183,7 @@ public abstract class Logger {
final String signature = classification.getSignature();
logEvent(new SelectionEvent(
start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
- entityType, signature, mConfig));
+ entityType, mInvocationMethod, signature, mConfig));
}
/**
@@ -213,7 +217,8 @@ public abstract class Logger {
? selection.getEntity(0)
: TextClassifier.TYPE_UNKNOWN;
final String signature = selection.getSignature();
- logEvent(new SelectionEvent(start, end, eventType, entityType, signature, mConfig));
+ logEvent(new SelectionEvent(start, end, eventType, entityType, mInvocationMethod, signature,
+ mConfig));
}
/**
@@ -234,7 +239,8 @@ public abstract class Logger {
}
logEvent(new SelectionEvent(
- start, end, actionType, TextClassifier.TYPE_UNKNOWN, NO_SIGNATURE, mConfig));
+ start, end, actionType, TextClassifier.TYPE_UNKNOWN, mInvocationMethod,
+ NO_SIGNATURE, mConfig));
}
/**
@@ -265,7 +271,8 @@ public abstract class Logger {
? classification.getEntity(0)
: TextClassifier.TYPE_UNKNOWN;
final String signature = classification.getSignature();
- logEvent(new SelectionEvent(start, end, actionType, entityType, signature, mConfig));
+ logEvent(new SelectionEvent(start, end, actionType, entityType, mInvocationMethod,
+ signature, mConfig));
}
private void logEvent(@NonNull SelectionEvent event) {
diff --git a/core/java/android/view/textclassifier/logging/SelectionEvent.java b/core/java/android/view/textclassifier/logging/SelectionEvent.java
index f40b65571142..a8de3088d8cc 100644
--- a/core/java/android/view/textclassifier/logging/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/logging/SelectionEvent.java
@@ -98,6 +98,16 @@ public final class SelectionEvent {
/** Something else other than User or the default TextClassifier triggered a selection. */
public static final int EVENT_AUTO_SELECTION = 5;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({INVOCATION_MANUAL, INVOCATION_LINK})
+ public @interface InvocationMethod {}
+
+ /** Selection was invoked by the user long pressing, double tapping, or dragging to select. */
+ public static final int INVOCATION_MANUAL = 1;
+ /** Selection was invoked by the user tapping on a link. */
+ public static final int INVOCATION_LINK = 2;
+
private final int mAbsoluteStart;
private final int mAbsoluteEnd;
private final @EventType int mEventType;
@@ -105,6 +115,7 @@ public final class SelectionEvent {
@Nullable private final String mWidgetVersion;
private final String mPackageName;
private final String mWidgetType;
+ private final @InvocationMethod int mInvocationMethod;
// These fields should only be set by creator of a SelectionEvent.
private String mSignature;
@@ -121,7 +132,7 @@ public final class SelectionEvent {
SelectionEvent(
int start, int end,
@EventType int eventType, @EntityType String entityType,
- String signature, Logger.Config config) {
+ @InvocationMethod int invocationMethod, String signature, Logger.Config config) {
Preconditions.checkArgument(end >= start, "end cannot be less than start");
mAbsoluteStart = start;
mAbsoluteEnd = end;
@@ -132,6 +143,7 @@ public final class SelectionEvent {
mWidgetVersion = config.getWidgetVersion();
mPackageName = Preconditions.checkNotNull(config.getPackageName());
mWidgetType = Preconditions.checkNotNull(config.getWidgetType());
+ mInvocationMethod = invocationMethod;
}
int getAbsoluteStart() {
@@ -180,6 +192,13 @@ public final class SelectionEvent {
}
/**
+ * Returns the way the selection mode was invoked.
+ */
+ public @InvocationMethod int getInvocationMethod() {
+ return mInvocationMethod;
+ }
+
+ /**
* Returns the signature of the text classifier result associated with this event.
*/
public String getSignature() {
diff --git a/core/java/android/webkit/TracingConfig.java b/core/java/android/webkit/TracingConfig.java
index 75e2bf7026c5..68badecaec3a 100644
--- a/core/java/android/webkit/TracingConfig.java
+++ b/core/java/android/webkit/TracingConfig.java
@@ -21,61 +21,76 @@ import android.annotation.NonNull;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
/**
* Holds tracing configuration information and predefined settings.
*/
public class TracingConfig {
- private final String mCustomCategoryPattern;
- private final @PresetCategories int mPresetCategories;
+ private @PredefinedCategories int mPredefinedCategories;
+ private final List<String> mCustomIncludedCategories = new ArrayList<String>();
private @TracingMode int mTracingMode;
/** @hide */
- @IntDef({CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER, CATEGORIES_INPUT_LATENCY,
- CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING, CATEGORIES_FRAME_VIEWER})
+ @IntDef(flag = true, value = {CATEGORIES_NONE, CATEGORIES_WEB_DEVELOPER,
+ CATEGORIES_INPUT_LATENCY, CATEGORIES_RENDERING, CATEGORIES_JAVASCRIPT_AND_RENDERING,
+ CATEGORIES_FRAME_VIEWER})
@Retention(RetentionPolicy.SOURCE)
- public @interface PresetCategories {}
+ public @interface PredefinedCategories {}
/**
- * Indicates that there are no preset categories.
+ * Indicates that there are no predefined categories.
*/
- public static final int CATEGORIES_NONE = -1;
+ public static final int CATEGORIES_NONE = 0;
/**
- * Predefined categories typically useful for web developers.
+ * Predefined set of categories, includes all categories enabled by default in chromium.
+ * Use with caution: this setting may produce large trace output.
+ */
+ public static final int CATEGORIES_ALL = 1 << 0;
+
+ /**
+ * Predefined set of categories typically useful for analyzing WebViews.
+ * Typically includes android_webview and Java.
+ */
+ public static final int CATEGORIES_ANDROID_WEBVIEW = 1 << 1;
+
+ /**
+ * Predefined set of categories typically useful for web developers.
* Typically includes blink, compositor, renderer.scheduler and v8 categories.
*/
- public static final int CATEGORIES_WEB_DEVELOPER = 0;
+ public static final int CATEGORIES_WEB_DEVELOPER = 1 << 2;
/**
- * Predefined categories for analyzing input latency issues.
+ * Predefined set of categories for analyzing input latency issues.
* Typically includes input, renderer.scheduler categories.
*/
- public static final int CATEGORIES_INPUT_LATENCY = 1;
+ public static final int CATEGORIES_INPUT_LATENCY = 1 << 3;
/**
- * Predefined categories for analyzing rendering issues.
+ * Predefined set of categories for analyzing rendering issues.
* Typically includes blink, compositor and gpu categories.
*/
- public static final int CATEGORIES_RENDERING = 2;
+ public static final int CATEGORIES_RENDERING = 1 << 4;
/**
- * Predefined categories for analyzing javascript and rendering issues.
- * Typically includes blink, compositor, gpu, renderer.schduler and v8 categories.
+ * Predefined set of categories for analyzing javascript and rendering issues.
+ * Typically includes blink, compositor, gpu, renderer.scheduler and v8 categories.
*/
- public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 3;
+ public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 1 << 5;
/**
- * Predefined categories for studying difficult rendering performance problems.
+ * Predefined set of categories for studying difficult rendering performance problems.
* Typically includes blink, compositor, gpu, renderer.scheduler, v8 and
* some other compositor categories which are disabled by default.
*/
- public static final int CATEGORIES_FRAME_VIEWER = 4;
+ public static final int CATEGORIES_FRAME_VIEWER = 1 << 6;
/** @hide */
- @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER,
- RECORD_TO_CONSOLE})
+ @IntDef({RECORD_UNTIL_FULL, RECORD_CONTINUOUSLY, RECORD_UNTIL_FULL_LARGE_BUFFER})
@Retention(RetentionPolicy.SOURCE)
public @interface TracingMode {}
@@ -97,99 +112,38 @@ public class TracingConfig {
/**
* Record trace events using a larger internal tracing buffer until it is full.
- * Uses more memory than the other modes and may not be suitable on devices
- * with smaller RAM. Depending on the implementation typically allows up to
- * 512 million events to be stored.
+ * Uses significantly more memory than {@link #RECORD_UNTIL_FULL} and may not be
+ * suitable on devices with smaller RAM.
*/
public static final int RECORD_UNTIL_FULL_LARGE_BUFFER = 2;
/**
- * Record trace events to console (logcat). The events are discarded and nothing
- * is sent back to the caller. Uses the least memory as compared to the other modes.
- */
- public static final int RECORD_TO_CONSOLE = 3;
-
- /**
- * Create config with the preset categories.
- * <p>
- * Example:
- * TracingConfig(CATEGORIES_WEB_DEVELOPER) -- records trace events from the "web developer"
- * preset categories.
- *
- * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER},
- * {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING},
- * {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
- * {@link #CATEGORIES_FRAME_VIEWER}.
- *
- * Note: for specifying custom categories without presets use
- * {@link #TracingConfig(int, String, int)}.
- *
+ * @hide
*/
- public TracingConfig(@PresetCategories int presetCategories) {
- this(presetCategories, "", RECORD_UNTIL_FULL);
+ public TracingConfig(@PredefinedCategories int predefinedCategories,
+ @NonNull List<String> customIncludedCategories,
+ @TracingMode int tracingMode) {
+ mPredefinedCategories = predefinedCategories;
+ mCustomIncludedCategories.addAll(customIncludedCategories);
+ mTracingMode = tracingMode;
}
/**
- * Create a configuration with both preset categories and custom categories.
- * Also allows to specify the tracing mode.
- *
- * Note that the categories are defined by the currently-in-use version of WebView. They live
- * in chromium code and are not part of the Android API. See
- * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
- * chromium documentation on tracing</a> for more details.
- *
- * <p>
- * Examples:
- *
- * Preset category with a specified trace mode:
- * TracingConfig(CATEGORIES_WEB_DEVELOPER, "", RECORD_UNTIL_FULL_LARGE_BUFFER);
- * Custom categories:
- * TracingConfig(CATEGORIES_NONE, "browser", RECORD_UNTIL_FULL)
- * -- records only the trace events from the "browser" category.
- * TraceConfig(CATEGORIES_NONE, "-input,-gpu", RECORD_UNTIL_FULL)
- * -- records all trace events excluding the events from the "input" and 'gpu' categories.
- * TracingConfig(CATEGORIES_NONE, "blink*,devtools*", RECORD_UNTIL_FULL)
- * -- records only the trace events matching the "blink*" and "devtools*" patterns
- * (e.g. "blink_gc" and "devtools.timeline" categories).
- *
- * Combination of preset and additional custom categories:
- * TracingConfig(CATEGORIES_WEB_DEVELOPER, "memory-infra", RECORD_CONTINUOUSLY)
- * -- records events from the "web developer" categories and events from the "memory-infra"
- * category to understand where memory is being used.
- *
- * @param presetCategories preset categories to use, one of {@link #CATEGORIES_WEB_DEVELOPER},
- * {@link #CATEGORIES_INPUT_LATENCY}, {@link #CATEGORIES_RENDERING},
- * {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
- * {@link #CATEGORIES_FRAME_VIEWER}.
- * @param customCategories a comma-delimited list of category wildcards. A category can
- * have an optional '-' prefix to make it an excluded category.
- * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL},
- * {@link #RECORD_CONTINUOUSLY}, {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}
- * or {@link #RECORD_TO_CONSOLE}.
- */
- public TracingConfig(@PresetCategories int presetCategories,
- @NonNull String customCategories, @TracingMode int tracingMode) {
- mPresetCategories = presetCategories;
- mCustomCategoryPattern = customCategories;
- mTracingMode = RECORD_UNTIL_FULL;
+ * Returns a bitmask of the predefined categories values of this configuration.
+ */
+ @PredefinedCategories
+ public int getPredefinedCategories() {
+ return mPredefinedCategories;
}
/**
- * Returns the custom category pattern for this configuration.
+ * Returns the list of included custom category patterns for this configuration.
*
- * @return empty string if no custom category pattern is specified.
+ * @return empty list if no custom category patterns are specified.
*/
@NonNull
- public String getCustomCategoryPattern() {
- return mCustomCategoryPattern;
- }
-
- /**
- * Returns the preset categories value of this configuration.
- */
- @PresetCategories
- public int getPresetCategories() {
- return mPresetCategories;
+ public List<String> getCustomIncludedCategories() {
+ return mCustomIncludedCategories;
}
/**
@@ -200,4 +154,111 @@ public class TracingConfig {
return mTracingMode;
}
+ /**
+ * Builder used to create {@link TracingConfig} objects.
+ *
+ * Examples:
+ * new TracingConfig.Builder().build()
+ * -- creates a configuration with default options: {@link #CATEGORIES_NONE},
+ * {@link #RECORD_UNTIL_FULL}.
+ * new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER).build()
+ * -- records trace events from the "web developer" predefined category sets.
+ * new TracingConfig.Builder().addCategories(CATEGORIES_RENDERING,
+ * CATEGORIES_INPUT_LATENCY).build()
+ * -- records trace events from the "rendering" and "input latency" predefined
+ * category sets.
+ * new TracingConfig.Builder().addCategories("browser").build()
+ * -- records only the trace events from the "browser" category.
+ * new TracingConfig.Builder().addCategories("blink*","renderer*").build()
+ * -- records only the trace events matching the "blink*" and "renderer*" patterns
+ * (e.g. "blink.animations", "renderer_host" and "renderer.scheduler" categories).
+ * new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER)
+ * .addCategories("disabled-by-default-v8.gc")
+ * .setTracingMode(RECORD_CONTINUOUSLY).build()
+ * -- records events from the "web developer" predefined category set and events from
+ * the "disabled-by-default-v8.gc" category to understand where garbage collection
+ * is being triggered. Uses a ring buffer for internal storage during tracing.
+ */
+ public static class Builder {
+ private @PredefinedCategories int mPredefinedCategories = CATEGORIES_NONE;
+ private final List<String> mCustomIncludedCategories = new ArrayList<String>();
+ private @TracingMode int mTracingMode = RECORD_UNTIL_FULL;
+
+ /**
+ * Default constructor for Builder.
+ */
+ public Builder() {}
+
+ /**
+ * Build {@link TracingConfig} using the current settings.
+ */
+ public TracingConfig build() {
+ return new TracingConfig(mPredefinedCategories, mCustomIncludedCategories,
+ mTracingMode);
+ }
+
+ /**
+ * Adds categories from a predefined set of categories to be included in the trace output.
+ *
+ * @param predefinedCategories list or bitmask of predefined category sets to use:
+ * {@link #CATEGORIES_NONE}, {@link #CATEGORIES_ALL},
+ * {@link #CATEGORIES_WEB_DEVELOPER}, {@link #CATEGORIES_INPUT_LATENCY},
+ * {@link #CATEGORIES_RENDERING},
+ * {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
+ * {@link #CATEGORIES_FRAME_VIEWER}.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder addCategories(@PredefinedCategories int... predefinedCategories) {
+ for (int categorySet : predefinedCategories) {
+ mPredefinedCategories |= categorySet;
+ }
+ return this;
+ }
+
+ /**
+ * Adds custom categories to be included in trace output.
+ *
+ * Note that the categories are defined by the currently-in-use version of WebView. They
+ * live in chromium code and are not part of the Android API. See
+ * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
+ * chromium documentation on tracing</a> for more details.
+ *
+ * @param categories a list of category patterns. A category pattern can contain wilcards,
+ * e.g. "blink*" or full category name e.g. "renderer.scheduler".
+ * @return The builder to facilitate chaining.
+ */
+ public Builder addCategories(String... categories) {
+ for (String category: categories) {
+ mCustomIncludedCategories.add(category);
+ }
+ return this;
+ }
+
+ /**
+ * Adds custom categories to be included in trace output.
+ *
+ * Same as {@link #addCategories(String...)} but allows to pass a Collection as a parameter.
+ *
+ * @param categories a list of category patters.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder addCategories(Collection<String> categories) {
+ mCustomIncludedCategories.addAll(categories);
+ return this;
+ }
+
+ /**
+ * Sets the tracing mode for this configuration.
+ *
+ * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL},
+ * {@link #RECORD_CONTINUOUSLY} or
+ * {@link #RECORD_UNTIL_FULL_LARGE_BUFFER}.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder setTracingMode(@TracingMode int tracingMode) {
+ mTracingMode = tracingMode;
+ return this;
+ }
+ }
+
}
diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java
index cadb8a184072..7871021a33c8 100644
--- a/core/java/android/webkit/TracingController.java
+++ b/core/java/android/webkit/TracingController.java
@@ -16,9 +16,12 @@
package android.webkit;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Handler;
+
+import java.io.OutputStream;
+import java.util.concurrent.Executor;
/**
* Manages tracing of WebViews. In particular provides functionality for the app
@@ -29,40 +32,22 @@ import android.os.Handler;
* The resulting trace data is sent back as a byte sequence in json format. This
* file can be loaded in "chrome://tracing" for further analysis.
* <p>
- * Note: All methods in this class must be called on the UI thread. All callbacks
- * are also called on the UI thread.
- * <p>
* Example usage:
* <pre class="prettyprint">
* TracingController tracingController = TracingController.getInstance();
- * tracingController.start(new TraceConfig(CATEGORIES_WEB_DEVELOPER));
+ * tracingController.start(new TraceConfig.Builder()
+ * .addCategories(CATEGORIES_WEB_DEVELOPER).build());
* [..]
- * tracingController.stopAndFlush(new TraceFileOutput("trace.json"), null);
+ * tracingController.stop(new FileOutputStream("trace.json"),
+ * Executors.newSingleThreadExecutor());
* </pre></p>
*/
public abstract class TracingController {
/**
- * Interface for capturing tracing data.
- */
- public interface TracingOutputStream {
- /**
- * Will be called to return tracing data in chunks.
- * Tracing data is returned in json format an array of bytes.
- */
- void write(byte[] chunk);
-
- /**
- * Called when tracing is finished and the data collection is over.
- * There will be no calls to #write after #complete is called.
- */
- void complete();
- }
-
- /**
* Returns the default TracingController instance. At present there is
* only one TracingController instance for all WebView instances,
- * however this restriction may be relaxed in the future.
+ * however this restriction may be relaxed in a future Android release.
*
* @return the default TracingController instance
*/
@@ -72,55 +57,38 @@ public abstract class TracingController {
}
/**
- * Starts tracing all webviews. Depeding on the trace mode in traceConfig
+ * Starts tracing all webviews. Depending on the trace mode in traceConfig
* specifies how the trace events are recorded.
*
* For tracing modes {@link TracingConfig#RECORD_UNTIL_FULL},
* {@link TracingConfig#RECORD_CONTINUOUSLY} and
* {@link TracingConfig#RECORD_UNTIL_FULL_LARGE_BUFFER} the events are recorded
* using an internal buffer and flushed to the outputStream when
- * {@link #stopAndFlush(TracingOutputStream, Handler)} is called.
+ * {@link #stop(OutputStream, Executor)} is called.
*
* @param tracingConfig configuration options to use for tracing
- * @return false if the system is already tracing, true otherwise.
+ * @throws IllegalStateException if the system is already tracing.
*/
- public abstract boolean start(TracingConfig tracingConfig);
+ public abstract void start(@NonNull TracingConfig tracingConfig);
/**
- * Stops tracing and discards all tracing data.
+ * Stops tracing and flushes tracing data to the specified outputStream.
*
- * This method is particularly useful in conjunction with the
- * {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode because tracing data is logged to
- * console and not sent to an outputStream as with
- * {@link #stopAndFlush(TracingOutputStream, Handler)}.
+ * The data is sent to the specified output stream in json format typically
+ * in chunks by invoking {@link java.io.OutputStream#write(byte[])}. On completion
+ * the {@link java.io.OutputStream#close()} method is called.
*
+ * @param outputStream the output steam the tracing data will be sent to. If null
+ * the tracing data will be discarded.
+ * @param executor the {@link java.util.concurrent.Executor} on which the
+ * outputStream #write and #close methods will be invoked.
* @return false if the system was not tracing at the time of the call, true
* otherwise.
*/
- public abstract boolean stop();
-
- /**
- * Stops tracing and flushes tracing data to the specifid outputStream.
- *
- * Note that if the {@link TracingConfig#RECORD_TO_CONSOLE} tracing mode is used
- * nothing will be sent to the outputStream and no TracingOuputStream methods will be
- * called. In that case it is more convenient to just use {@link #stop()} instead.
- *
- * @param outputStream the output steam the tracing data will be sent to.
- * @param handler the {@link android.os.Handler} on which the outputStream callbacks
- * will be invoked. If the handler is null the current thread's Looper
- * will be used.
- * @return false if the system was not tracing at the time of the call, true
- * otherwise.
- */
- public abstract boolean stopAndFlush(TracingOutputStream outputStream,
- @Nullable Handler handler);
+ public abstract boolean stop(@Nullable OutputStream outputStream,
+ @NonNull @CallbackExecutor Executor executor);
/** True if the system is tracing */
public abstract boolean isTracing();
- // TODO: consider adding getTraceBufferUsage, percentage and approx event count.
- // TODO: consider adding String getCategories(), for obtaining the actual list
- // of categories used (given that presets are ints).
-
}
diff --git a/core/java/android/webkit/TracingFileOutputStream.java b/core/java/android/webkit/TracingFileOutputStream.java
deleted file mode 100644
index 8a5fa36c2d99..000000000000
--- a/core/java/android/webkit/TracingFileOutputStream.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 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.webkit;
-
-import android.annotation.NonNull;
-
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Simple TracingOutputStream implementation which writes the trace data from
- * {@link TracingController} to a new file.
- *
- */
-public class TracingFileOutputStream implements TracingController.TracingOutputStream {
-
- private FileOutputStream mFileOutput;
-
- public TracingFileOutputStream(@NonNull String filename) throws FileNotFoundException {
- mFileOutput = new FileOutputStream(filename);
- }
-
- /**
- * Writes bytes chunk to the file.
- */
- public void write(byte[] chunk) {
- try {
- mFileOutput.write(chunk);
- } catch (IOException e) {
- onIOException(e);
- }
- }
-
- /**
- * Closes the file.
- */
- public void complete() {
- try {
- mFileOutput.close();
- } catch (IOException e) {
- onIOException(e);
- }
- }
-
- private void onIOException(IOException e) {
- throw new RuntimeException(e);
- }
-}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5ab579df5ed6..998866137dcf 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1299,6 +1299,16 @@ public class Editor {
if (mSelectionModifierCursorController != null) {
mSelectionModifierCursorController.resetTouchOffsets();
}
+
+ ensureNoSelectionIfNonSelectable();
+ }
+ }
+
+ private void ensureNoSelectionIfNonSelectable() {
+ // This could be the case if a TextLink has been tapped.
+ if (!mTextView.textCanBeSelected() && mTextView.hasSelection()) {
+ Selection.setSelection((Spannable) mTextView.getText(),
+ mTextView.length(), mTextView.length());
}
}
@@ -1382,6 +1392,8 @@ public class Editor {
// Don't leave us in the middle of a batch edit. Same as in onFocusChanged
ensureEndedBatchEdit();
+
+ ensureNoSelectionIfNonSelectable();
}
}
@@ -4174,7 +4186,7 @@ public class Editor {
primaryHorizontal,
layout.getLineTop(line),
primaryHorizontal,
- layout.getLineBottom(line) - layout.getLineBottom(line) + mHandleHeight);
+ layout.getLineBottom(line) + mHandleHeight);
}
// Take TextView's padding and scroll into account.
int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset();
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 012b918ff34e..3aae8497ba1b 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -904,6 +904,9 @@ public class GridLayout extends ViewGroup {
}
}
+ /**
+ * @hide
+ */
@Override
protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
// Apply defaults, so as to remove UNDEFINED values
@@ -919,6 +922,9 @@ public class GridLayout extends ViewGroup {
}
}
+ /**
+ * @hide
+ */
@Override
protected void onDebugDraw(Canvas canvas) {
Paint paint = new Paint();
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 7ea1f1edadf5..d32e93c7a862 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -917,7 +917,7 @@ public class LinearLayout extends ViewGroup {
// measurement on any children, we need to measure them now.
int remainingExcess = heightSize - mTotalLength
+ (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
- if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) {
+ if (skippedMeasure || totalWeight > 0.0f) {
float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
mTotalLength = 0;
@@ -1300,7 +1300,7 @@ public class LinearLayout extends ViewGroup {
// measurement on any children, we need to measure them now.
int remainingExcess = widthSize - mTotalLength
+ (mAllowInconsistentMeasurement ? 0 : usedExcessSpace);
- if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) {
+ if (skippedMeasure || totalWeight > 0.0f) {
float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 7a4c800ba15e..88365617cd6e 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -18,8 +18,10 @@ package android.widget;
import android.annotation.FloatRange;
import android.annotation.NonNull;
+import android.annotation.TestApi;
import android.annotation.UiThread;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
@@ -269,4 +271,46 @@ public final class Magnifier {
return mWindow.getContentView().findViewById(
com.android.internal.R.id.magnifier_image);
}
+
+ /**
+ * @return the content being currently displayed in the magnifier, as bitmap
+ *
+ * @hide
+ */
+ @TestApi
+ public Bitmap getContent() {
+ return mBitmap;
+ }
+
+ /**
+ * @return the position of the magnifier window relative to the screen
+ *
+ * @hide
+ */
+ @TestApi
+ public Rect getWindowPositionOnScreen() {
+ final int[] viewLocationOnScreen = new int[2];
+ mView.getLocationOnScreen(viewLocationOnScreen);
+ final int[] viewLocationInSurface = new int[2];
+ mView.getLocationInSurface(viewLocationInSurface);
+
+ final int left = mWindowCoords.x + viewLocationOnScreen[0] - viewLocationInSurface[0];
+ final int top = mWindowCoords.y + viewLocationOnScreen[1] - viewLocationInSurface[1];
+ return new Rect(left, top, left + mWindow.getWidth(), top + mWindow.getHeight());
+ }
+
+ /**
+ * @return the size of the magnifier window in dp
+ *
+ * @hide
+ */
+ @TestApi
+ public static PointF getMagnifierDefaultSize() {
+ final Resources resources = Resources.getSystem();
+ final float density = resources.getDisplayMetrics().density;
+ final PointF size = new PointF();
+ size.x = resources.getDimension(com.android.internal.R.dimen.magnifier_width) / density;
+ size.y = resources.getDimension(com.android.internal.R.dimen.magnifier_height) / density;
+ return size;
+ }
}
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 8f0d02f5d788..e4b2930a23cb 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -1,12 +1,11 @@
per-file TextView.java = siyamed@google.com
per-file TextView.java = nona@google.com
per-file TextView.java = clarabayarri@google.com
-per-file TextView.java = toki@google.com
+
per-file EditText.java = siyamed@google.com
per-file EditText.java = nona@google.com
per-file EditText.java = clarabayarri@google.com
-per-file EditText.java = toki@google.com
+
per-file Editor.java = siyamed@google.com
per-file Editor.java = nona@google.com
per-file Editor.java = clarabayarri@google.com
-per-file Editor.java = toki@google.com
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index e91db1390582..7217def3cf08 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1583,7 +1583,7 @@ public class PopupWindow {
*
* @hide
*/
- protected final boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
+ protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
final int anchorHeight = anchor.getHeight();
final int anchorWidth = anchor.getWidth();
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 5c4d4d2a7fa0..c98714799742 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -183,13 +183,17 @@ public class RadioGroup extends LinearLayout {
}
private void setCheckedId(@IdRes int id) {
+ boolean changed = id != mCheckedId;
mCheckedId = id;
+
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
- final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
- if (afm != null) {
- afm.notifyValueChanged(this);
+ if (changed) {
+ final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
+ if (afm != null) {
+ afm.notifyValueChanged(this);
+ }
}
}
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index e7a4c0246e1f..6ab09d6cbe74 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -111,7 +111,8 @@ public final class SelectionActionModeHelper {
mSelectionTracker.onOriginalSelection(
getText(mTextView),
mTextView.getSelectionStart(),
- mTextView.getSelectionEnd());
+ mTextView.getSelectionEnd(),
+ false /*isLink*/);
cancelAsyncTask();
if (skipTextClassification()) {
startSelectionActionMode(null);
@@ -134,7 +135,11 @@ public final class SelectionActionModeHelper {
* Starts Link ActionMode.
*/
public void startLinkActionModeAsync(TextLinks.TextLink textLink) {
- //TODO: tracking/logging
+ mSelectionTracker.onOriginalSelection(
+ getText(mTextView),
+ mTextView.getSelectionStart(),
+ mTextView.getSelectionEnd(),
+ true /*isLink*/);
cancelAsyncTask();
if (skipTextClassification()) {
startLinkActionMode(null);
@@ -487,7 +492,8 @@ public final class SelectionActionModeHelper {
/**
* Called when the original selection happens, before smart selection is triggered.
*/
- public void onOriginalSelection(CharSequence text, int selectionStart, int selectionEnd) {
+ public void onOriginalSelection(
+ CharSequence text, int selectionStart, int selectionEnd, boolean isLink) {
// If we abandoned a selection and created a new one very shortly after, we may still
// have a pending request to log ABANDON, which we flush here.
mDelayedLogAbandon.flush();
@@ -496,7 +502,8 @@ public final class SelectionActionModeHelper {
mOriginalEnd = mSelectionEnd = selectionEnd;
mAllowReset = false;
maybeInvalidateLogger();
- mLogger.logSelectionStarted(text, selectionStart);
+ mLogger.logSelectionStarted(text, selectionStart,
+ isLink ? SelectionEvent.INVOCATION_LINK : SelectionEvent.INVOCATION_MANUAL);
}
/**
@@ -679,7 +686,9 @@ public final class SelectionActionModeHelper {
return Logger.WIDGET_UNSELECTABLE_TEXTVIEW;
}
- public void logSelectionStarted(CharSequence text, int index) {
+ public void logSelectionStarted(
+ CharSequence text, int index,
+ @SelectionEvent.InvocationMethod int invocationMethod) {
try {
Preconditions.checkNotNull(text);
Preconditions.checkArgumentInRange(index, 0, text.length(), "index");
@@ -688,7 +697,7 @@ public final class SelectionActionModeHelper {
}
mTokenIterator.setText(mText);
mStartIndex = index;
- mLogger.logSelectionStartedEvent(0);
+ mLogger.logSelectionStartedEvent(invocationMethod, 0);
} catch (Exception e) {
// Avoid crashes due to logging.
Log.d(LOG_TAG, e.getMessage());
diff --git a/core/java/android/widget/TextInputTimePickerView.java b/core/java/android/widget/TextInputTimePickerView.java
index 0cf8faad1c57..e0261ad04f58 100644
--- a/core/java/android/widget/TextInputTimePickerView.java
+++ b/core/java/android/widget/TextInputTimePickerView.java
@@ -174,7 +174,8 @@ public class TextInputTimePickerView extends RelativeLayout {
*/
void updateTextInputValues(int localizedHour, int minute, int amOrPm, boolean is24Hour,
boolean hourFormatStartsAtZero) {
- final String format = "%d";
+ final String hourFormat = "%d";
+ final String minuteFormat = "%02d";
mIs24Hour = is24Hour;
mHourFormatStartsAtZero = hourFormatStartsAtZero;
@@ -187,8 +188,8 @@ public class TextInputTimePickerView extends RelativeLayout {
mAmPmSpinner.setSelection(1);
}
- mHourEditText.setText(String.format(format, localizedHour));
- mMinuteEditText.setText(String.format(format, minute));
+ mHourEditText.setText(String.format(hourFormat, localizedHour));
+ mMinuteEditText.setText(String.format(minuteFormat, minute));
if (mErrorShowing) {
validateInput();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5710db3ce8e0..1e02c3062d97 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -791,11 +791,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// mAutoSizeStepGranularityInPx.
private boolean mHasPresetAutoSizeValues = false;
+ // Autofill-related attributes
+ //
// Indicates whether the text was set statically or dynamically, so it can be used to
// sanitize autofill requests.
private boolean mTextSetFromXmlOrResourceId = false;
- // Resource id used to set the text - used for autofill purposes.
+ // Resource id used to set the text.
private @StringRes int mTextId = ResourceId.ID_NULL;
+ // Last value used on AFM.notifyValueChanged(), used to optimize autofill workflow by avoiding
+ // calls when the value did not change
+ private CharSequence mLastValueSentToAutofillManager;
+ //
+ // End of autofill-related attributes
/**
* Kick-start the font cache for the zygote process (to pay the cost of
@@ -5665,7 +5672,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (needEditableForNotification) {
sendAfterTextChanged((Editable) text);
} else {
- // Always notify AutoFillManager - it will return right away if autofill is disabled.
notifyAutoFillManagerAfterTextChangedIfNeeded();
}
@@ -9697,11 +9703,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return;
}
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
- if (afm != null) {
+ if (afm == null) {
+ return;
+ }
+
+ if (mLastValueSentToAutofillManager == null
+ || !mLastValueSentToAutofillManager.equals(mText)) {
if (android.view.autofill.Helper.sVerbose) {
- Log.v(LOG_TAG, "sendAfterTextChanged(): notify AFM for text=" + mText);
+ Log.v(LOG_TAG, "notifying AFM after text changed");
}
afm.notifyValueChanged(TextView.this);
+ mLastValueSentToAutofillManager = mText;
+ } else {
+ if (android.view.autofill.Helper.sVerbose) {
+ Log.v(LOG_TAG, "not notifying AFM on unchanged text");
+ }
}
}
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index b22ce5e2a6ee..f6a69d9aeb93 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -16,157 +16,214 @@
package com.android.internal.app;
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.annotation.Nullable;
+import android.animation.TimeAnimator;
import android.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
-import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.OvalShape;
import android.os.Bundle;
-import android.provider.Settings;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.MathUtils;
-import android.view.Gravity;
-import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
-import android.widget.ImageView;
public class PlatLogoActivity extends Activity {
- public static final boolean FINISH = true;
+ FrameLayout layout;
+ TimeAnimator anim;
+ PBackground bg;
- FrameLayout mLayout;
- int mTapCount;
- int mKeyCount;
- PathInterpolator mInterpolator = new PathInterpolator(0f, 0f, 0.5f, 1f);
+ private class PBackground extends Drawable {
+ private float maxRadius, radius, x, y, dp;
+ private int[] palette;
+ private int darkest;
+ private float offset;
+
+ public PBackground() {
+ randomizePalette();
+ }
+
+ /**
+ * set inner radius of "p" logo
+ */
+ public void setRadius(float r) {
+ this.radius = Math.max(48*dp, r);
+ }
+
+ /**
+ * move the "p"
+ */
+ public void setPosition(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ /**
+ * for animating the "p"
+ */
+ public void setOffset(float o) {
+ this.offset = o;
+ }
+
+ /**
+ * rough luminance calculation
+ * https://www.w3.org/TR/AERT/#color-contrast
+ */
+ public float lum(int rgb) {
+ return ((Color.red(rgb) * 299f) + (Color.green(rgb) * 587f) + (Color.blue(rgb) * 114f)) / 1000f;
+ }
+
+ /**
+ * create a random evenly-spaced color palette
+ * guaranteed to contrast!
+ */
+ public void randomizePalette() {
+ final int slots = 2 + (int)(Math.random() * 2);
+ float[] color = new float[] { (float) Math.random() * 360f, 1f, 1f };
+ palette = new int[slots];
+ darkest = 0;
+ for (int i=0; i<slots; i++) {
+ palette[i] = Color.HSVToColor(color);
+ color[0] += 360f/slots;
+ if (lum(palette[i]) < lum(palette[darkest])) darkest = i;
+ }
+
+ final StringBuilder str = new StringBuilder();
+ for (int c : palette) {
+ str.append(String.format("#%08x ", c));
+ }
+ Log.v("PlatLogoActivity", "color palette: " + str);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (dp == 0) dp = getResources().getDisplayMetrics().density;
+ final float width = canvas.getWidth();
+ final float height = canvas.getHeight();
+ if (radius == 0) {
+ setPosition(width / 2, height / 2);
+ setRadius(width / 6);
+ }
+ final float inner_w = radius * 0.667f;
+
+ final Paint paint = new Paint();
+ paint.setStrokeCap(Paint.Cap.BUTT);
+ canvas.translate(x, y);
+
+ Path p = new Path();
+ p.moveTo(-radius, height);
+ p.lineTo(-radius, 0);
+ p.arcTo(-radius, -radius, radius, radius, -180, 270, false);
+ p.lineTo(-radius, radius);
+
+ float w = Math.max(canvas.getWidth(), canvas.getHeight()) * 1.414f;
+ paint.setStyle(Paint.Style.FILL);
+
+ int i=0;
+ while (w > radius*2 + inner_w*2) {
+ paint.setColor(0xFF000000 | palette[i % palette.length]);
+ // for a slower but more complete version:
+ // paint.setStrokeWidth(w);
+ // canvas.drawPath(p, paint);
+ canvas.drawOval(-w/2, -w/2, w/2, w/2, paint);
+ w -= inner_w * (1.1f + Math.sin((i/20f + offset) * 3.14159f));
+ i++;
+ }
+
+ // the innermost circle needs to be a constant color to avoid rapid flashing
+ paint.setColor(0xFF000000 | palette[(darkest+1) % palette.length]);
+ canvas.drawOval(-radius, -radius, radius, radius, paint);
+
+ p.reset();
+ p.moveTo(-radius, height);
+ p.lineTo(-radius, 0);
+ p.arcTo(-radius, -radius, radius, radius, -180, 270, false);
+ p.lineTo(-radius + inner_w, radius);
+
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeWidth(inner_w*2);
+ paint.setColor(palette[darkest]);
+ canvas.drawPath(p, paint);
+ paint.setStrokeWidth(inner_w);
+ paint.setColor(0xFFFFFFFF);
+ canvas.drawPath(p, paint);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mLayout = new FrameLayout(this);
- setContentView(mLayout);
- }
+ layout = new FrameLayout(this);
+ setContentView(layout);
- @Override
- public void onAttachedToWindow() {
- final DisplayMetrics dm = getResources().getDisplayMetrics();
- final float dp = dm.density;
- final int size = (int)
- (Math.min(Math.min(dm.widthPixels, dm.heightPixels), 600*dp) - 100*dp);
-
- final ImageView im = new ImageView(this);
- final int pad = (int)(40*dp);
- im.setPadding(pad, pad, pad, pad);
- im.setTranslationZ(20);
- im.setScaleX(0.5f);
- im.setScaleY(0.5f);
- im.setAlpha(0f);
-
- im.setBackground(new RippleDrawable(
- ColorStateList.valueOf(0xFF776677),
- getDrawable(com.android.internal.R.drawable.platlogo),
- null));
- im.setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- final int w = view.getWidth();
- final int h = view.getHeight();
- outline.setOval((int)(w*.125), (int)(h*.125), (int)(w*.96), (int)(h*.96));
- }
- });
- im.setElevation(12f*dp);
- im.setClickable(true);
- im.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- im.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- if (mTapCount < 5) return false;
-
- final ContentResolver cr = getContentResolver();
- if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0)
- == 0) {
- // For posterity: the moment this user unlocked the easter egg
- try {
- Settings.System.putLong(cr,
- Settings.System.EGG_MODE,
- System.currentTimeMillis());
- } catch (RuntimeException e) {
- Log.e("PlatLogoActivity", "Can't write settings", e);
- }
- }
- im.post(new Runnable() {
- @Override
- public void run() {
- try {
- startActivity(new Intent(Intent.ACTION_MAIN)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
- .addCategory("com.android.internal.category.PLATLOGO"));
- } catch (ActivityNotFoundException ex) {
- Log.e("PlatLogoActivity", "No more eggs.");
- }
- if (FINISH) finish();
- }
- });
- return true;
- }
- });
- mTapCount++;
- }
- });
+ bg = new PBackground();
+ layout.setBackground(bg);
+
+ layout.setOnTouchListener(new View.OnTouchListener() {
+ final PointerCoords pc0 = new PointerCoords();
+ final PointerCoords pc1 = new PointerCoords();
- // Enable hardware keyboard input for TV compatibility.
- im.setFocusable(true);
- im.requestFocus();
- im.setOnKeyListener(new View.OnKeyListener() {
@Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode != KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
- ++mKeyCount;
- if (mKeyCount > 2) {
- if (mTapCount > 5) {
- im.performLongClick();
- } else {
- im.performClick();
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ if (event.getPointerCount() > 1) {
+ event.getPointerCoords(0, pc0);
+ event.getPointerCoords(1, pc1);
+ bg.setRadius((float) Math.hypot(pc0.x - pc1.x, pc0.y - pc1.y) / 2f);
}
- }
- return true;
- } else {
- return false;
+ break;
}
+ return true;
}
});
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ bg.randomizePalette();
- mLayout.addView(im, new FrameLayout.LayoutParams(size, size, Gravity.CENTER));
+ anim = new TimeAnimator();
+ anim.setTimeListener(
+ new TimeAnimator.TimeListener() {
+ @Override
+ public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+ bg.setOffset((float) totalTime / 60000f);
+ bg.invalidateSelf();
+ }
+ });
- im.animate().scaleX(1f).scaleY(1f).alpha(1f)
- .setInterpolator(mInterpolator)
- .setDuration(500)
- .setStartDelay(800)
- .start();
+ anim.start();
+ }
+
+ @Override
+ public void onStop() {
+ if (anim != null) {
+ anim.cancel();
+ anim = null;
+ }
+ super.onStop();
}
}
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 6bd693061a85..61aeca679303 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -56,9 +56,6 @@ public class ResolverListController {
private static final String TAG = "ResolverListController";
private static final boolean DEBUG = false;
- Object mLock = new Object();
-
- @GuardedBy("mLock")
private ResolverComparator mResolverComparator;
private boolean isComputed = false;
@@ -73,10 +70,8 @@ public class ResolverListController {
mLaunchedFromUid = launchedFromUid;
mTargetIntent = targetIntent;
mReferrerPackage = referrerPackage;
- synchronized (mLock) {
- mResolverComparator =
- new ResolverComparator(mContext, mTargetIntent, mReferrerPackage, null);
- }
+ mResolverComparator =
+ new ResolverComparator(mContext, mTargetIntent, mReferrerPackage, null);
}
@VisibleForTesting
@@ -244,29 +239,27 @@ public class ResolverListController {
@VisibleForTesting
@WorkerThread
public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) {
- synchronized (mLock) {
- if (mResolverComparator == null) {
- Log.d(TAG, "Comparator has already been destroyed; skipped.");
- return;
+ if (mResolverComparator == null) {
+ Log.d(TAG, "Comparator has already been destroyed; skipped.");
+ return;
+ }
+ try {
+ long beforeRank = System.currentTimeMillis();
+ if (!isComputed) {
+ final CountDownLatch finishComputeSignal = new CountDownLatch(1);
+ ComputeCallback callback = new ComputeCallback(finishComputeSignal);
+ mResolverComparator.setCallBack(callback);
+ mResolverComparator.compute(inputList);
+ finishComputeSignal.await();
+ isComputed = true;
}
- final CountDownLatch finishComputeSignal = new CountDownLatch(1);
- ComputeCallback callback = new ComputeCallback(finishComputeSignal);
- mResolverComparator.setCallBack(callback);
- try {
- long beforeRank = System.currentTimeMillis();
- if (!isComputed) {
- mResolverComparator.compute(inputList);
- finishComputeSignal.await();
- isComputed = true;
- }
- Collections.sort(inputList, mResolverComparator);
- long afterRank = System.currentTimeMillis();
- if (DEBUG) {
- Log.d(TAG, "Time Cost: " + Long.toString(afterRank - beforeRank));
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "Compute & Sort was interrupted: " + e);
+ Collections.sort(inputList, mResolverComparator);
+ long afterRank = System.currentTimeMillis();
+ if (DEBUG) {
+ Log.d(TAG, "Time Cost: " + Long.toString(afterRank - beforeRank));
}
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Compute & Sort was interrupted: " + e);
}
}
@@ -287,36 +280,18 @@ public class ResolverListController {
@VisibleForTesting
public float getScore(ResolverActivity.DisplayResolveInfo target) {
- synchronized (mLock) {
- if (mResolverComparator == null) {
- return 0.0f;
- }
- return mResolverComparator.getScore(target.getResolvedComponentName());
- }
+ return mResolverComparator.getScore(target.getResolvedComponentName());
}
public void updateModel(ComponentName componentName) {
- synchronized (mLock) {
- if (mResolverComparator != null) {
- mResolverComparator.updateModel(componentName);
- }
- }
+ mResolverComparator.updateModel(componentName);
}
public void updateChooserCounts(String packageName, int userId, String action) {
- synchronized (mLock) {
- if (mResolverComparator != null) {
- mResolverComparator.updateChooserCounts(packageName, userId, action);
- }
- }
+ mResolverComparator.updateChooserCounts(packageName, userId, action);
}
public void destroy() {
- synchronized (mLock) {
- if (mResolverComparator != null) {
- mResolverComparator.destroy();
- }
- mResolverComparator = null;
- }
+ mResolverComparator.destroy();
}
}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 543bd0c4913d..b049db379341 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -16,6 +16,7 @@
package com.android.internal.backup;
+import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
@@ -98,6 +99,7 @@ public class LocalTransport extends BackupTransport {
private FileInputStream mCurFullRestoreStream;
private FileOutputStream mFullRestoreSocketStream;
private byte[] mFullRestoreBuffer;
+ private final LocalTransportParameters mParameters;
private void makeDataDirs() {
mCurrentSetDir.mkdirs();
@@ -105,11 +107,16 @@ public class LocalTransport extends BackupTransport {
mCurrentSetIncrementalDir.mkdir();
}
- public LocalTransport(Context context) {
+ public LocalTransport(Context context, LocalTransportParameters parameters) {
mContext = context;
+ mParameters = parameters;
makeDataDirs();
}
+ LocalTransportParameters getParameters() {
+ return mParameters;
+ }
+
@Override
public String name() {
return new ComponentName(mContext, this.getClass()).flattenToShortString();
@@ -143,6 +150,17 @@ public class LocalTransport extends BackupTransport {
}
@Override
+ public int getTransportFlags() {
+ int flags = super.getTransportFlags();
+ // Testing for a fake flag and having it set as a boolean in settings prevents anyone from
+ // using this it to pull data from the agent
+ if (mParameters.isFakeEncryptionFlag()) {
+ flags |= BackupAgent.FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED;
+ }
+ return flags;
+ }
+
+ @Override
public long requestBackupTime() {
// any time is a good time for local backup
return 0;
diff --git a/core/java/com/android/internal/backup/LocalTransportParameters.java b/core/java/com/android/internal/backup/LocalTransportParameters.java
new file mode 100644
index 000000000000..390fae96f810
--- /dev/null
+++ b/core/java/com/android/internal/backup/LocalTransportParameters.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 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.backup;
+
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+class LocalTransportParameters {
+ private static final String TAG = "LocalTransportParams";
+ private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS;
+ private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag";
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+ private final ContentObserver mObserver;
+ private final ContentResolver mResolver;
+ private boolean mFakeEncryptionFlag;
+
+ LocalTransportParameters(Handler handler, ContentResolver resolver) {
+ mObserver = new Observer(handler);
+ mResolver = resolver;
+ }
+
+ /** Observes for changes in the setting. This method MUST be paired with {@link #stop()}. */
+ void start() {
+ mResolver.registerContentObserver(Settings.Secure.getUriFor(SETTING), false, mObserver);
+ update();
+ }
+
+ /** Stop observing for changes in the setting. */
+ void stop() {
+ mResolver.unregisterContentObserver(mObserver);
+ }
+
+ boolean isFakeEncryptionFlag() {
+ return mFakeEncryptionFlag;
+ }
+
+ private void update() {
+ String parameters = "";
+ try {
+ parameters = Settings.Secure.getString(mResolver, SETTING);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Malformed " + SETTING + " setting: " + e.getMessage());
+ }
+ mParser.setString(parameters);
+ mFakeEncryptionFlag = mParser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false);
+ }
+
+ private class Observer extends ContentObserver {
+ private Observer(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ update();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/backup/LocalTransportService.java b/core/java/com/android/internal/backup/LocalTransportService.java
index 77ac31332522..69c48e2a48cf 100644
--- a/core/java/com/android/internal/backup/LocalTransportService.java
+++ b/core/java/com/android/internal/backup/LocalTransportService.java
@@ -26,8 +26,16 @@ public class LocalTransportService extends Service {
@Override
public void onCreate() {
if (sTransport == null) {
- sTransport = new LocalTransport(this);
+ LocalTransportParameters parameters =
+ new LocalTransportParameters(getMainThreadHandler(), getContentResolver());
+ sTransport = new LocalTransport(this, parameters);
}
+ sTransport.getParameters().start();
+ }
+
+ @Override
+ public void onDestroy() {
+ sTransport.getParameters().stop();
}
@Override
diff --git a/core/java/com/android/internal/car/ICarServiceHelper.aidl b/core/java/com/android/internal/car/ICarServiceHelper.aidl
deleted file mode 100644
index 9ee330be060b..000000000000
--- a/core/java/com/android/internal/car/ICarServiceHelper.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.car;
-
-/**
- * Helper API for car service. Only for itneraction between system server and car service.
- * @hide
- */
-interface ICarServiceHelper {
-}
diff --git a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java
index 500c028ed4c6..bf151c39271a 100644
--- a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java
+++ b/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java
@@ -57,6 +57,8 @@ public class GradientDrawable extends Drawable {
private int mMainColor;
private int mSecondaryColor;
private ValueAnimator mColorAnimation;
+ private int mMainColorTo;
+ private int mSecondaryColorTo;
public GradientDrawable(@NonNull Context context) {
mDensity = context.getResources().getDisplayMetrics().density;
@@ -76,7 +78,7 @@ public class GradientDrawable extends Drawable {
}
public void setColors(int mainColor, int secondaryColor, boolean animated) {
- if (mainColor == mMainColor && secondaryColor == mSecondaryColor) {
+ if (mainColor == mMainColorTo && secondaryColor == mSecondaryColorTo) {
return;
}
@@ -84,6 +86,9 @@ public class GradientDrawable extends Drawable {
mColorAnimation.cancel();
}
+ mMainColorTo = mainColor;
+ mSecondaryColorTo = mainColor;
+
if (animated) {
final int mainFrom = mMainColor;
final int secFrom = mSecondaryColor;
diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java
index 9b7383fcbe8a..7b25a0691c40 100644
--- a/core/java/com/android/internal/colorextraction/types/Tonal.java
+++ b/core/java/com/android/internal/colorextraction/types/Tonal.java
@@ -405,12 +405,13 @@ public class Tonal implements ExtractionType {
return v - (float) Math.floor(v);
}
- static class TonalPalette {
- final float[] h;
- final float[] s;
- final float[] l;
- final float minHue;
- final float maxHue;
+ @VisibleForTesting
+ public static class TonalPalette {
+ public final float[] h;
+ public final float[] s;
+ public final float[] l;
+ public final float minHue;
+ public final float maxHue;
TonalPalette(float[] h, float[] s, float[] l) {
if (h.length != s.length || s.length != l.length) {
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 4a181b27b2e3..44adbb22eb7e 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -49,6 +49,7 @@ public class SystemNotificationChannels {
public static String USB = "USB";
public static String FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
+ public static String SYSTEM_CHANGES = "SYSTEM_CHANGES";
public static void createAll(Context context) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -152,6 +153,11 @@ public class SystemNotificationChannels {
.build());
channelsList.add(heavyWeightChannel);
+ NotificationChannel systemChanges = new NotificationChannel(SYSTEM_CHANGES,
+ context.getString(R.string.notification_channel_system_changes),
+ NotificationManager.IMPORTANCE_LOW);
+ channelsList.add(systemChanges);
+
nm.createNotificationChannels(channelsList);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 40dcf25bbd10..4e515918a0cd 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -187,7 +187,7 @@ public class BatteryStatsImpl extends BatteryStats {
public final AtomicFile mCheckinFile;
public final AtomicFile mDailyFile;
- static final int MSG_UPDATE_WAKELOCKS = 1;
+ static final int MSG_REPORT_CPU_UPDATE_NEEDED = 1;
static final int MSG_REPORT_POWER_CHANGE = 2;
static final int MSG_REPORT_CHARGING = 3;
static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
@@ -230,6 +230,13 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting
protected final SparseIntArray mPendingUids = new SparseIntArray();
+ @GuardedBy("this")
+ private long mNumCpuTimeReads;
+ @GuardedBy("this")
+ private long mNumBatchedCpuTimeReads;
+ @GuardedBy("this")
+ private long mCpuTimeReadsTrackingStartTime = SystemClock.uptimeMillis();
+
/** Container for Resource Power Manager stats. Updated by updateRpmStatsLocked. */
private final RpmStats mTmpRpmStats = new RpmStats();
/** The soonest the RPM stats can be updated after it was last updated. */
@@ -273,10 +280,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void handleMessage(Message msg) {
BatteryCallback cb = mCallback;
switch (msg.what) {
- case MSG_UPDATE_WAKELOCKS:
- synchronized (BatteryStatsImpl.this) {
- updateCpuTimeLocked();
- }
+ case MSG_REPORT_CPU_UPDATE_NEEDED:
if (cb != null) {
cb.batteryNeedsCpuUpdate();
}
@@ -302,6 +306,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ public void postBatteryNeedsCpuUpdateMsg() {
+ mHandler.sendEmptyMessage(MSG_REPORT_CPU_UPDATE_NEEDED);
+ }
+
/**
* Update per-freq cpu times for all the uids in {@link #mPendingUids}.
*/
@@ -484,9 +492,14 @@ public class BatteryStatsImpl extends BatteryStats {
Future<?> scheduleSync(String reason, int flags);
Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
- Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff);
+ Future<?> scheduleReadProcStateCpuTimes(boolean onBattery, boolean onBatteryScreenOff,
+ long delayMillis);
Future<?> scheduleCopyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff);
Future<?> scheduleCpuSyncDueToSettingChange();
+ Future<?> scheduleCpuSyncDueToScreenStateChange(boolean onBattery,
+ boolean onBatteryScreenOff);
+ Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis);
+ void cancelCpuSyncDueToWakelockChange();
}
public Handler mHandler;
@@ -678,7 +691,8 @@ public class BatteryStatsImpl extends BatteryStats {
StopwatchTimer mCameraOnTimer;
int mGpsSignalQualityBin = -1;
- final StopwatchTimer[] mGpsSignalQualityTimer =
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected final StopwatchTimer[] mGpsSignalQualityTimer =
new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
int mPhoneSignalStrengthBin = -1;
@@ -760,6 +774,8 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected StopwatchTimer mBluetoothScanTimer;
+ boolean mIsCellularTxPowerHigh = false;
+
int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
long mMobileRadioActiveStartTime;
StopwatchTimer mMobileRadioActiveTimer;
@@ -1450,12 +1466,10 @@ public class BatteryStatsImpl extends BatteryStats {
long mCount;
long mLoadedCount;
long mUnpluggedCount;
- long mPluggedCount;
LongSamplingCounter(TimeBase timeBase, Parcel in) {
mTimeBase = timeBase;
- mPluggedCount = in.readLong();
- mCount = mPluggedCount;
+ mCount = in.readLong();
mLoadedCount = in.readLong();
mUnpluggedCount = in.readLong();
timeBase.add(this);
@@ -1474,16 +1488,15 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
- mUnpluggedCount = mPluggedCount;
+ mUnpluggedCount = mCount;
}
@Override
public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
- mPluggedCount = mCount;
}
public long getCountLocked(int which) {
- long val = mTimeBase.isRunning() ? mCount : mPluggedCount;
+ long val = mCount;
if (which == STATS_SINCE_UNPLUGGED) {
val -= mUnpluggedCount;
} else if (which != STATS_SINCE_CHARGED) {
@@ -1496,12 +1509,15 @@ public class BatteryStatsImpl extends BatteryStats {
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCount=" + mCount
+ " mLoadedCount=" + mLoadedCount
- + " mUnpluggedCount=" + mUnpluggedCount
- + " mPluggedCount=" + mPluggedCount);
+ + " mUnpluggedCount=" + mUnpluggedCount);
}
void addCountLocked(long count) {
- if (mTimeBase.isRunning()) {
+ addCountLocked(count, mTimeBase.isRunning());
+ }
+
+ void addCountLocked(long count, boolean isRunning) {
+ if (isRunning) {
mCount += count;
}
}
@@ -1511,7 +1527,7 @@ public class BatteryStatsImpl extends BatteryStats {
*/
void reset(boolean detachIfReset) {
mCount = 0;
- mLoadedCount = mPluggedCount = mUnpluggedCount = 0;
+ mLoadedCount = mUnpluggedCount = 0;
if (detachIfReset) {
detach();
}
@@ -1528,7 +1544,7 @@ public class BatteryStatsImpl extends BatteryStats {
void readSummaryFromParcelLocked(Parcel in) {
mLoadedCount = in.readLong();
mCount = mLoadedCount;
- mUnpluggedCount = mPluggedCount = mLoadedCount;
+ mUnpluggedCount = mLoadedCount;
}
}
@@ -3522,7 +3538,7 @@ public class BatteryStatsImpl extends BatteryStats {
mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
}
- void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+ void addHistoryBufferLocked(long elapsedRealtimeMs, HistoryItem cur) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
@@ -3603,8 +3619,8 @@ public class BatteryStatsImpl extends BatteryStats {
} else if (dataSize >= MAX_HISTORY_BUFFER) {
if (!mHistoryOverflow) {
mHistoryOverflow = true;
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
return;
}
@@ -3642,7 +3658,7 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
return;
}
@@ -3650,15 +3666,14 @@ public class BatteryStatsImpl extends BatteryStats {
// The history is currently empty; we need it to start with a time stamp.
cur.currentTime = System.currentTimeMillis();
if (recordResetDueToOverflow) {
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_RESET, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur);
}
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
}
- private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd,
- HistoryItem cur) {
+ private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) {
if (mIteratingHistory) {
throw new IllegalStateException("Can't do this while iterating history!");
}
@@ -3692,17 +3707,17 @@ public class BatteryStatsImpl extends BatteryStats {
mHistoryAddTmp.wakeReasonTag = null;
mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
- addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp);
+ addHistoryRecordInnerLocked(wakeElapsedTime, mHistoryAddTmp);
}
}
mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs;
mTrackRunningHistoryUptime = uptimeMs;
- addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
+ addHistoryRecordInnerLocked(elapsedRealtimeMs, mHistoryCur);
}
- void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
+ void addHistoryRecordInnerLocked(long elapsedRealtimeMs, HistoryItem cur) {
+ addHistoryBufferLocked(elapsedRealtimeMs, cur);
if (!USE_OLD_HISTORY) {
return;
@@ -3743,7 +3758,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (mNumHistoryItems == MAX_HISTORY_ITEMS
|| mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
- addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW, cur);
}
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
@@ -3760,7 +3775,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
}
public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
@@ -3826,6 +3841,7 @@ public class BatteryStatsImpl extends BatteryStats {
mActiveHistoryStates2 = 0xffffffff;
}
+ @GuardedBy("this")
public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
long realtime) {
final boolean screenOff = !isScreenOn(screenState);
@@ -3849,9 +3865,6 @@ public class BatteryStatsImpl extends BatteryStats {
+ Display.stateToString(screenState)
+ " and battery is " + (unplugged ? "on" : "off"));
}
- updateCpuTimeLocked();
- mExternalSync.scheduleCopyFromAllUidsCpuTimes(mOnBatteryTimeBase.isRunning(),
- mOnBatteryScreenOffTimeBase.isRunning());
mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime);
if (updateOnBatteryTimeBase) {
@@ -3903,6 +3916,7 @@ public class BatteryStatsImpl extends BatteryStats {
* This should only be called after the cpu times have been read.
* @see #scheduleRemoveIsolatedUidLocked(int, int)
*/
+ @GuardedBy("this")
public void removeIsolatedUidLocked(int isolatedUid) {
StatsLog.write(
StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1),
@@ -4139,15 +4153,11 @@ public class BatteryStatsImpl extends BatteryStats {
}
private void requestWakelockCpuUpdate() {
- if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
- Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
- mHandler.sendMessageDelayed(m, DELAY_UPDATE_WAKELOCKS);
- }
+ mExternalSync.scheduleCpuSyncDueToWakelockChange(DELAY_UPDATE_WAKELOCKS);
}
private void requestImmediateCpuUpdate() {
- mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
- mHandler.sendEmptyMessage(MSG_UPDATE_WAKELOCKS);
+ mExternalSync.scheduleCpuSyncDueToWakelockChange(0 /* delayMillis */);
}
public void setRecordAllHistoryLocked(boolean enabled) {
@@ -4550,7 +4560,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public boolean startAddingCpuLocked() {
- mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
+ mExternalSync.cancelCpuSyncDueToWakelockChange();
return mOnBatteryInternal;
}
@@ -4732,6 +4742,7 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
+ @GuardedBy("this")
public void noteScreenStateLocked(int state) {
state = mPretendScreenOff ? Display.STATE_OFF : state;
@@ -4802,6 +4813,8 @@ public class BatteryStatsImpl extends BatteryStats {
+ Display.stateToString(state));
addHistoryRecordLocked(elapsedRealtime, uptime);
}
+ mExternalSync.scheduleCpuSyncDueToScreenStateChange(
+ mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning());
if (isScreenOn(state)) {
updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
@@ -5281,6 +5294,18 @@ public class BatteryStatsImpl extends BatteryStats {
case TelephonyManager.NETWORK_TYPE_HSPAP:
bin = DATA_CONNECTION_HSPAP;
break;
+ case TelephonyManager.NETWORK_TYPE_GSM:
+ bin = DATA_CONNECTION_GSM;
+ break;
+ case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+ bin = DATA_CONNECTION_TD_SCDMA;
+ break;
+ case TelephonyManager.NETWORK_TYPE_IWLAN:
+ bin = DATA_CONNECTION_IWLAN;
+ break;
+ case TelephonyManager.NETWORK_TYPE_LTE_CA:
+ bin = DATA_CONNECTION_LTE_CA;
+ break;
default:
bin = DATA_CONNECTION_OTHER;
break;
@@ -9191,8 +9216,14 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void addCpuTimeLocked(int utime, int stime) {
- mUserTime += utime;
- mSystemTime += stime;
+ addCpuTimeLocked(utime, stime, mBsi.mOnBatteryTimeBase.isRunning());
+ }
+
+ public void addCpuTimeLocked(int utime, int stime, boolean isRunning) {
+ if (isRunning) {
+ mUserTime += utime;
+ mSystemTime += stime;
+ }
}
public void addForegroundTimeLocked(long ttime) {
@@ -9648,6 +9679,7 @@ public class BatteryStatsImpl extends BatteryStats {
return ps;
}
+ @GuardedBy("mBsi")
public void updateUidProcessStateLocked(int procState) {
int uidRunningState;
// Make special note of Foreground Services
@@ -9670,7 +9702,11 @@ public class BatteryStatsImpl extends BatteryStats {
if (mBsi.mPendingUids.size() == 0) {
mBsi.mExternalSync.scheduleReadProcStateCpuTimes(
mBsi.mOnBatteryTimeBase.isRunning(),
- mBsi.mOnBatteryScreenOffTimeBase.isRunning());
+ mBsi.mOnBatteryScreenOffTimeBase.isRunning(),
+ mBsi.mConstants.PROC_STATE_CPU_TIMES_READ_DELAY_MS);
+ mBsi.mNumCpuTimeReads++;
+ } else {
+ mBsi.mNumBatchedCpuTimeReads++;
}
if (mBsi.mPendingUids.indexOfKey(mUid) < 0
|| ArrayUtils.contains(CRITICAL_PROC_STATES, mProcessState)) {
@@ -9931,8 +9967,6 @@ public class BatteryStatsImpl extends BatteryStats {
if (wl != null) {
StopwatchTimer wlt = getWakelockTimerLocked(wl, type);
wlt.stopRunningLocked(elapsedRealtimeMs);
- if (!wlt.isRunningLocked()) { // only tell statsd if truly stopped
- }
}
if (type == WAKE_TYPE_PARTIAL) {
if (mAggregatedPartialWakelockTimer != null) {
@@ -11206,6 +11240,25 @@ public class BatteryStatsImpl extends BatteryStats {
private ModemActivityInfo mLastModemActivityInfo =
new ModemActivityInfo(0, 0, 0, new int[0], 0, 0);
+ private ModemActivityInfo getDeltaModemActivityInfo(ModemActivityInfo activityInfo) {
+ if (activityInfo == null) {
+ return null;
+ }
+ int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+ for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+ txTimeMs[i] = activityInfo.getTxTimeMillis()[i]
+ - mLastModemActivityInfo.getTxTimeMillis()[i];
+ }
+ ModemActivityInfo deltaInfo = new ModemActivityInfo(activityInfo.getTimestamp(),
+ activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(),
+ activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(),
+ txTimeMs,
+ activityInfo.getRxTimeMillis() - mLastModemActivityInfo.getRxTimeMillis(),
+ activityInfo.getEnergyUsed() - mLastModemActivityInfo.getEnergyUsed());
+ mLastModemActivityInfo = activityInfo;
+ return deltaInfo;
+ }
+
/**
* Distribute Cell radio energy info and network traffic to apps.
*/
@@ -11213,6 +11266,10 @@ public class BatteryStatsImpl extends BatteryStats {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
+ ModemActivityInfo deltaInfo = getDeltaModemActivityInfo(activityInfo);
+
+ // Add modem tx power to history.
+ addModemTxPowerToHistory(deltaInfo);
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
@@ -11226,22 +11283,6 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- int rxTimeMs = 0;
- int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
- int idleTimeMs = 0;
- int sleepTimeMs = 0;
- if (activityInfo != null) {
- rxTimeMs = activityInfo.getRxTimeMillis() - mLastModemActivityInfo.getRxTimeMillis();
- for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
- txTimeMs[i] = activityInfo.getTxTimeMillis()[i]
- - mLastModemActivityInfo.getTxTimeMillis()[i];
- }
- idleTimeMs =
- activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis();
- sleepTimeMs =
- activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis();
- }
-
synchronized (this) {
if (!mOnBatteryInternal) {
if (delta != null) {
@@ -11250,14 +11291,14 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
- if (activityInfo != null) {
+ if (deltaInfo != null) {
mHasModemReporting = true;
mModemActivity.getIdleTimeCounter().addCountLocked(
- idleTimeMs);
- mModemActivity.getRxTimeCounter().addCountLocked(rxTimeMs);
+ deltaInfo.getIdleTimeMillis());
+ mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getRxTimeMillis());
for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
mModemActivity.getTxTimeCounters()[lvl]
- .addCountLocked(txTimeMs[lvl]);
+ .addCountLocked(deltaInfo.getTxTimeMillis()[lvl]);
}
// POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -11265,16 +11306,17 @@ public class BatteryStatsImpl extends BatteryStats {
PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
if (opVolt != 0) {
double energyUsed =
- sleepTimeMs *
+ deltaInfo.getSleepTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP)
- + idleTimeMs *
+ + deltaInfo.getIdleTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
- + rxTimeMs *
+ + deltaInfo.getRxTimeMillis() *
mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
+ int[] txTimeMs = deltaInfo.getTxTimeMillis();
for (int i = 0; i < Math.min(txTimeMs.length,
- SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) {
+ SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) {
energyUsed += txTimeMs[i] * mPowerProfile.getAveragePower(
- PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+ PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
}
// We store the power drain as mAms.
@@ -11350,11 +11392,11 @@ public class BatteryStatsImpl extends BatteryStats {
radioTime -= appRadioTime;
totalPackets -= appPackets;
- if (activityInfo != null) {
+ if (deltaInfo != null) {
ControllerActivityCounterImpl activityCounter =
u.getOrCreateModemControllerActivityLocked();
if (totalRxPackets > 0 && entry.rxPackets > 0) {
- final long rxMs = (entry.rxPackets * rxTimeMs)
+ final long rxMs = (entry.rxPackets * deltaInfo.getRxTimeMillis())
/ totalRxPackets;
activityCounter.getRxTimeCounter().addCountLocked(rxMs);
}
@@ -11362,7 +11404,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (totalTxPackets > 0 && entry.txPackets > 0) {
for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
long txMs =
- entry.txPackets * txTimeMs[lvl];
+ entry.txPackets * deltaInfo.getTxTimeMillis()[lvl];
txMs /= totalTxPackets;
activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
}
@@ -11388,6 +11430,44 @@ public class BatteryStatsImpl extends BatteryStats {
new BluetoothActivityEnergyInfo(0, 0, 0, 0, 0, 0);
/**
+ * Add modem tx power to history
+ * Device is said to be in high cellular transmit power when it has spent most of the transmit
+ * time at the highest power level.
+ * @param activityInfo
+ */
+ private void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) {
+ if (activityInfo == null) {
+ return;
+ }
+ int[] txTimeMs = activityInfo.getTxTimeMillis();
+ if (txTimeMs == null || txTimeMs.length != ModemActivityInfo.TX_POWER_LEVELS) {
+ return;
+ }
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+ int levelMaxTimeSpent = 0;
+ for (int i = 1; i < txTimeMs.length; i++) {
+ if (txTimeMs[i] > txTimeMs[levelMaxTimeSpent]) {
+ levelMaxTimeSpent = i;
+ }
+ }
+ if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) {
+ if (!mIsCellularTxPowerHigh) {
+ mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mIsCellularTxPowerHigh = true;
+ }
+ return;
+ }
+ if (mIsCellularTxPowerHigh) {
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mIsCellularTxPowerHigh = false;
+ }
+ return;
+ }
+
+ /**
* Distribute Bluetooth energy info and network traffic to apps.
* @param info The energy information from the bluetooth controller.
*/
@@ -11716,12 +11796,24 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
+ public boolean isOnBatteryLocked() {
+ return mOnBatteryTimeBase.isRunning();
+ }
+
+ public boolean isOnBatteryScreenOffLocked() {
+ return mOnBatteryScreenOffTimeBase.isRunning();
+ }
+
/**
* Read and distribute CPU usage across apps. If their are partial wakelocks being held
* and we are on battery with screen off, we give more of the cpu time to those apps holding
* wakelocks. If the screen is on, we just assign the actual cpu time an app used.
+ * It's possible this will be invoked after the internal battery/screen states are updated, so
+ * passing the appropriate battery/screen states to try attribute the cpu times to correct
+ * buckets.
*/
- public void updateCpuTimeLocked() {
+ @GuardedBy("this")
+ public void updateCpuTimeLocked(boolean onBattery, boolean onBatteryScreenOff) {
if (mPowerProfile == null) {
return;
}
@@ -11738,7 +11830,7 @@ public class BatteryStatsImpl extends BatteryStats {
// usually holding the wakelock on behalf of an app.
// And Only distribute cpu power to wakelocks if the screen is off and we're on battery.
ArrayList<StopwatchTimer> partialTimersToConsider = null;
- if (mOnBatteryScreenOffTimeBase.isRunning()) {
+ if (onBatteryScreenOff) {
partialTimersToConsider = new ArrayList<>();
for (int i = mPartialTimers.size() - 1; i >= 0; --i) {
final StopwatchTimer timer = mPartialTimers.get(i);
@@ -11756,7 +11848,7 @@ public class BatteryStatsImpl extends BatteryStats {
// When the battery is not on, we don't attribute the cpu times to any timers but we still
// need to take the snapshots.
- if (!mOnBatteryInternal) {
+ if (!onBattery) {
mKernelUidCpuTimeReader.readDelta(null);
mKernelUidCpuFreqTimeReader.readDelta(null);
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
@@ -11772,16 +11864,16 @@ public class BatteryStatsImpl extends BatteryStats {
mUserInfoProvider.refreshUserIds();
final SparseLongArray updatedUids = mKernelUidCpuFreqTimeReader.perClusterTimesAvailable()
? null : new SparseLongArray();
- readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids);
+ readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids, onBattery);
// updatedUids=null means /proc/uid_time_in_state provides snapshots of per-cluster cpu
// freqs, so no need to approximate these values.
if (updatedUids != null) {
- updateClusterSpeedTimes(updatedUids);
+ updateClusterSpeedTimes(updatedUids, onBattery);
}
- readKernelUidCpuFreqTimesLocked(partialTimersToConsider);
+ readKernelUidCpuFreqTimesLocked(partialTimersToConsider, onBattery, onBatteryScreenOff);
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
- readKernelUidCpuActiveTimesLocked();
- readKernelUidCpuClusterTimesLocked();
+ readKernelUidCpuActiveTimesLocked(onBattery);
+ readKernelUidCpuClusterTimesLocked(onBattery);
}
}
@@ -11821,7 +11913,7 @@ public class BatteryStatsImpl extends BatteryStats {
* @param updatedUids The uids for which times spent at different frequencies are calculated.
*/
@VisibleForTesting
- public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids) {
+ public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids, boolean onBattery) {
long totalCpuClustersTimeMs = 0;
// Read the time spent for each cluster at various cpu frequencies.
final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][];
@@ -11863,7 +11955,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
cpuSpeeds[speed].addCountLocked(appCpuTimeUs
* clusterSpeedTimesMs[cluster][speed]
- / totalCpuClustersTimeMs);
+ / totalCpuClustersTimeMs, onBattery);
}
}
}
@@ -11880,7 +11972,7 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@VisibleForTesting
public void readKernelUidCpuTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers,
- @Nullable SparseLongArray updatedUids) {
+ @Nullable SparseLongArray updatedUids, boolean onBattery) {
mTempTotalCpuUserTimeUs = mTempTotalCpuSystemTimeUs = 0;
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
final long startTimeMs = mClocks.uptimeMillis();
@@ -11931,8 +12023,8 @@ public class BatteryStatsImpl extends BatteryStats {
Slog.d(TAG, sb.toString());
}
- u.mUserCpuTime.addCountLocked(userTimeUs);
- u.mSystemCpuTime.addCountLocked(systemTimeUs);
+ u.mUserCpuTime.addCountLocked(userTimeUs, onBattery);
+ u.mSystemCpuTime.addCountLocked(systemTimeUs, onBattery);
if (updatedUids != null) {
updatedUids.put(u.getUid(), userTimeUs + systemTimeUs);
}
@@ -11964,15 +12056,15 @@ public class BatteryStatsImpl extends BatteryStats {
Slog.d(TAG, sb.toString());
}
- timer.mUid.mUserCpuTime.addCountLocked(userTimeUs);
- timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs);
+ timer.mUid.mUserCpuTime.addCountLocked(userTimeUs, onBattery);
+ timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs, onBattery);
if (updatedUids != null) {
final int uid = timer.mUid.getUid();
updatedUids.put(uid, updatedUids.get(uid, 0) + userTimeUs + systemTimeUs);
}
final Uid.Proc proc = timer.mUid.getProcessStatsLocked("*wakelock*");
- proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000);
+ proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000, onBattery);
mTempTotalCpuUserTimeUs -= userTimeUs;
mTempTotalCpuSystemTimeUs -= systemTimeUs;
@@ -11987,7 +12079,8 @@ public class BatteryStatsImpl extends BatteryStats {
* @param partialTimers The wakelock holders among which the cpu freq times will be distributed.
*/
@VisibleForTesting
- public void readKernelUidCpuFreqTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers) {
+ public void readKernelUidCpuFreqTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers,
+ boolean onBattery, boolean onBatteryScreenOff) {
final boolean perClusterTimesAvailable =
mKernelUidCpuFreqTimeReader.perClusterTimesAvailable();
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
@@ -12010,13 +12103,13 @@ public class BatteryStatsImpl extends BatteryStats {
if (u.mCpuFreqTimeMs == null || u.mCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
}
- u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs);
+ u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBattery);
if (u.mScreenOffCpuFreqTimeMs == null ||
u.mScreenOffCpuFreqTimeMs.getSize() != cpuFreqTimeMs.length) {
u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray(
mOnBatteryScreenOffTimeBase);
}
- u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs);
+ u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs, onBatteryScreenOff);
if (perClusterTimesAvailable) {
if (u.mCpuClusterSpeedTimesUs == null ||
@@ -12052,7 +12145,7 @@ public class BatteryStatsImpl extends BatteryStats {
} else {
appAllocationUs = cpuFreqTimeMs[freqIndex] * 1000;
}
- cpuTimesUs[speed].addCountLocked(appAllocationUs);
+ cpuTimesUs[speed].addCountLocked(appAllocationUs, onBattery);
freqIndex++;
}
}
@@ -12086,7 +12179,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
final long allocationUs =
mWakeLockAllocationsUs[cluster][speed] / (numWakelocks - i);
- cpuTimeUs[speed].addCountLocked(allocationUs);
+ cpuTimeUs[speed].addCountLocked(allocationUs, onBattery);
mWakeLockAllocationsUs[cluster][speed] -= allocationUs;
}
}
@@ -12099,7 +12192,7 @@ public class BatteryStatsImpl extends BatteryStats {
* counters.
*/
@VisibleForTesting
- public void readKernelUidCpuActiveTimesLocked() {
+ public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
final long startTimeMs = mClocks.uptimeMillis();
mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> {
uid = mapUid(uid);
@@ -12114,7 +12207,7 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
final Uid u = getUidStatsLocked(uid);
- u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs);
+ u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs, onBattery);
});
final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
@@ -12128,7 +12221,7 @@ public class BatteryStatsImpl extends BatteryStats {
* counters.
*/
@VisibleForTesting
- public void readKernelUidCpuClusterTimesLocked() {
+ public void readKernelUidCpuClusterTimesLocked(boolean onBattery) {
final long startTimeMs = mClocks.uptimeMillis();
mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> {
uid = mapUid(uid);
@@ -12143,7 +12236,7 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
final Uid u = getUidStatsLocked(uid);
- u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs);
+ u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs, onBattery);
});
final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
@@ -12166,6 +12259,7 @@ public class BatteryStatsImpl extends BatteryStats {
return false;
}
+ @GuardedBy("this")
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) {
boolean doWrite = false;
@@ -12307,7 +12401,7 @@ public class BatteryStatsImpl extends BatteryStats {
boolean reset) {
mRecordingHistory = true;
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs,
+ addHistoryBufferLocked(elapsedRealtimeMs,
reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
mHistoryCur);
mHistoryCur.currentTime = 0;
@@ -12320,8 +12414,7 @@ public class BatteryStatsImpl extends BatteryStats {
final long uptimeMs) {
if (mRecordingHistory) {
mHistoryCur.currentTime = currentTime;
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
- mHistoryCur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_CURRENT_TIME, mHistoryCur);
mHistoryCur.currentTime = 0;
}
}
@@ -12329,8 +12422,7 @@ public class BatteryStatsImpl extends BatteryStats {
private void recordShutdownLocked(final long elapsedRealtimeMs, final long uptimeMs) {
if (mRecordingHistory) {
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_SHUTDOWN,
- mHistoryCur);
+ addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_SHUTDOWN, mHistoryCur);
mHistoryCur.currentTime = 0;
}
}
@@ -12344,6 +12436,7 @@ public class BatteryStatsImpl extends BatteryStats {
// This should probably be exposed in the API, though it's not critical
public static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0
+ @GuardedBy("this")
public void setBatteryStateLocked(final int status, final int health, final int plugType,
final int level, /* not final */ int temp, final int volt, final int chargeUAh,
final int chargeFullUAh) {
@@ -12353,9 +12446,7 @@ public class BatteryStatsImpl extends BatteryStats {
reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null,
status, plugType, level, temp);
- final boolean onBattery =
- plugType == BATTERY_PLUGGED_NONE &&
- status != BatteryManager.BATTERY_STATUS_UNKNOWN;
+ final boolean onBattery = isOnBattery(plugType, status);
final long uptime = mClocks.uptimeMillis();
final long elapsedRealtime = mClocks.elapsedRealtime();
if (!mHaveBatteryLevel) {
@@ -12545,6 +12636,10 @@ public class BatteryStatsImpl extends BatteryStats {
mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh);
}
+ public static boolean isOnBattery(int plugType, int status) {
+ return plugType == BATTERY_PLUGGED_NONE && status != BatteryManager.BATTERY_STATUS_UNKNOWN;
+ }
+
// Inform StatsLog of setBatteryState changes.
// If this is the first reporting, pass in recentPast == null.
private void reportChangesToStatsLog(HistoryItem recentPast,
@@ -13089,15 +13184,19 @@ public class BatteryStatsImpl extends BatteryStats {
= "track_cpu_active_cluster_time";
public static final String KEY_READ_BINARY_CPU_TIME
= "read_binary_cpu_time";
+ public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS
+ = "proc_state_cpu_times_read_delay_ms";
private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false;
+ private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
// Not used right now.
public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME;
+ public long PROC_STATE_CPU_TIMES_READ_DELAY_MS = DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -13137,7 +13236,9 @@ public class BatteryStatsImpl extends BatteryStats {
KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
READ_BINARY_CPU_TIME = mParser.getBoolean(
KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME);
-
+ updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS,
+ mParser.getLong(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS,
+ DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS));
}
}
@@ -13146,6 +13247,19 @@ public class BatteryStatsImpl extends BatteryStats {
if (isEnabled && !wasEnabled) {
mKernelSingleUidTimeReader.markDataAsStale(true);
mExternalSync.scheduleCpuSyncDueToSettingChange();
+
+ mNumCpuTimeReads = 0;
+ mNumBatchedCpuTimeReads = 0;
+ mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis();
+ }
+ }
+
+ private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) {
+ PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis;
+ if (oldDelayMillis != newDelayMillis) {
+ mNumCpuTimeReads = 0;
+ mNumBatchedCpuTimeReads = 0;
+ mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis();
}
}
@@ -13156,9 +13270,12 @@ public class BatteryStatsImpl extends BatteryStats {
pw.println(TRACK_CPU_ACTIVE_CLUSTER_TIME);
pw.print(KEY_READ_BINARY_CPU_TIME); pw.print("=");
pw.println(READ_BINARY_CPU_TIME);
+ pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("=");
+ pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS);
}
}
+ @GuardedBy("this")
public void dumpConstantsLocked(PrintWriter pw) {
mConstants.dumpLocked(pw);
}
@@ -13274,7 +13391,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (USE_OLD_HISTORY) {
addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
}
- addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
+ addHistoryBufferLocked(elapsedRealtime, HistoryItem.CMD_START, mHistoryCur);
startRecordingHistory(elapsedRealtime, uptime, false);
}
@@ -13542,6 +13659,7 @@ public class BatteryStatsImpl extends BatteryStats {
mCameraOnTimer.readSummaryFromParcelLocked(in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer.readSummaryFromParcelLocked(in);
+ mIsCellularTxPowerHigh = false;
int NRPMS = in.readInt();
if (NRPMS > 10000) {
@@ -14478,6 +14596,7 @@ public class BatteryStatsImpl extends BatteryStats {
mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase, in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase, in);
+ mIsCellularTxPowerHigh = false;
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
@@ -14862,5 +14981,11 @@ public class BatteryStatsImpl extends BatteryStats {
mCameraOnTimer.logState(pr, " ");
}
super.dumpLocked(context, pw, flags, reqUid, histStart);
+ pw.print("Total cpu time reads: ");
+ pw.println(mNumCpuTimeReads);
+ pw.print("Batched cpu time reads: ");
+ pw.println(mNumBatchedCpuTimeReads);
+ pw.print("Batching Duration (min): ");
+ pw.println((mClocks.uptimeMillis() - mCpuTimeReadsTrackingStartTime) / (60 * 1000));
}
}
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index 088e7268b984..12405ebce057 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -283,6 +283,7 @@ public class FuseAppLoop implements Handler.Callback {
return -OsConstants.EBADF;
}
+ @GuardedBy("mLock")
private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException {
final CallbackEntry entry = mCallbackMap.get(checkInode(inode));
if (entry == null) {
@@ -291,12 +292,14 @@ public class FuseAppLoop implements Handler.Callback {
return entry;
}
+ @GuardedBy("mLock")
private void recycleLocked(Args args) {
if (mArgsPool.size() < ARGS_POOL_SIZE) {
mArgsPool.add(args);
}
}
+ @GuardedBy("mLock")
private void replySimpleLocked(long unique, int result) {
if (mInstance != 0) {
native_replySimple(mInstance, unique, result);
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index ebeb24c41479..42839171dc53 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -16,6 +16,7 @@
package com.android.internal.os;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.internal.os.KernelUidCpuFreqTimeReader.UID_TIMES_PROC_FILE;
import android.annotation.NonNull;
import android.util.Slog;
@@ -54,6 +55,12 @@ public class KernelSingleUidTimeReader {
private boolean mSingleUidCpuTimesAvailable = true;
@GuardedBy("this")
private boolean mHasStaleData;
+ // We use the freq count obtained from /proc/uid_time_in_state to decide how many longs
+ // to read from each /proc/uid/<uid>/time_in_state. On the first read, verify if this is
+ // correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will
+ // indicate whether we checked for validity or not.
+ @GuardedBy("this")
+ private boolean mCpuFreqsCountVerified;
private final Injector mInjector;
@@ -82,15 +89,15 @@ public class KernelSingleUidTimeReader {
final String procFile = new StringBuilder(PROC_FILE_DIR)
.append(uid)
.append(PROC_FILE_NAME).toString();
- final long[] cpuTimesMs = new long[mCpuFreqsCount];
+ final long[] cpuTimesMs;
try {
final byte[] data = mInjector.readData(procFile);
+ if (!mCpuFreqsCountVerified) {
+ verifyCpuFreqsCount(data.length, procFile);
+ }
final ByteBuffer buffer = ByteBuffer.wrap(data);
buffer.order(ByteOrder.nativeOrder());
- for (int i = 0; i < mCpuFreqsCount; ++i) {
- // Times read will be in units of 10ms
- cpuTimesMs[i] = buffer.getLong() * 10;
- }
+ cpuTimesMs = readCpuTimesFromByteBuffer(buffer);
} catch (Exception e) {
if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) {
mSingleUidCpuTimesAvailable = false;
@@ -103,6 +110,27 @@ public class KernelSingleUidTimeReader {
}
}
+ private void verifyCpuFreqsCount(int numBytes, String procFile) {
+ final int actualCount = (numBytes / Long.BYTES);
+ if (mCpuFreqsCount != actualCount) {
+ mSingleUidCpuTimesAvailable = false;
+ throw new IllegalStateException("Freq count didn't match,"
+ + "count from " + UID_TIMES_PROC_FILE + "=" + mCpuFreqsCount + ", but"
+ + "count from " + procFile + "=" + actualCount);
+ }
+ mCpuFreqsCountVerified = true;
+ }
+
+ private long[] readCpuTimesFromByteBuffer(ByteBuffer buffer) {
+ final long[] cpuTimesMs;
+ cpuTimesMs = new long[mCpuFreqsCount];
+ for (int i = 0; i < mCpuFreqsCount; ++i) {
+ // Times read will be in units of 10ms
+ cpuTimesMs[i] = buffer.getLong() * 10;
+ }
+ return cpuTimesMs;
+ }
+
/**
* Compute and return cpu times delta of an uid using previously read cpu times and
* {@param latestCpuTimesMs}.
diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
index b8982cce91b5..d97538c30498 100644
--- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
@@ -49,7 +49,7 @@ import java.io.IOException;
public class KernelUidCpuFreqTimeReader {
private static final boolean DEBUG = false;
private static final String TAG = "KernelUidCpuFreqTimeReader";
- private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
+ static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
public interface Callback {
void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs);
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
index 65615c0ffb02..444049e7e415 100644
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
@@ -78,10 +78,11 @@ public class KernelUidCpuTimeReader {
final long userTimeUs = Long.parseLong(splitter.next(), 10);
final long systemTimeUs = Long.parseLong(splitter.next(), 10);
+ boolean notifyCallback = false;
+ long userTimeDeltaUs = userTimeUs;
+ long systemTimeDeltaUs = systemTimeUs;
// Only report if there is a callback and if this is not the first read.
if (callback != null && mLastTimeReadUs != 0) {
- long userTimeDeltaUs = userTimeUs;
- long systemTimeDeltaUs = systemTimeUs;
int index = mLastUserTimeUs.indexOfKey(uid);
if (index >= 0) {
userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
@@ -114,12 +115,13 @@ public class KernelUidCpuTimeReader {
}
}
- if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) {
- callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
- }
+ notifyCallback = (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0);
}
mLastUserTimeUs.put(uid, userTimeUs);
mLastSystemTimeUs.put(uid, systemTimeUs);
+ if (notifyCallback) {
+ callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
+ }
}
} catch (IOException e) {
Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 66475e445efa..bb5a0ad86dd4 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -71,10 +71,11 @@ public class RuntimeInit {
public void uncaughtException(Thread t, Throwable e) {
// Don't re-enter if KillApplicationHandler has already run
if (mCrashing) return;
- if (mApplicationObject == null) {
- // The "FATAL EXCEPTION" string is still used on Android even though
- // apps can set a custom UncaughtExceptionHandler that renders uncaught
- // exceptions non-fatal.
+
+ // mApplicationObject is null for non-zygote java programs (e.g. "am")
+ // There are also apps running with the system UID. We don't want the
+ // first clause in either of these two cases, only for system_server.
+ if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
StringBuilder message = new StringBuilder();
@@ -229,7 +230,7 @@ public class RuntimeInit {
* @param argv Argument vector for main()
* @param classLoader the classLoader to load {@className} with
*/
- private static Runnable findStaticMain(String className, String[] argv,
+ protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index cadb66ae6e08..b38c851e5101 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -129,7 +129,7 @@ class WebViewZygoteInit {
final Runnable caller;
try {
- sServer.registerServerSocket("webview_zygote");
+ sServer.registerServerSocketFromEnv("webview_zygote");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index e69a36064693..28a7c1204071 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -53,8 +53,8 @@ public final class Zygote {
public static final int DISABLE_VERIFIER = 1 << 9;
/** Only use oat files located in /system. Otherwise use dex/jar/apk . */
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
- /** Do not enfore hidden API access restrictions. */
- public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
+ /** Do enfore hidden API access restrictions. */
+ public static final int ENABLE_HIDDEN_API_CHECKS = 1 << 11;
/** Force generation of native debugging information for backtraces. */
public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
@@ -69,6 +69,13 @@ public final class Zygote {
private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
+ /**
+ * An extraArg passed when a zygote process is forking a child-zygote, specifying a name
+ * in the abstract socket namespace. This socket name is what the new child zygote
+ * should listen for connections on.
+ */
+ public static final String CHILD_ZYGOTE_SOCKET_NAME_ARG = "--zygote-socket=";
+
private Zygote() {}
/** Called for some security initialization before any fork. */
@@ -100,6 +107,8 @@ public final class Zygote {
* @param fdsToIgnore null-ok an array of ints, either null or holding
* one or more POSIX file descriptor numbers that are to be ignored
* in the file descriptor table check.
+ * @param startChildZygote if true, the new child process will itself be a
+ * new zygote process.
* @param instructionSet null-ok the instruction set to use.
* @param appDataDir null-ok the data directory of the app.
*
@@ -108,13 +117,13 @@ public final class Zygote {
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, String instructionSet, String appDataDir) {
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
- fdsToIgnore, instructionSet, appDataDir);
+ fdsToIgnore, startChildZygote, instructionSet, appDataDir);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -128,7 +137,7 @@ public final class Zygote {
native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, String instructionSet, String appDataDir);
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir);
/**
* Called to do any initialization before starting an application.
@@ -160,9 +169,6 @@ public final class Zygote {
*/
public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
- // SystemServer is always allowed to use hidden APIs.
- runtimeFlags |= DISABLE_HIDDEN_API_CHECKS;
-
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
@@ -191,8 +197,8 @@ public final class Zygote {
native protected static void nativeUnmountStorageOnInit();
private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
- String instructionSet) {
- VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, instructionSet);
+ boolean isZygote, String instructionSet) {
+ VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
}
/**
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 6a87b1f4d3fd..a32fb4316d12 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -221,8 +221,8 @@ class ZygoteConnection {
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
- parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
- parsedArgs.appDataDir);
+ parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
+ parsedArgs.instructionSet, parsedArgs.appDataDir);
try {
if (pid == 0) {
@@ -233,7 +233,8 @@ class ZygoteConnection {
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
- return handleChildProc(parsedArgs, descriptors, childPipeFd);
+ return handleChildProc(parsedArgs, descriptors, childPipeFd,
+ parsedArgs.startChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
@@ -415,6 +416,14 @@ class ZygoteConnection {
boolean preloadDefault;
/**
+ * Whether this is a request to start a zygote process as a child of this zygote.
+ * Set with --start-child-zygote. The remaining arguments must include the
+ * CHILD_ZYGOTE_SOCKET_NAME_ARG flag to indicate the abstract socket name that
+ * should be used for communication.
+ */
+ boolean startChildZygote;
+
+ /**
* Constructs instance and parses args
* @param args zygote command-line args
* @throws IllegalArgumentException
@@ -565,6 +574,8 @@ class ZygoteConnection {
preloadPackageCacheKey = args[++curArg];
} else if (arg.equals("--preload-default")) {
preloadDefault = true;
+ } else if (arg.equals("--start-child-zygote")) {
+ startChildZygote = true;
} else {
break;
}
@@ -587,6 +598,20 @@ class ZygoteConnection {
remainingArgs = new String[args.length - curArg];
System.arraycopy(args, curArg, remainingArgs, 0, remainingArgs.length);
}
+
+ if (startChildZygote) {
+ boolean seenChildSocketArg = false;
+ for (String arg : remainingArgs) {
+ if (arg.startsWith(Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG)) {
+ seenChildSocketArg = true;
+ break;
+ }
+ }
+ if (!seenChildSocketArg) {
+ throw new IllegalArgumentException("--start-child-zygote specified " +
+ "without " + Zygote.CHILD_ZYGOTE_SOCKET_NAME_ARG);
+ }
+ }
}
}
@@ -739,9 +764,10 @@ class ZygoteConnection {
* @param parsedArgs non-null; zygote args
* @param descriptors null-ok; new file descriptors for stdio if available.
* @param pipeFd null-ok; pipe for communication back to Zygote.
+ * @param isZygote whether this new child process is itself a new Zygote.
*/
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
- FileDescriptor pipeFd) {
+ FileDescriptor pipeFd, boolean isZygote) {
/**
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
@@ -778,8 +804,13 @@ class ZygoteConnection {
// Should not get here.
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
- return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
- null /* classLoader */);
+ if (!isZygote) {
+ return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
+ null /* classLoader */);
+ } else {
+ return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
+ parsedArgs.remainingArgs, null /* classLoader */);
+ }
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 74802c8bdf79..c5d0a04b5deb 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -98,10 +98,6 @@ public class ZygoteInit {
private static final String SOCKET_NAME_ARG = "--socket-name=";
- /* Dexopt flag to disable hidden API access checks when dexopting SystemServer.
- * Must be kept in sync with com.android.server.pm.Installer. */
- private static final int DEXOPT_DISABLE_HIDDEN_API_CHECKS = 1 << 10;
-
/**
* Used to pre-load resources.
*/
@@ -569,10 +565,7 @@ public class ZygoteInit {
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
final String packageName = "*";
final String outputPath = null;
- // Dexopt with a flag which lifts restrictions on hidden API usage.
- // Offending methods would otherwise be re-verified at runtime and
- // we want to avoid the performance overhead of that.
- final int dexFlags = DEXOPT_DISABLE_HIDDEN_API_CHECKS;
+ final int dexFlags = 0;
final String compilerFilter = systemServerFilter;
final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
final String seInfo = null;
@@ -583,7 +576,8 @@ public class ZygoteInit {
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
uuid, classLoaderContext, seInfo, false /* downgrade */,
- targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null);
+ targetSdkVersion, /*profileName*/ null, /*dexMetadataPath*/ null,
+ "server-dexopt");
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
@@ -762,7 +756,7 @@ public class ZygoteInit {
throw new RuntimeException("No ABI list supplied.");
}
- zygoteServer.registerServerSocket(socketName);
+ zygoteServer.registerServerSocketFromEnv(socketName);
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
@@ -877,5 +871,16 @@ public class ZygoteInit {
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
+ /**
+ * The main function called when starting a child zygote process. This is used as an
+ * alternative to zygoteInit(), which skips calling into initialization routines that
+ * start the Binder threadpool.
+ */
+ static final Runnable childZygoteInit(
+ int targetSdkVersion, String[] argv, ClassLoader classLoader) {
+ RuntimeInit.Arguments args = new RuntimeInit.Arguments(argv);
+ return RuntimeInit.findStaticMain(args.startClass, args.startArgs, classLoader);
+ }
+
private static final native void nativeZygoteInit();
}
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 8baa15a058de..fecf9b9da5dd 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -44,9 +44,21 @@ class ZygoteServer {
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
+ /**
+ * Listening socket that accepts new server connections.
+ */
private LocalServerSocket mServerSocket;
/**
+ * Whether or not mServerSocket's underlying FD should be closed directly.
+ * If mServerSocket is created with an existing FD, closing the socket does
+ * not close the FD and it must be closed explicitly. If the socket is created
+ * with a name instead, then closing the socket will close the underlying FD
+ * and it should not be double-closed.
+ */
+ private boolean mCloseSocketFd;
+
+ /**
* Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}.
*/
private boolean mIsForkChild;
@@ -59,11 +71,12 @@ class ZygoteServer {
}
/**
- * Registers a server socket for zygote command connections
+ * Registers a server socket for zygote command connections. This locates the server socket
+ * file descriptor through an ANDROID_SOCKET_ environment variable.
*
* @throws RuntimeException when open fails
*/
- void registerServerSocket(String socketName) {
+ void registerServerSocketFromEnv(String socketName) {
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
@@ -78,6 +91,7 @@ class ZygoteServer {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
mServerSocket = new LocalServerSocket(fd);
+ mCloseSocketFd = true;
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
@@ -86,6 +100,22 @@ class ZygoteServer {
}
/**
+ * Registers a server socket for zygote command connections. This opens the server socket
+ * at the specified name in the abstract socket namespace.
+ */
+ void registerServerSocketAtAbstractName(String socketName) {
+ if (mServerSocket == null) {
+ try {
+ mServerSocket = new LocalServerSocket(socketName);
+ mCloseSocketFd = false;
+ } catch (IOException ex) {
+ throw new RuntimeException(
+ "Error binding to abstract socket '" + socketName + "'", ex);
+ }
+ }
+ }
+
+ /**
* Waits for and accepts a single command connection. Throws
* RuntimeException on failure.
*/
@@ -112,7 +142,7 @@ class ZygoteServer {
if (mServerSocket != null) {
FileDescriptor fd = mServerSocket.getFileDescriptor();
mServerSocket.close();
- if (fd != null) {
+ if (fd != null && mCloseSocketFd) {
Os.close(fd);
}
}
@@ -219,6 +249,11 @@ class ZygoteServer {
Log.e(TAG, "Caught post-fork exception in child process.", e);
throw e;
}
+ } finally {
+ // Reset the child flag, in the event that the child process is a child-
+ // zygote. The flag will not be consulted this loop pass after the Runnable
+ // is returned.
+ mIsForkChild = false;
}
}
}
diff --git a/core/java/com/android/internal/print/DumpUtils.java b/core/java/com/android/internal/print/DumpUtils.java
index 1916c11e9c9d..f44a1d122f39 100644
--- a/core/java/com/android/internal/print/DumpUtils.java
+++ b/core/java/com/android/internal/print/DumpUtils.java
@@ -213,10 +213,9 @@ public class DumpUtils {
PrintAttributes.MediaSize mediaSize = attributes.getMediaSize();
if (mediaSize != null) {
writeMediaSize(context, proto, "media_size", PrintAttributesProto.MEDIA_SIZE, mediaSize);
+ proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
}
- proto.write("is_portrait", PrintAttributesProto.IS_PORTRAIT, attributes.isPortrait());
-
PrintAttributes.Resolution res = attributes.getResolution();
if (res != null) {
writeResolution(proto, "resolution", PrintAttributesProto.RESOLUTION, res);
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f70d3c2a27fd..221bf8823b74 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -35,7 +35,7 @@ oneway interface IStatusBar
void animateCollapsePanels();
void togglePanel();
- void showChargingAnimation(int batteryLevel);
+ void showWirelessChargingAnimation(int batteryLevel);
/**
* Notifies the status bar of a System UI visibility flag change.
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
new file mode 100644
index 000000000000..21d750c59a4e
--- /dev/null
+++ b/core/java/com/android/internal/util/OWNERS
@@ -0,0 +1,24 @@
+per-file AsyncChannel*=lorenzo@google.com
+per-file AsyncChannel*=satk@google.com
+per-file AsyncChannel*=silberst@google.com
+per-file BitUtils*=ek@google.com
+per-file BitUtils*=lorenzo@google.com
+per-file BitUtils*=satk@google.com
+per-file MessageUtils*=ek@google.com
+per-file MessageUtils*=lorenzo@google.com
+per-file MessageUtils*=satk@google.com
+per-file Protocol*=ek@google.com
+per-file Protocol*=lorenzo@google.com
+per-file Protocol*=quiche@google.com
+per-file Protocol*=satk@google.com
+per-file Protocol*=silberst@google.com
+per-file RingBuffer*=ek@google.com
+per-file RingBuffer*=lorenzo@google.com
+per-file RingBuffer*=satk@google.com
+per-file State*=ek@google.com
+per-file State*=lorenzo@google.com
+per-file State*=quiche@google.com
+per-file State*=silberst@google.com
+per-file TokenBucket*=ek@google.com
+per-file TokenBucket*=lorenzo@google.com
+per-file TokenBucket*=satk@google.com
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index b53459e072d4..67dc81af5895 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -551,6 +551,7 @@ public class MenuBuilder implements Menu {
mPreventDispatchingItemsChanged = true;
clear();
clearHeader();
+ mPresenters.clear();
mPreventDispatchingItemsChanged = false;
mItemsChangedWhileDispatchPrevented = false;
onItemsChanged(true);
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
new file mode 100644
index 000000000000..71d3bb5d6b5c
--- /dev/null
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 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.Nullable;
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A class to extract Bitmaps from a MessagingStyle message.
+ */
+public class LocalImageResolver {
+
+ private static final int MAX_SAFE_ICON_SIZE_PX = 480;
+
+ @Nullable
+ public static Drawable resolveImage(Uri uri, Context context) throws IOException {
+ BitmapFactory.Options onlyBoundsOptions = getBoundsOptionsForImage(uri, context);
+ if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
+ return null;
+ }
+
+ int originalSize =
+ (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth)
+ ? onlyBoundsOptions.outHeight
+ : onlyBoundsOptions.outWidth;
+
+ double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX)
+ ? (originalSize / MAX_SAFE_ICON_SIZE_PX)
+ : 1.0;
+
+ BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
+ bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
+ InputStream input = context.getContentResolver().openInputStream(uri);
+ Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
+ input.close();
+ return new BitmapDrawable(context.getResources(), bitmap);
+ }
+
+ private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context)
+ throws IOException {
+ InputStream input = context.getContentResolver().openInputStream(uri);
+ BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
+ onlyBoundsOptions.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
+ input.close();
+ return onlyBoundsOptions;
+ }
+
+ private static int getPowerOfTwoForSampleRatio(double ratio) {
+ int k = Integer.highestOneBit((int) Math.floor(ratio));
+ return Math.max(1, k);
+ }
+}
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 5577d6e86fee..239beaa220e0 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -22,9 +22,12 @@ import android.annotation.Nullable;
import android.annotation.StyleRes;
import android.app.Notification;
import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Pools;
import android.view.LayoutInflater;
import android.view.View;
@@ -61,6 +64,11 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
private boolean mIsHidingAnimated;
private boolean mNeedsGeneratedAvatar;
private Notification.Person mSender;
+ private boolean mAvatarsAtEnd;
+ private ViewGroup mImageContainer;
+ private MessagingImageMessage mIsolatedMessage;
+ private boolean mTransformingImages;
+ private Point mDisplaySize = new Point();
public MessagingGroup(@NonNull Context context) {
super(context);
@@ -87,6 +95,35 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
mSenderName = findViewById(R.id.message_name);
mSenderName.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
mAvatarView = findViewById(R.id.message_icon);
+ mImageContainer = findViewById(R.id.messaging_group_icon_container);
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ mDisplaySize.x = displayMetrics.widthPixels;
+ mDisplaySize.y = displayMetrics.heightPixels;
+ }
+
+ public void updateClipRect() {
+ // We want to clip to the senderName if it's available, otherwise our images will come
+ // from a weird position
+ Rect clipRect;
+ if (mSenderName.getVisibility() != View.GONE && !mTransformingImages) {
+ ViewGroup parent = (ViewGroup) mSenderName.getParent();
+ int top = getDistanceFromParent(mSenderName, parent) - getDistanceFromParent(
+ mMessageContainer, parent) + mSenderName.getHeight();
+ clipRect = new Rect(0, top, mDisplaySize.x, mDisplaySize.y);
+ } else {
+ clipRect = null;
+ }
+ mMessageContainer.setClipBounds(clipRect);
+ }
+
+ private int getDistanceFromParent(View searchedView, ViewGroup parent) {
+ int position = 0;
+ View view = searchedView;
+ while(view != parent) {
+ position += view.getTop() + view.getTranslationY();
+ view = (View) view.getParent();
+ }
+ return position;
}
public void setSender(Notification.Person sender, CharSequence nameOverride) {
@@ -129,12 +166,14 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
}
public void removeMessage(MessagingMessage messagingMessage) {
- mMessageContainer.removeView(messagingMessage);
+ ViewGroup messageParent = (ViewGroup) messagingMessage.getView().getParent();
+ messageParent.removeView(messagingMessage.getView());
Runnable recycleRunnable = () -> {
- mMessageContainer.removeTransientView(messagingMessage);
+ messageParent.removeTransientView(messagingMessage.getView());
messagingMessage.recycle();
if (mMessageContainer.getChildCount() == 0
- && mMessageContainer.getTransientViewCount() == 0) {
+ && mMessageContainer.getTransientViewCount() == 0
+ && mImageContainer.getChildCount() == 0) {
ViewParent parent = getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(MessagingGroup.this);
@@ -148,9 +187,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
}
};
if (isShown()) {
- mMessageContainer.addTransientView(messagingMessage, 0);
- performRemoveAnimation(messagingMessage, recycleRunnable);
- if (mMessageContainer.getChildCount() == 0) {
+ messageParent.addTransientView(messagingMessage.getView(), 0);
+ performRemoveAnimation(messagingMessage.getView(), recycleRunnable);
+ if (mMessageContainer.getChildCount() == 0
+ && mImageContainer.getChildCount() == 0) {
removeGroupAnimated(null);
}
} else {
@@ -160,12 +200,8 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
}
private void removeGroupAnimated(Runnable endAction) {
- MessagingPropertyAnimator.fadeOut(mAvatarView, null);
- MessagingPropertyAnimator.startLocalTranslationTo(mAvatarView,
- (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN);
- MessagingPropertyAnimator.fadeOut(mSenderName, null);
- MessagingPropertyAnimator.startLocalTranslationTo(mSenderName,
- (int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN);
+ performRemoveAnimation(mAvatarView, null);
+ performRemoveAnimation(mSenderName, null);
boolean endActionTriggered = false;
for (int i = mMessageContainer.getChildCount() - 1; i >= 0; i--) {
View child = mMessageContainer.getChildAt(i);
@@ -182,14 +218,17 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
performRemoveAnimation(child, childEndAction);
endActionTriggered = true;
}
+ if (mIsolatedMessage != null) {
+ performRemoveAnimation(mIsolatedMessage, !endActionTriggered ? endAction : null);
+ endActionTriggered = true;
+ }
if (!endActionTriggered && endAction != null) {
endAction.run();
}
}
- public void performRemoveAnimation(View message,
- Runnable recycleRunnable) {
- MessagingPropertyAnimator.fadeOut(message, recycleRunnable);
+ public void performRemoveAnimation(View message, Runnable endAction) {
+ MessagingPropertyAnimator.fadeOut(message, endAction);
MessagingPropertyAnimator.startLocalTranslationTo(message,
(int) (-getHeight() * 0.5f), MessagingLayout.FAST_OUT_LINEAR_IN);
}
@@ -222,6 +261,9 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
}
}
}
+ if (mMessageContainer.getChildCount() == 0 && mIsolatedMessage != null) {
+ return mIsolatedMessage.getMeasuredType();
+ }
return MEASURED_NORMAL;
}
@@ -234,6 +276,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
result += ((MessagingLinearLayout.MessagingChild) child).getConsumedLines();
}
}
+ result = mIsolatedMessage != null ? Math.max(result, 1) : result;
// A group is usually taking up quite some space with the padding and the name, let's add 1
return result + 1;
}
@@ -289,26 +332,67 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
public void setMessages(List<MessagingMessage> group) {
// Let's now make sure all children are added and in the correct order
+ int textMessageIndex = 0;
+ MessagingImageMessage isolatedMessage = null;
for (int messageIndex = 0; messageIndex < group.size(); messageIndex++) {
MessagingMessage message = group.get(messageIndex);
+ message.setColor(mTextColor);
if (message.getGroup() != this) {
message.setMessagingGroup(this);
- ViewParent parent = mMessageContainer.getParent();
- if (parent instanceof ViewGroup) {
- ((ViewGroup) parent).removeView(message);
- }
- mMessageContainer.addView(message, messageIndex);
mAddedMessages.add(message);
}
- if (messageIndex != mMessageContainer.indexOfChild(message)) {
- mMessageContainer.removeView(message);
- mMessageContainer.addView(message, messageIndex);
+ boolean isImage = message instanceof MessagingImageMessage;
+ if (mAvatarsAtEnd && isImage) {
+ isolatedMessage = (MessagingImageMessage) message;
+ } else {
+ if (removeFromParentIfDifferent(message, mMessageContainer)) {
+ ViewGroup.LayoutParams layoutParams = message.getView().getLayoutParams();
+ if (layoutParams != null
+ && !(layoutParams instanceof MessagingLinearLayout.LayoutParams)) {
+ message.getView().setLayoutParams(
+ mMessageContainer.generateDefaultLayoutParams());
+ }
+ mMessageContainer.addView(message.getView(), textMessageIndex);
+ }
+ if (isImage) {
+ ((MessagingImageMessage) message).setIsolated(false);
+ }
+ // Let's sort them properly
+ if (textMessageIndex != mMessageContainer.indexOfChild(message.getView())) {
+ mMessageContainer.removeView(message.getView());
+ mMessageContainer.addView(message.getView(), textMessageIndex);
+ }
+ textMessageIndex++;
+ }
+ }
+ if (isolatedMessage != null) {
+ if (removeFromParentIfDifferent(isolatedMessage, mImageContainer)) {
+ mImageContainer.removeAllViews();
+ mImageContainer.addView(isolatedMessage.getView());
}
- message.setTextColor(mTextColor);
+ isolatedMessage.setIsolated(true);
+ } else if (mIsolatedMessage != null) {
+ mImageContainer.removeAllViews();
}
+ mIsolatedMessage = isolatedMessage;
mMessages = group;
}
+ /**
+ * Remove the message from the parent if the parent isn't the one provided
+ * @return whether the message was removed
+ */
+ private boolean removeFromParentIfDifferent(MessagingMessage message, ViewGroup newParent) {
+ ViewParent parent = message.getView().getParent();
+ if (parent != newParent) {
+ if (parent instanceof ViewGroup) {
+ ((ViewGroup) parent).removeView(message.getView());
+ }
+ return true;
+ }
+ return false;
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -317,13 +401,14 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
@Override
public boolean onPreDraw() {
for (MessagingMessage message : mAddedMessages) {
- if (!message.isShown()) {
+ if (!message.getView().isShown()) {
continue;
}
- MessagingPropertyAnimator.fadeIn(message);
+ MessagingPropertyAnimator.fadeIn(message.getView());
if (!mFirstLayout) {
- MessagingPropertyAnimator.startLocalTranslationFrom(message,
- message.getHeight(), MessagingLayout.LINEAR_OUT_SLOW_IN);
+ MessagingPropertyAnimator.startLocalTranslationFrom(message.getView(),
+ message.getView().getHeight(),
+ MessagingLayout.LINEAR_OUT_SLOW_IN);
}
}
mAddedMessages.clear();
@@ -333,6 +418,7 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
});
}
mFirstLayout = false;
+ updateClipRect();
}
/**
@@ -372,6 +458,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
return mMessageContainer;
}
+ public MessagingImageMessage getIsolatedMessage() {
+ return mIsolatedMessage;
+ }
+
public boolean needsGeneratedAvatar() {
return mNeedsGeneratedAvatar;
}
@@ -379,4 +469,19 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou
public Notification.Person getSender() {
return mSender;
}
+
+ public void setTransformingImages(boolean transformingImages) {
+ mTransformingImages = transformingImages;
+ }
+
+ public void setDisplayAvatarsAtEnd(boolean atEnd) {
+ if (mAvatarsAtEnd != atEnd) {
+ mAvatarsAtEnd = atEnd;
+ mImageContainer.setVisibility(atEnd ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ public List<MessagingMessage> getMessages() {
+ return mMessages;
+ }
}
diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java
new file mode 100644
index 000000000000..961f90a3c110
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingImageMessage.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2018 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.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Pools;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+import com.android.internal.R;
+
+import java.io.IOException;
+
+/**
+ * A message of a {@link MessagingLayout} that is an image.
+ */
+@RemoteViews.RemoteView
+public class MessagingImageMessage extends ImageView implements MessagingMessage {
+ private static final String TAG = "MessagingImageMessage";
+ private static Pools.SimplePool<MessagingImageMessage> sInstancePool
+ = new Pools.SynchronizedPool<>(10);
+ private final MessagingMessageState mState = new MessagingMessageState(this);
+ private final int mMinImageHeight;
+ private final Path mPath = new Path();
+ private final int mImageRounding;
+ private final int mMaxImageHeight;
+ private final int mIsolatedSize;
+ private final int mExtraSpacing;
+ private Drawable mDrawable;
+ private float mAspectRatio;
+ private int mActualWidth;
+ private int mActualHeight;
+ private boolean mIsIsolated;
+
+ public MessagingImageMessage(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public MessagingImageMessage(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mMinImageHeight = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.messaging_image_min_size);
+ mMaxImageHeight = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.messaging_image_max_height);
+ mImageRounding = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.messaging_image_rounding);
+ mExtraSpacing = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.messaging_image_extra_spacing);
+ setMaxHeight(mMaxImageHeight);
+ mIsolatedSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
+ }
+
+ @Override
+ public MessagingMessageState getState() {
+ return mState;
+ }
+
+ @Override
+ public boolean setMessage(Notification.MessagingStyle.Message message) {
+ MessagingMessage.super.setMessage(message);
+ Drawable drawable;
+ try {
+ drawable = LocalImageResolver.resolveImage(message.getDataUri(), getContext());
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ int intrinsicHeight = drawable.getIntrinsicHeight();
+ if (intrinsicHeight == 0) {
+ Log.w(TAG, "Drawable with 0 intrinsic height was returned");
+ return false;
+ }
+ mDrawable = drawable;
+ mAspectRatio = ((float) mDrawable.getIntrinsicWidth()) / intrinsicHeight;
+ setImageDrawable(drawable);
+ setContentDescription(message.getText());
+ return true;
+ }
+
+ static MessagingMessage createMessage(MessagingLayout layout,
+ Notification.MessagingStyle.Message m) {
+ MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
+ MessagingImageMessage createdMessage = sInstancePool.acquire();
+ if (createdMessage == null) {
+ createdMessage = (MessagingImageMessage) LayoutInflater.from(
+ layout.getContext()).inflate(
+ R.layout.notification_template_messaging_image_message,
+ messagingLinearLayout,
+ false);
+ createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
+ }
+ boolean created = createdMessage.setMessage(m);
+ if (!created) {
+ createdMessage.recycle();
+ return MessagingTextMessage.createMessage(layout, m);
+ }
+ return createdMessage;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.save();
+ canvas.clipPath(getRoundedRectPath());
+ int width = (int) Math.max(getActualWidth(), getActualHeight() * mAspectRatio);
+ int height = (int) (width / mAspectRatio);
+ int left = (int) ((getActualWidth() - width) / 2.0f);
+ mDrawable.setBounds(left, 0, left + width, height);
+ mDrawable.draw(canvas);
+ canvas.restore();
+ }
+
+ public Path getRoundedRectPath() {
+ int left = 0;
+ int right = getActualWidth();
+ int top = 0;
+ int bottom = getActualHeight();
+ mPath.reset();
+ int width = right - left;
+ float roundnessX = mImageRounding;
+ float roundnessY = mImageRounding;
+ roundnessX = Math.min(width / 2, roundnessX);
+ roundnessY = Math.min((bottom - top) / 2, roundnessY);
+ mPath.moveTo(left, top + roundnessY);
+ mPath.quadTo(left, top, left + roundnessX, top);
+ mPath.lineTo(right - roundnessX, top);
+ mPath.quadTo(right, top, right, top + roundnessY);
+ mPath.lineTo(right, bottom - roundnessY);
+ mPath.quadTo(right, bottom, right - roundnessX, bottom);
+ mPath.lineTo(left + roundnessX, bottom);
+ mPath.quadTo(left, bottom, left, bottom - roundnessY);
+ mPath.close();
+ return mPath;
+ }
+
+ public void recycle() {
+ MessagingMessage.super.recycle();
+ setAlpha(1.0f);
+ setTranslationY(0);
+ setImageBitmap(null);
+ mDrawable = null;
+ sInstancePool.release(this);
+ }
+
+ public static void dropCache() {
+ sInstancePool = new Pools.SynchronizedPool<>(10);
+ }
+
+ @Override
+ public int getMeasuredType() {
+ int measuredHeight = getMeasuredHeight();
+ int minImageHeight;
+ if (mIsIsolated) {
+ minImageHeight = mIsolatedSize;
+ } else {
+ minImageHeight = mMinImageHeight;
+ }
+ boolean measuredTooSmall = measuredHeight < minImageHeight
+ && measuredHeight != mDrawable.getIntrinsicHeight();
+ if (measuredTooSmall) {
+ return MEASURED_TOO_SMALL;
+ } else {
+ if (!mIsIsolated && measuredHeight != mDrawable.getIntrinsicHeight()) {
+ return MEASURED_SHORTENED;
+ } else {
+ return MEASURED_NORMAL;
+ }
+ }
+ }
+
+ @Override
+ public void setMaxDisplayedLines(int lines) {
+ // Nothing to do, this should be handled automatically.
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mIsIsolated) {
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
+ MeasureSpec.getSize(heightMeasureSpec));
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ // TODO: ensure that this isn't called when transforming
+ setActualWidth(getStaticWidth());
+ setActualHeight(getHeight());
+ }
+
+ @Override
+ public int getConsumedLines() {
+ return 3;
+ }
+
+ public void setActualWidth(int actualWidth) {
+ mActualWidth = actualWidth;
+ invalidate();
+ }
+
+ public int getActualWidth() {
+ return mActualWidth;
+ }
+
+ public void setActualHeight(int actualHeight) {
+ mActualHeight = actualHeight;
+ invalidate();
+ }
+
+ public int getActualHeight() {
+ return mActualHeight;
+ }
+
+ public int getStaticWidth() {
+ if (mIsIsolated) {
+ return getWidth();
+ }
+ return (int) (getHeight() * mAspectRatio);
+ }
+
+ public void setIsolated(boolean isolated) {
+ if (mIsIsolated != isolated) {
+ mIsIsolated = isolated;
+ // update the layout params not to have margins
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) getLayoutParams();
+ layoutParams.topMargin = isolated ? 0 : mExtraSpacing;
+ setLayoutParams(layoutParams);
+ }
+ }
+
+ @Override
+ public int getExtraSpacing() {
+ return mExtraSpacing;
+ }
+}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index d45c086e3b52..5279636ae2a0 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -81,6 +81,7 @@ public class MessagingLayout extends FrameLayout {
private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
private Notification.Person mUser;
private CharSequence mNameReplacement;
+ private boolean mIsCollapsed;
public MessagingLayout(@NonNull Context context) {
super(context);
@@ -127,6 +128,11 @@ public class MessagingLayout extends FrameLayout {
}
@RemotableViewMethod
+ public void setIsCollapsed(boolean isCollapsed) {
+ mIsCollapsed = isCollapsed;
+ }
+
+ @RemotableViewMethod
public void setData(Bundle extras) {
Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
List<Notification.MessagingStyle.Message> newMessages
@@ -331,6 +337,7 @@ public class MessagingLayout extends FrameLayout {
newGroup = MessagingGroup.createGroup(mMessagingLinearLayout);
mAddedGroups.add(newGroup);
}
+ newGroup.setDisplayAvatarsAtEnd(mIsCollapsed);
newGroup.setLayoutColor(mLayoutColor);
Notification.Person sender = senders.get(groupIndex);
CharSequence nameOverride = null;
@@ -392,7 +399,6 @@ public class MessagingLayout extends FrameLayout {
MessagingMessage message = findAndRemoveMatchingMessage(m);
if (message == null) {
message = MessagingMessage.createMessage(this, m);
- message.addOnLayoutChangeListener(MESSAGING_PROPERTY_ANIMATOR);
}
message.setIsHistoric(historic);
result.add(message);
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index f0ef37076618..991e3e7a80f9 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -75,7 +75,6 @@ public class MessagingLinearLayout extends ViewGroup {
targetHeight = Integer.MAX_VALUE;
break;
}
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
// Now that we know which views to take, fix up the indents and see what width we get.
int measuredWidth = mPaddingLeft + mPaddingRight;
@@ -90,7 +89,6 @@ public class MessagingLinearLayout extends ViewGroup {
totalHeight = mPaddingTop + mPaddingBottom;
boolean first = true;
int linesRemaining = mMaxDisplayedLines;
-
// Starting from the bottom: we measure every view as if it were the only one. If it still
// fits, we take it, otherwise we stop there.
for (int i = count - 1; i >= 0 && totalHeight < targetHeight; i--) {
@@ -100,11 +98,13 @@ public class MessagingLinearLayout extends ViewGroup {
final View child = getChildAt(i);
LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
MessagingChild messagingChild = null;
+ int spacing = mSpacing;
if (child instanceof MessagingChild) {
messagingChild = (MessagingChild) child;
messagingChild.setMaxDisplayedLines(linesRemaining);
+ spacing += messagingChild.getExtraSpacing();
}
- int spacing = first ? 0 : mSpacing;
+ spacing = first ? 0 : spacing;
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, totalHeight
- mPaddingTop - mPaddingBottom + spacing);
@@ -254,6 +254,9 @@ public class MessagingLinearLayout extends ViewGroup {
void setMaxDisplayedLines(int lines);
void hideAnimated();
boolean isHidingAnimated();
+ default int getExtraSpacing() {
+ return 0;
+ }
}
public static class LayoutParams extends MarginLayoutParams {
diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java
index f09621f544bc..bf1c5ca747a4 100644
--- a/core/java/com/android/internal/widget/MessagingMessage.java
+++ b/core/java/com/android/internal/widget/MessagingMessage.java
@@ -16,182 +16,125 @@
package com.android.internal.widget;
-import android.annotation.AttrRes;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StyleRes;
import android.app.Notification;
-import android.content.Context;
-import android.text.Layout;
-import android.util.AttributeSet;
-import android.util.Pools;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.widget.RemoteViews;
-
-import com.android.internal.R;
+import android.view.View;
import java.util.Objects;
/**
* A message of a {@link MessagingLayout}.
*/
-@RemoteViews.RemoteView
-public class MessagingMessage extends ImageFloatingTextView implements
- MessagingLinearLayout.MessagingChild {
-
- private static Pools.SimplePool<MessagingMessage> sInstancePool
- = new Pools.SynchronizedPool<>(10);
- private Notification.MessagingStyle.Message mMessage;
- private MessagingGroup mGroup;
- private boolean mIsHistoric;
- private boolean mIsHidingAnimated;
+public interface MessagingMessage extends MessagingLinearLayout.MessagingChild {
- public MessagingMessage(@NonNull Context context) {
- super(context);
- }
+ /**
+ * Prefix for supported image MIME types
+ **/
+ String IMAGE_MIME_TYPE_PREFIX = "image/";
- public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
+ static MessagingMessage createMessage(MessagingLayout layout,
+ Notification.MessagingStyle.Message m) {
+ if (hasImage(m)) {
+ return MessagingImageMessage.createMessage(layout, m);
+ } else {
+ return MessagingTextMessage.createMessage(layout, m);
+ }
}
- public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs,
- @AttrRes int defStyleAttr) {
- super(context, attrs, defStyleAttr);
+ static void dropCache() {
+ MessagingTextMessage.dropCache();
+ MessagingImageMessage.dropCache();
}
- public MessagingMessage(@NonNull Context context, @Nullable AttributeSet attrs,
- @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
+ static boolean hasImage(Notification.MessagingStyle.Message m) {
+ return m.getDataUri() != null
+ && m.getDataMimeType() != null
+ && m.getDataMimeType().startsWith(IMAGE_MIME_TYPE_PREFIX);
}
- private void setMessage(Notification.MessagingStyle.Message message) {
- mMessage = message;
- setText(message.getText());
+ /**
+ * Set a message for this view.
+ * @return true if setting the message worked
+ */
+ default boolean setMessage(Notification.MessagingStyle.Message message) {
+ getState().setMessage(message);
+ return true;
}
- public Notification.MessagingStyle.Message getMessage() {
- return mMessage;
+ default Notification.MessagingStyle.Message getMessage() {
+ return getState().getMessage();
}
- boolean sameAs(Notification.MessagingStyle.Message message) {
- if (!Objects.equals(message.getText(), mMessage.getText())) {
+ default boolean sameAs(Notification.MessagingStyle.Message message) {
+ Notification.MessagingStyle.Message ownMessage = getMessage();
+ if (!Objects.equals(message.getText(), ownMessage.getText())) {
return false;
}
- if (!Objects.equals(message.getSender(), mMessage.getSender())) {
+ if (!Objects.equals(message.getSender(), ownMessage.getSender())) {
return false;
}
- if (!Objects.equals(message.getTimestamp(), mMessage.getTimestamp())) {
+ if (!Objects.equals(message.getTimestamp(), ownMessage.getTimestamp())) {
+ return false;
+ }
+ if (!Objects.equals(message.getDataMimeType(), ownMessage.getDataMimeType())) {
+ return false;
+ }
+ if (!Objects.equals(message.getDataUri(), ownMessage.getDataUri())) {
return false;
}
return true;
}
- boolean sameAs(MessagingMessage message) {
+ default boolean sameAs(MessagingMessage message) {
return sameAs(message.getMessage());
}
- static MessagingMessage createMessage(MessagingLayout layout,
- Notification.MessagingStyle.Message m) {
- MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
- MessagingMessage createdMessage = sInstancePool.acquire();
- if (createdMessage == null) {
- createdMessage = (MessagingMessage) LayoutInflater.from(layout.getContext()).inflate(
- R.layout.notification_template_messaging_message, messagingLinearLayout,
- false);
- }
- createdMessage.setMessage(m);
- return createdMessage;
- }
-
- public void removeMessage() {
- mGroup.removeMessage(this);
- }
-
- public void recycle() {
- mGroup = null;
- mMessage = null;
- setAlpha(1.0f);
- setTranslationY(0);
- sInstancePool.release(this);
+ default void removeMessage() {
+ getGroup().removeMessage(this);
}
- public void setMessagingGroup(MessagingGroup group) {
- mGroup = group;
+ default void setMessagingGroup(MessagingGroup group) {
+ getState().setGroup(group);
}
- public static void dropCache() {
- sInstancePool = new Pools.SynchronizedPool<>(10);
+ default void setIsHistoric(boolean isHistoric) {
+ getState().setIsHistoric(isHistoric);
}
- public void setIsHistoric(boolean isHistoric) {
- mIsHistoric = isHistoric;
+ default MessagingGroup getGroup() {
+ return getState().getGroup();
}
- public MessagingGroup getGroup() {
- return mGroup;
+ default void setIsHidingAnimated(boolean isHiding) {
+ getState().setIsHidingAnimated(isHiding);
}
@Override
- public int getMeasuredType() {
- boolean measuredTooSmall = getMeasuredHeight()
- < getLayoutHeight() + getPaddingTop() + getPaddingBottom();
- if (measuredTooSmall) {
- return MEASURED_TOO_SMALL;
- } else {
- Layout layout = getLayout();
- if (layout == null) {
- return MEASURED_TOO_SMALL;
- }
- if (layout.getEllipsisCount(layout.getLineCount() - 1) > 0) {
- return MEASURED_SHORTENED;
- } else {
- return MEASURED_NORMAL;
- }
- }
+ default boolean isHidingAnimated() {
+ return getState().isHidingAnimated();
}
@Override
- public void hideAnimated() {
+ default void hideAnimated() {
setIsHidingAnimated(true);
- mGroup.performRemoveAnimation(this, () -> setIsHidingAnimated(false));
+ getGroup().performRemoveAnimation(getState().getHostView(),
+ () -> setIsHidingAnimated(false));
}
- private void setIsHidingAnimated(boolean isHiding) {
- ViewParent parent = getParent();
- mIsHidingAnimated = isHiding;
- invalidate();
- if (parent instanceof ViewGroup) {
- ((ViewGroup) parent).invalidate();
- }
+ default boolean hasOverlappingRendering() {
+ return false;
}
- @Override
- public boolean isHidingAnimated() {
- return mIsHidingAnimated;
+ default void recycle() {
+ getState().reset();
}
- @Override
- public void setMaxDisplayedLines(int lines) {
- setMaxLines(lines);
+ default View getView() {
+ return (View) this;
}
- @Override
- public int getConsumedLines() {
- return getLineCount();
- }
+ default void setColor(int textColor) {}
- public int getLayoutHeight() {
- Layout layout = getLayout();
- if (layout == null) {
- return 0;
- }
- return layout.getHeight();
- }
+ MessagingMessageState getState();
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
+ void setVisibility(int visibility);
}
diff --git a/core/java/com/android/internal/widget/MessagingMessageState.java b/core/java/com/android/internal/widget/MessagingMessageState.java
new file mode 100644
index 000000000000..ac624728689d
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingMessageState.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 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.app.Notification;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+/**
+ * Shared state and implementation for MessagingMessages. Used to share common implementations.
+ */
+public class MessagingMessageState {
+ private final View mHostView;
+ private Notification.MessagingStyle.Message mMessage;
+ private MessagingGroup mGroup;
+ private boolean mIsHistoric;
+ private boolean mIsHidingAnimated;
+
+ MessagingMessageState(View hostView) {
+ mHostView = hostView;
+ }
+
+ public void setMessage(Notification.MessagingStyle.Message message) {
+ mMessage = message;
+ }
+
+ public Notification.MessagingStyle.Message getMessage() {
+ return mMessage;
+ }
+
+ public void setGroup(MessagingGroup group) {
+ mGroup = group;
+ }
+
+ public MessagingGroup getGroup() {
+ return mGroup;
+ }
+
+ public void setIsHistoric(boolean isHistoric) {
+ mIsHistoric = isHistoric;
+ }
+
+ public void setIsHidingAnimated(boolean isHiding) {
+ ViewParent parent = mHostView.getParent();
+ mIsHidingAnimated = isHiding;
+ mHostView.invalidate();
+ if (parent instanceof ViewGroup) {
+ ((ViewGroup) parent).invalidate();
+ }
+ }
+
+ public boolean isHidingAnimated() {
+ return mIsHidingAnimated;
+ }
+
+ public View getHostView() {
+ return mHostView;
+ }
+
+ public void reset() {
+ mIsHidingAnimated = false;
+ mIsHistoric = false;
+ mGroup = null;
+ mMessage = null;
+ }
+}
diff --git a/core/java/com/android/internal/widget/MessagingTextMessage.java b/core/java/com/android/internal/widget/MessagingTextMessage.java
new file mode 100644
index 000000000000..794cc1dc66f7
--- /dev/null
+++ b/core/java/com/android/internal/widget/MessagingTextMessage.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 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.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
+import android.app.Notification;
+import android.content.Context;
+import android.text.Layout;
+import android.util.AttributeSet;
+import android.util.Pools;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.RemoteViews;
+
+import com.android.internal.R;
+
+import java.util.Objects;
+
+/**
+ * A message of a {@link MessagingLayout}.
+ */
+@RemoteViews.RemoteView
+public class MessagingTextMessage extends ImageFloatingTextView implements MessagingMessage {
+
+ private static Pools.SimplePool<MessagingTextMessage> sInstancePool
+ = new Pools.SynchronizedPool<>(20);
+ private final MessagingMessageState mState = new MessagingMessageState(this);
+
+ public MessagingTextMessage(@NonNull Context context) {
+ super(context);
+ }
+
+ public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public MessagingTextMessage(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public MessagingMessageState getState() {
+ return mState;
+ }
+
+ @Override
+ public boolean setMessage(Notification.MessagingStyle.Message message) {
+ MessagingMessage.super.setMessage(message);
+ setText(message.getText());
+ return true;
+ }
+
+ static MessagingMessage createMessage(MessagingLayout layout,
+ Notification.MessagingStyle.Message m) {
+ MessagingLinearLayout messagingLinearLayout = layout.getMessagingLinearLayout();
+ MessagingTextMessage createdMessage = sInstancePool.acquire();
+ if (createdMessage == null) {
+ createdMessage = (MessagingTextMessage) LayoutInflater.from(
+ layout.getContext()).inflate(
+ R.layout.notification_template_messaging_text_message,
+ messagingLinearLayout,
+ false);
+ createdMessage.addOnLayoutChangeListener(MessagingLayout.MESSAGING_PROPERTY_ANIMATOR);
+ }
+ createdMessage.setMessage(m);
+ return createdMessage;
+ }
+
+ public void recycle() {
+ MessagingMessage.super.recycle();
+ setAlpha(1.0f);
+ setTranslationY(0);
+ sInstancePool.release(this);
+ }
+
+ public static void dropCache() {
+ sInstancePool = new Pools.SynchronizedPool<>(10);
+ }
+
+ @Override
+ public int getMeasuredType() {
+ boolean measuredTooSmall = getMeasuredHeight()
+ < getLayoutHeight() + getPaddingTop() + getPaddingBottom();
+ if (measuredTooSmall) {
+ return MEASURED_TOO_SMALL;
+ } else {
+ Layout layout = getLayout();
+ if (layout == null) {
+ return MEASURED_TOO_SMALL;
+ }
+ if (layout.getEllipsisCount(layout.getLineCount() - 1) > 0) {
+ return MEASURED_SHORTENED;
+ } else {
+ return MEASURED_NORMAL;
+ }
+ }
+ }
+
+ @Override
+ public void setMaxDisplayedLines(int lines) {
+ setMaxLines(lines);
+ }
+
+ @Override
+ public int getConsumedLines() {
+ return getLineCount();
+ }
+
+ public int getLayoutHeight() {
+ Layout layout = getLayout();
+ if (layout == null) {
+ return 0;
+ }
+ return layout.getHeight();
+ }
+
+ @Override
+ public void setColor(int color) {
+ setTextColor(color);
+ }
+}
diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
index ad6020c0846c..7d1c70647092 100644
--- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java
+++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
@@ -98,6 +98,8 @@ public final class VerifyCredentialResponse implements Parcelable {
if (mPayload != null) {
dest.writeInt(mPayload.length);
dest.writeByteArray(mPayload);
+ } else {
+ dest.writeInt(0);
}
}
}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index d5b6def97426..64bdc6e1eb37 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -31,6 +31,7 @@ import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
+import android.view.AbsSavedState;
import android.view.FocusFinder;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -1198,16 +1199,12 @@ public class ViewPager extends ViewGroup {
* state, in which case it should implement a subclass of this which
* contains that state.
*/
- public static class SavedState extends BaseSavedState {
+ public static class SavedState extends AbsSavedState {
int position;
Parcelable adapterState;
ClassLoader loader;
- public SavedState(Parcel source) {
- super(source);
- }
-
- public SavedState(Parcelable superState) {
+ public SavedState(@NonNull Parcelable superState) {
super(superState);
}
@@ -1225,10 +1222,15 @@ public class ViewPager extends ViewGroup {
+ " position=" + position + "}";
}
- public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
+ public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+ return new SavedState(in, loader);
+ }
+
@Override
public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
+ return new SavedState(in, null);
}
@Override
public SavedState[] newArray(int size) {
@@ -1237,7 +1239,7 @@ public class ViewPager extends ViewGroup {
};
SavedState(Parcel in, ClassLoader loader) {
- super(in);
+ super(in, loader);
if (loader == null) {
loader = getClass().getClassLoader();
}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index fb186693979d..95534e264b80 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -72,6 +72,7 @@ public class BootReceiver extends BroadcastReceiver {
SystemProperties.getInt("ro.debuggable", 0) == 1 ? 98304 : 65536;
private static final File TOMBSTONE_DIR = new File("/data/tombstones");
+ private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
// The pre-froyo package and class of the system updater, which
// ran in the system process. We need to remove its packages here
@@ -164,7 +165,7 @@ public class BootReceiver extends BroadcastReceiver {
.append("Revision: ")
.append(SystemProperties.get("ro.revision", "")).append("\n")
.append("Bootloader: ").append(Build.BOOTLOADER).append("\n")
- .append("Radio: ").append(Build.RADIO).append("\n")
+ .append("Radio: ").append(Build.getRadioVersion()).append("\n")
.append("Kernel: ")
.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
.append("\n").toString();
@@ -265,7 +266,7 @@ public class BootReceiver extends BroadcastReceiver {
File file = new File(TOMBSTONE_DIR, path);
if (file.isFile()) {
addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
- "SYSTEM_TOMBSTONE");
+ TAG_TOMBSTONE);
}
} catch (IOException e) {
Slog.e(TAG, "Can't log tombstone", e);
@@ -299,9 +300,20 @@ public class BootReceiver extends BroadcastReceiver {
timestamps.put(filename, fileTime);
+
+ String fileContents = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
+ String text = headers + fileContents + footers;
+ // Create an additional report for system server native crashes, with a special tag.
+ if (tag.equals(TAG_TOMBSTONE) && fileContents.contains(">>> system_server <<<")) {
+ addTextToDropBox(db, "system_server_native_crash", text, filename, maxSize);
+ }
+ addTextToDropBox(db, tag, text, filename, maxSize);
+ }
+
+ private static void addTextToDropBox(DropBoxManager db, String tag, String text,
+ String filename, int maxSize) {
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
- db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") +
- footers);
+ db.addText(tag, text);
EventLog.writeEvent(DropboxLogTags.DROPBOX_FILE_COPY, filename, maxSize, tag);
}