summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java51
-rw-r--r--apex/statsd/framework/Android.bp5
-rw-r--r--api/current.txt23
-rwxr-xr-xapi/system-current.txt72
-rw-r--r--api/test-current.txt6
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp1517
-rw-r--r--core/java/android/app/ActivityManager.java31
-rw-r--r--core/java/android/app/ActivityOptions.java27
-rw-r--r--core/java/android/app/ApplicationExitInfo.java205
-rw-r--r--core/java/android/app/IActivityManager.aidl23
-rw-r--r--core/java/android/app/IAppTraceRetriever.aidl38
-rw-r--r--core/java/android/app/ITaskOrganizerController.aidl5
-rw-r--r--core/java/android/app/SystemServiceRegistry.java8
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java4
-rw-r--r--core/java/android/app/usage/UsageStatsManager.java18
-rw-r--r--core/java/android/content/Context.java5
-rw-r--r--core/java/android/content/Intent.java4
-rw-r--r--core/java/android/content/pm/IDataLoaderStatusListener.aidl4
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java4
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java46
-rw-r--r--core/java/android/net/LinkProperties.java66
-rw-r--r--core/java/android/net/NetworkStack.java17
-rw-r--r--core/java/android/net/RouteInfo.java20
-rw-r--r--core/java/android/permission/PermissionControllerService.java16
-rw-r--r--core/java/android/service/autofill/FillResponse.java37
-rw-r--r--core/java/android/service/autofill/InlineAction.aidl19
-rw-r--r--core/java/android/service/autofill/InlineAction.java205
-rw-r--r--core/java/android/service/autofill/augmented/AugmentedAutofillService.java6
-rw-r--r--core/java/android/service/autofill/augmented/FillCallback.java4
-rw-r--r--core/java/android/service/autofill/augmented/FillResponse.java59
-rw-r--r--core/java/android/service/autofill/augmented/IFillCallback.aidl4
-rw-r--r--core/java/android/service/quickaccesswallet/WalletServiceEvent.java8
-rw-r--r--core/java/android/text/style/ReplacementSpan.java4
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java7
-rw-r--r--core/java/android/view/SurfaceView.java16
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java4
-rw-r--r--core/java/android/widget/EditText.java10
-rw-r--r--core/java/android/widget/Editor.java21
-rw-r--r--core/java/android/widget/TextView.java17
-rw-r--r--core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java10
-rw-r--r--core/java/com/android/internal/compat/CompatibilityChangeInfo.java37
-rw-r--r--core/java/com/android/internal/compat/IPlatformCompat.aidl8
-rw-r--r--core/proto/android/app/appexitinfo.proto2
-rw-r--r--core/res/res/values/attrs_manifest.xml4
-rw-r--r--core/res/res/values/public.xml4
-rw-r--r--core/res/res/values/strings.xml14
-rw-r--r--core/tests/coretests/src/android/widget/TextViewActivityTest.java16
-rw-r--r--data/etc/privapp-permissions-platform.xml7
-rw-r--r--location/java/android/location/AbstractListenerManager.java36
-rw-r--r--media/java/android/media/AudioMetadata.java42
-rw-r--r--media/java/android/media/tv/tuner/Lnb.java11
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java116
-rw-r--r--media/java/android/media/tv/tuner/TunerUtils.java1
-rw-r--r--mms/java/android/telephony/MmsManager.java1
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/res/layout/controls_no_favorites.xml16
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_section_header.xml29
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml47
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/res/values/styles.xml14
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt183
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt76
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt8
-rw-r--r--packages/Tethering/res/values-mcc204-mnc04/strings.xml26
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004/strings.xml26
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480/strings.xml26
-rw-r--r--packages/Tethering/res/values/strings.xml10
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java4
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java10
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java21
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java28
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java69
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java69
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java84
-rw-r--r--services/core/java/com/android/server/am/AppExitInfoTracker.java828
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java11
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java8
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java24
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java27
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java17
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java2
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java50
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java13
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java5
-rw-r--r--services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp17
-rw-r--r--services/incremental/IncrementalService.cpp12
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java88
-rw-r--r--services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java43
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java74
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java16
-rw-r--r--telephony/api/system-current.txt9
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java13
-rw-r--r--tests/net/common/Android.bp1
-rw-r--r--tests/net/common/java/android/net/LinkPropertiesTest.java104
-rw-r--r--tests/net/common/java/android/net/NetworkCapabilitiesTest.java30
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java96
-rw-r--r--wifi/Android.bp1
-rwxr-xr-xwifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java284
-rwxr-xr-xwifi/java/android/net/wifi/WifiMigration.java27
-rw-r--r--wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java199
119 files changed, 3772 insertions, 2275 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index b6f85b2d06bf..e14ca990a2e5 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -23,8 +23,6 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
@@ -75,6 +73,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
+import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -305,7 +304,10 @@ public class AppStandbyController implements AppStandbyInternal {
private final AppStandbyHandler mHandler;
private final Context mContext;
+ // TODO: Provide a mechanism to set an external bucketing service
+
private AppWidgetManager mAppWidgetManager;
+ private ConnectivityManager mConnectivityManager;
private PackageManager mPackageManager;
Injector mInjector;
@@ -409,6 +411,7 @@ public class AppStandbyController implements AppStandbyInternal {
settingsObserver.updateSettings();
mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
+ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mInjector.registerDisplayListener(mDisplayListener, mHandler);
synchronized (mAppIdleLock) {
@@ -1516,38 +1519,6 @@ public class AppStandbyController implements AppStandbyInternal {
}
}
- /**
- * Remove an app from the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
- * bucket if it was forced into the bucket by the system because it was buggy.
- */
- @VisibleForTesting
- void maybeUnrestrictBuggyApp(String packageName, int userId) {
- synchronized (mAppIdleLock) {
- final long elapsedRealtime = mInjector.elapsedRealtime();
- final AppIdleHistory.AppUsageHistory app =
- mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime);
- if (app.currentBucket != STANDBY_BUCKET_RESTRICTED
- || (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_FORCED_BY_SYSTEM) {
- return;
- }
-
- final int newBucket;
- final int newReason;
- if ((app.bucketingReason & REASON_SUB_MASK) == REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY) {
- // If bugginess was the only reason the app should be restricted, then lift it out.
- newBucket = STANDBY_BUCKET_RARE;
- newReason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_UPDATE;
- } else {
- // There's another reason the app was restricted. Remove the buggy bit and call
- // it a day.
- newBucket = STANDBY_BUCKET_RESTRICTED;
- newReason = app.bucketingReason & ~REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
- }
- mAppIdleHistory.setAppStandbyBucket(
- packageName, userId, elapsedRealtime, newBucket, newReason);
- }
- }
-
private class PackageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -1557,14 +1528,10 @@ public class AppStandbyController implements AppStandbyInternal {
clearCarrierPrivilegedApps();
}
if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
- Intent.ACTION_PACKAGE_ADDED.equals(action))) {
- final String pkgName = intent.getData().getSchemeSpecificPart();
- final int userId = getSendingUserId();
- if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- maybeUnrestrictBuggyApp(pkgName, userId);
- } else {
- clearAppIdleForPackage(pkgName, userId);
- }
+ Intent.ACTION_PACKAGE_ADDED.equals(action))
+ && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
+ getSendingUserId());
}
}
}
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 986682e6a8c9..8185bb036b22 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -166,8 +166,10 @@ java_library {
android_test {
name: "FrameworkStatsdTest",
- sdk_version: "module_current",
+ platform_apis: true,
srcs: [
+ // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
+ ":framework-statsd-sources",
"test/**/*.java",
],
manifest: "test/AndroidManifest.xml",
@@ -178,7 +180,6 @@ android_test {
libs: [
"android.test.runner.stubs",
"android.test.base.stubs",
- "framework-statsd",
],
test_suites: [
"device-tests",
diff --git a/api/current.txt b/api/current.txt
index 61b427c1d50a..baad02c0278c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -288,9 +288,9 @@ package android {
field public static final int alignmentMode = 16843642; // 0x101037a
field public static final int allContactsName = 16843468; // 0x10102cc
field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601
+ field public static final int allowAutoRevokePermissionsExemption = 16844309; // 0x1010615
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
- field public static final int allowDontAutoRevokePermissions = 16844309; // 0x1010615
field public static final int allowEmbedded = 16843765; // 0x10103f5
field public static final int allowNativeHeapPointerTagging = 16844307; // 0x1010613
field public static final int allowParallelSyncs = 16843570; // 0x1010332
@@ -1140,7 +1140,7 @@ package android {
field public static final int reqKeyboardType = 16843304; // 0x1010228
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
- field public static final int requestDontAutoRevokePermissions = 16844308; // 0x1010614
+ field public static final int requestAutoRevokePermissionsExemption = 16844308; // 0x1010614
field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
@@ -4049,6 +4049,7 @@ package android.app {
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int);
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int, android.os.Bundle);
method @Deprecated public void restartPackage(String);
+ method public void setProcessStateSummary(@Nullable byte[]);
method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
field public static final String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
@@ -4561,12 +4562,14 @@ package android.app {
method public int getPackageUid();
method public int getPid();
method @NonNull public String getProcessName();
+ method @Nullable public byte[] getProcessStateSummary();
method public long getPss();
method public int getRealUid();
method public int getReason();
method public long getRss();
method public int getStatus();
method public long getTimestamp();
+ method @Nullable public java.io.InputStream getTraceInputStream() throws java.io.IOException;
method @NonNull public android.os.UserHandle getUserHandle();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ApplicationExitInfo> CREATOR;
@@ -10202,7 +10205,6 @@ package android.content {
field public static final String MEDIA_ROUTER_SERVICE = "media_router";
field public static final String MEDIA_SESSION_SERVICE = "media_session";
field public static final String MIDI_SERVICE = "midi";
- field public static final String MMS_SERVICE = "mms";
field public static final int MODE_APPEND = 32768; // 0x8000
field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
field @Deprecated public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -43121,7 +43123,6 @@ package android.service.autofill {
public static final class FillResponse.Builder {
ctor public FillResponse.Builder();
method @NonNull public android.service.autofill.FillResponse.Builder addDataset(@Nullable android.service.autofill.Dataset);
- method @NonNull public android.service.autofill.FillResponse.Builder addInlineAction(@NonNull android.service.autofill.InlineAction);
method @NonNull public android.service.autofill.FillResponse build();
method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
@@ -43151,15 +43152,6 @@ package android.service.autofill {
method public android.service.autofill.ImageTransformation build();
}
- public final class InlineAction implements android.os.Parcelable {
- ctor public InlineAction(@NonNull android.service.autofill.InlinePresentation, @NonNull android.content.IntentSender);
- method public int describeContents();
- method @NonNull public android.content.IntentSender getAction();
- method @NonNull public android.service.autofill.InlinePresentation getInlinePresentation();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.InlineAction> CREATOR;
- }
-
public final class InlinePresentation implements android.os.Parcelable {
ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.view.inline.InlinePresentationSpec, boolean);
method public int describeContents();
@@ -47545,11 +47537,6 @@ package android.telephony {
method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
}
- public class MmsManager {
- method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
- method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
- }
-
@Deprecated public class NeighboringCellInfo implements android.os.Parcelable {
ctor @Deprecated public NeighboringCellInfo();
ctor @Deprecated public NeighboringCellInfo(int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index f4e2a936f603..fd3798908d1e 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1807,7 +1807,6 @@ package android.content {
field public static final String NETD_SERVICE = "netd";
field public static final String NETWORK_POLICY_SERVICE = "netpolicy";
field public static final String NETWORK_SCORE_SERVICE = "network_score";
- field public static final String NETWORK_STACK_SERVICE = "network_stack";
field public static final String OEM_LOCK_SERVICE = "oem_lock";
field public static final String PERMISSION_SERVICE = "permission";
field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
@@ -1871,7 +1870,6 @@ package android.content {
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
- field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
@@ -4828,11 +4826,11 @@ package android.media.tv.tuner {
}
public class Lnb implements java.lang.AutoCloseable {
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int sendDiseqcMessage(@NonNull byte[]);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setSatellitePosition(int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setTone(int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setVoltage(int);
+ method public void close();
+ method public int sendDiseqcMessage(@NonNull byte[]);
+ method public int setSatellitePosition(int);
+ method public int setTone(int);
+ method public int setVoltage(int);
field public static final int EVENT_TYPE_DISEQC_RX_OVERFLOW = 0; // 0x0
field public static final int EVENT_TYPE_DISEQC_RX_PARITY_ERROR = 2; // 0x2
field public static final int EVENT_TYPE_DISEQC_RX_TIMEOUT = 1; // 0x1
@@ -4860,32 +4858,32 @@ package android.media.tv.tuner {
public class Tuner implements java.lang.AutoCloseable {
ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @Nullable String, int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int cancelScanning();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int cancelTuning();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearResourceLostListener();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int connectCiCam(int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int disconnectCiCam();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public long getAvSyncTime(int);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities(@NonNull android.content.Context);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
+ method public int cancelScanning();
+ method public int cancelTuning();
+ method public void clearOnTuneEventListener();
+ method public void clearResourceLostListener();
+ method public void close();
+ method public int connectCiCam(int);
+ method public int disconnectCiCam();
+ method public int getAvSyncHwId(@NonNull android.media.tv.tuner.filter.Filter);
+ method public long getAvSyncTime(int);
+ method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities();
+ method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.filter.Filter openFilter(int, int, long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.filter.FilterCallback);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Lnb openLnb(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
- method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Lnb openLnbByName(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
+ method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
+ method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
+ method @Nullable public android.media.tv.tuner.filter.Filter openFilter(int, int, long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.filter.FilterCallback);
+ method @Nullable public android.media.tv.tuner.Lnb openLnb(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
+ method @Nullable public android.media.tv.tuner.Lnb openLnbByName(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter();
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setLnaEnabled(boolean);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
- method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int);
+ method public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
+ method public int setLnaEnabled(boolean);
+ method public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
+ method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
+ method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
+ method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
+ method public void updateResourcePriority(int, int);
field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
field public static final int INVALID_STREAM_ID = 65535; // 0xffff
@@ -6219,6 +6217,7 @@ package android.net {
public final class LinkProperties implements android.os.Parcelable {
ctor public LinkProperties(@Nullable android.net.LinkProperties);
+ ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean);
method public boolean addDnsServer(@NonNull java.net.InetAddress);
method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
method public boolean addPcscfServer(@NonNull java.net.InetAddress);
@@ -6241,7 +6240,6 @@ package android.net {
method public boolean isIpv6Provisioned();
method public boolean isProvisioned();
method public boolean isReachable(@NonNull java.net.InetAddress);
- method @NonNull public android.net.LinkProperties makeSensitiveFieldsParcelingCopy();
method public boolean removeDnsServer(@NonNull java.net.InetAddress);
method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
method public boolean removeRoute(@NonNull android.net.RouteInfo);
@@ -6402,6 +6400,7 @@ package android.net {
}
public class NetworkStack {
+ method @Nullable public static android.os.IBinder getService();
field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
}
@@ -8893,6 +8892,7 @@ package android.permission {
method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
method @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
+ method @BinderThread public void onUpdateUserSensitivePermissionFlags(int, @NonNull java.util.concurrent.Executor, @NonNull Runnable);
method @BinderThread public void onUpdateUserSensitivePermissionFlags(int, @NonNull Runnable);
field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
}
@@ -9772,7 +9772,6 @@ package android.service.autofill.augmented {
method @NonNull public android.service.autofill.augmented.FillResponse build();
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@NonNull android.os.Bundle);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@NonNull android.service.autofill.augmented.FillWindow);
- method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@NonNull java.util.List<android.service.autofill.InlineAction>);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@NonNull java.util.List<android.service.autofill.Dataset>);
}
@@ -11567,14 +11566,10 @@ package android.telephony {
field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
- field public static final String ACTION_SERVICE_PROVIDERS_UPDATED = "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
- field public static final int CARD_POWER_DOWN = 0; // 0x0
- field public static final int CARD_POWER_UP = 1; // 0x1
- field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -11585,7 +11580,6 @@ package android.telephony {
field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
field @Deprecated public static final String EXTRA_APN_TYPE = "apnType";
field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
- field public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
field public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; // 0x4
@@ -11598,16 +11592,12 @@ package android.telephony {
field public static final String EXTRA_PCO_VALUE = "pcoValue";
field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
- field public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
- field public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
- field public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
field public static final String EXTRA_SIM_COMBINATION_NAMES = "android.telephony.extra.SIM_COMBINATION_NAMES";
field public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; // 0x1
field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; // 0x0
field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
- field public static final String EXTRA_SPN = "android.telephony.extra.SPN";
field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
diff --git a/api/test-current.txt b/api/test-current.txt
index 286408dbbecb..00c8fdcc77a2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -111,6 +111,7 @@ package android.app {
method public void setLaunchActivityType(int);
method public void setLaunchTaskId(int);
method public void setLaunchWindowingMode(int);
+ method public void setTaskAlwaysOnTop(boolean);
method public void setTaskOverlay(boolean, boolean);
}
@@ -829,7 +830,6 @@ package android.content {
field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
field public static final String DREAM_SERVICE = "dream";
field public static final String ETHERNET_SERVICE = "ethernet";
- field public static final String NETWORK_STACK_SERVICE = "network_stack";
field public static final String PERMISSION_SERVICE = "permission";
field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
field public static final String ROLLBACK_SERVICE = "rollback";
@@ -1790,6 +1790,7 @@ package android.net {
public final class LinkProperties implements android.os.Parcelable {
ctor public LinkProperties(@Nullable android.net.LinkProperties);
+ ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean);
method public boolean addDnsServer(@NonNull java.net.InetAddress);
method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
method @Nullable public android.net.Uri getCaptivePortalApiUrl();
@@ -1804,7 +1805,6 @@ package android.net {
method public boolean isIpv6Provisioned();
method public boolean isProvisioned();
method public boolean isReachable(@NonNull java.net.InetAddress);
- method @NonNull public android.net.LinkProperties makeSensitiveFieldsParcelingCopy();
method public boolean removeDnsServer(@NonNull java.net.InetAddress);
method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
method public boolean removeRoute(@NonNull android.net.RouteInfo);
@@ -1830,6 +1830,7 @@ package android.net {
}
public class NetworkStack {
+ method @Nullable public static android.os.IBinder getService();
field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
}
@@ -3260,7 +3261,6 @@ package android.service.autofill.augmented {
method @NonNull public android.service.autofill.augmented.FillResponse build();
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@NonNull android.os.Bundle);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@NonNull android.service.autofill.augmented.FillWindow);
- method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineActions(@NonNull java.util.List<android.service.autofill.InlineAction>);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@NonNull java.util.List<android.service.autofill.Dataset>);
}
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 0868aaa3f7a4..fab8e6828977 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -766,805 +766,726 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
{(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)});
EXPECT_FALSE(valueProducer->mCondition);
}
+TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(30, curInterval.value.long_value);
+
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs});
+}
+
+TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
+ valueProducer.mCondition = ConditionState::kFalse;
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ // has 1 slice
+ EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
+
+ valueProducer.onConditionChangedLocked(true, bucketStartTimeNs + 15);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(20, curInterval.value.long_value);
+
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 1, 30);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(50, curInterval.value.long_value);
+
+ valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
+
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 1, 40);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(50, curInterval.value.long_value);
+
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20});
+}
+
+TEST(ValueMetricProducerTest, TestAnomalyDetection) {
+ sp<AlarmMonitor> alarmMonitor;
+ Alert alert;
+ alert.set_id(101);
+ alert.set_metric_id(metricId);
+ alert.set_trigger_if_sum_gt(130);
+ alert.set_num_buckets(2);
+ const int32_t refPeriodSec = 3;
+ alert.set_refractory_period_secs(refPeriodSec);
+
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, -1 /*not pulled*/,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+ sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 1 * NS_PER_SEC, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 2 + NS_PER_SEC, 20);
+
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event3, tagId,
+ bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, 130);
+
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event4, tagId,
+ bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC, 1);
+
+ LogEvent event5(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event5, tagId,
+ bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, 150);
+
+ LogEvent event6(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event6, tagId,
+ bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC, 160);
+
+ // Two events in bucket #0.
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+ // Value sum == 30 <= 130.
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
+
+ // One event in bucket #2. No alarm as bucket #0 is trashed out.
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+ // Value sum == 130 <= 130.
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
+
+ // Three events in bucket #3.
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
+ // Anomaly at event 4 since Value sum == 131 > 130!
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
+ std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
+ // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
+ std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
+ // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
+ std::ceil(1.0 * event6.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+}
+
+// Test value metric no condition, the pull on bucket boundary come in time and too late
+TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+ vector<shared_ptr<LogEvent>> allData;
+ // pull 1
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
+
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+ // startUpdated:true sum:0 start:11
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+
+ // pull 2 at correct time
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ // tartUpdated:false sum:12
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(23, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
+
+ // pull 3 come late.
+ // The previous bucket gets closed with error. (Has start value 23, no ending)
+ // Another bucket gets closed with error. (No start, but ending with 36)
+ // The new bucket is back to normal.
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ // startUpdated:false sum:12
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
+}
+
+/*
+ * Test pulled event with non sliced condition. The pull on boundary come late because the alarm
+ * was delivered late.
+ */
+TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // condition becomes true
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
+ return true;
+ }))
+ // condition becomes false
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
+ return true;
+ }));
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(100, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+
+ // pull on bucket boundary come late, condition change happens before it
+ valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
+ EXPECT_EQ(false, curBaseInfo.hasBase);
+
+ // Now the alarm is delivered.
+ // since the condition turned to off before this pull finish, it has no effect
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
+ EXPECT_EQ(false, curInterval.hasValue);
+}
+
+/*
+* Test pulled event with non sliced condition. The pull on boundary come late, after the
+condition
+* change to false, and then true again. This is due to alarm delivered late.
+*/
+TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ // condition becomes true
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
+ return true;
+ }))
+ // condition becomes false
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
+ return true;
+ }))
+ // condition becomes true again
+ .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ // startUpdated:false sum:0 start:100
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(100, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+
+ // pull on bucket boundary come late, condition change happens before it
+ valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
+ EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
+ EXPECT_EQ(false, curInterval.hasValue);
+
+ // condition changed to true again, before the pull alarm is delivered
+ valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(130, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+
+ // Now the alarm is delivered, but it is considered late, the data will be used
+ // for the new bucket since it was just pulled.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
+
+ curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(140, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(10, curInterval.value.long_value);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30},
+ {bucketSizeNs - 8, bucketSizeNs - 24});
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_aggregation_type(ValueMetric::MIN);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(10, curInterval.value.long_value);
+
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs});
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_aggregation_type(ValueMetric::MAX);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(20, curInterval.value.long_value);
+
+ valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+ /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); */
+ /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); */
+ /* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_aggregation_type(ValueMetric::AVG);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 15);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(1, curInterval.sampleSize);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(25, curInterval.value.long_value);
+ EXPECT_EQ(2, curInterval.sampleSize);
+
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+
+ EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value -
+ 12.5) < epsilon);
+}
+
+TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_aggregation_type(ValueMetric::SUM);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 15);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(10, curInterval.value.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(25, curInterval.value.long_value);
+
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs});
+}
+
+TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.set_aggregation_type(ValueMetric::MIN);
+ metric.set_use_diff(true);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 1, 15);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(10, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(5, curInterval.value.long_value);
+
+ // no change in data.
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 1, 15);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(15, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(15, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs});
+}
+
+TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ metric.mutable_value_field()->add_child()->set_field(3);
+ metric.set_aggregation_type(ValueMetric::MIN);
+ metric.set_use_diff(true);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10, 20);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 1, 15, 22);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(10, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(20, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(5, curInterval.value.long_value);
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(2, curInterval.value.long_value);
+
+ // no change in first value field
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 1, 15, 25);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(15, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(25, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15, 29);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(15, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(29, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
+
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size());
+
+ EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[0].mConditionTrueNs);
+ EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
+ EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].valueIndex[0]);
+ EXPECT_EQ(2, valueProducer.mPastBuckets.begin()->second[0].values[1].long_value);
+ EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].valueIndex[1]);
+
+ EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[1].mConditionTrueNs);
+ EXPECT_EQ(3, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value);
+ EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[1].valueIndex[0]);
+}
-// TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-// event1->write(1);
-// event1->write(10);
-// event1->init();
-// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
-// event2->write(1);
-// event2->write(20);
-// event2->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer.mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo
-// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(10,
-// curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue);
-//
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(30, curInterval.value.long_value);
-//
-// valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
-// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs});
-//}
-//
-// TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-// valueProducer.mCondition = ConditionState::kFalse;
-//
-// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-// event1->write(1);
-// event1->write(10);
-// event1->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-// // has 1 slice
-// EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
-//
-// valueProducer.onConditionChangedLocked(true, bucketStartTimeNs + 15);
-// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
-// event2->write(1);
-// event2->write(20);
-// event2->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(20, curInterval.value.long_value);
-//
-// shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 30);
-// event3->write(1);
-// event3->write(30);
-// event3->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(50, curInterval.value.long_value);
-//
-// valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
-// shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 40);
-// event4->write(1);
-// event4->write(40);
-// event4->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(50, curInterval.value.long_value);
-//
-// valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
-// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20});
-//}
-//
-// TEST(ValueMetricProducerTest, TestAnomalyDetection) {
-// sp<AlarmMonitor> alarmMonitor;
-// Alert alert;
-// alert.set_id(101);
-// alert.set_metric_id(metricId);
-// alert.set_trigger_if_sum_gt(130);
-// alert.set_num_buckets(2);
-// const int32_t refPeriodSec = 3;
-// alert.set_refractory_period_secs(refPeriodSec);
-//
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-// logEventMatcherIndex, eventMatcherWizard, -1 /*not pulled*/,
-// bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-//
-// sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
-//
-//
-// shared_ptr<LogEvent> event1
-// = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1 * NS_PER_SEC);
-// event1->write(161);
-// event1->write(10); // value of interest
-// event1->init();
-// shared_ptr<LogEvent> event2
-// = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 + NS_PER_SEC);
-// event2->write(162);
-// event2->write(20); // value of interest
-// event2->init();
-// shared_ptr<LogEvent> event3
-// = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC);
-// event3->write(163);
-// event3->write(130); // value of interest
-// event3->init();
-// shared_ptr<LogEvent> event4
-// = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC);
-// event4->write(35);
-// event4->write(1); // value of interest
-// event4->init();
-// shared_ptr<LogEvent> event5
-// = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
-// event5->write(45);
-// event5->write(150); // value of interest
-// event5->init();
-// shared_ptr<LogEvent> event6
-// = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 *
-// NS_PER_SEC);
-// event6->write(25);
-// event6->write(160); // value of interest
-// event6->init();
-//
-// // Two events in bucket #0.
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-// // Value sum == 30 <= 130.
-// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-//
-// // One event in bucket #2. No alarm as bucket #0 is trashed out.
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
-// // Value sum == 130 <= 130.
-// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-//
-// // Three events in bucket #3.
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
-// // Anomaly at event 4 since Value sum == 131 > 130!
-// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
-// std::ceil(1.0 * event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
-// // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
-// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
-// std::ceil(1.0 * event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
-//
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
-// // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
-// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
-// std::ceil(1.0 * event6->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
-//}
-//
-//// Test value metric no condition, the pull on bucket boundary come in time and too late
-// TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-// vector<shared_ptr<LogEvent>> allData;
-// // pull 1
-// allData.clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(11);
-// event->init();
-// allData.push_back(event);
-//
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo =
-// valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-// // startUpdated:true sum:0 start:11
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(11, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
-//
-// // pull 2 at correct time
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-// event->write(tagId);
-// event->write(23);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// // tartUpdated:false sum:12
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(23, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
-//
-// // pull 3 come late.
-// // The previous bucket gets closed with error. (Has start value 23, no ending)
-// // Another bucket gets closed with error. (No start, but ending with 36)
-// // The new bucket is back to normal.
-// allData.clear();
-// event = make_shared<LogEvent>(tagId, bucket6StartTimeNs + 1);
-// event->write(tagId);
-// event->write(36);
-// event->init();
-// allData.push_back(event);
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// // startUpdated:false sum:12
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(36, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
-//}
-//
-///*
-// * Test pulled event with non sliced condition. The pull on boundary come late because the alarm
-// * was delivered late.
-// */
-// TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // condition becomes true
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
-// event->write(tagId);
-// event->write(100);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// // condition becomes false
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(120);
-// event->init();
-// data->push_back(event);
-// return true;
-// }));
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
-// metric);
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo =
-// valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(100, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
-//
-// // pull on bucket boundary come late, condition change happens before it
-// valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
-// EXPECT_EQ(false, curBaseInfo.hasBase);
-//
-// // Now the alarm is delivered.
-// // since the condition turned to off before this pull finish, it has no effect
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
-// EXPECT_EQ(false, curInterval.hasValue);
-//}
-//
-///*
-// * Test pulled event with non sliced condition. The pull on boundary come late, after the
-// condition
-// * change to false, and then true again. This is due to alarm delivered late.
-// */
-// TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
-//
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-// EXPECT_CALL(*pullerManager, Pull(tagId, _))
-// // condition becomes true
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
-// event->write(tagId);
-// event->write(100);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// // condition becomes false
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-// event->write(tagId);
-// event->write(120);
-// event->init();
-// data->push_back(event);
-// return true;
-// }))
-// // condition becomes true again
-// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-// data->clear();
-// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
-// 25); event->write(tagId); event->write(130); event->init();
-// data->push_back(event);
-// return true;
-// }));
-//
-// sp<ValueMetricProducer> valueProducer =
-// ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
-// metric);
-//
-// valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo =
-// valueProducer->mCurrentBaseInfo.begin()->second[0];
-// // startUpdated:false sum:0 start:100
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(100, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
-//
-// // pull on bucket boundary come late, condition change happens before it
-// valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
-// EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(false, curBaseInfo.hasBase);
-// EXPECT_EQ(false, curInterval.hasValue);
-//
-// // condition changed to true again, before the pull alarm is delivered
-// valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(130, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-//
-// // Now the alarm is delivered, but it is considered late, the data will be used
-// // for the new bucket since it was just pulled.
-// vector<shared_ptr<LogEvent>> allData;
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 50, 140));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
-//
-// curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(140, curBaseInfo.base.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-// EXPECT_EQ(10, curInterval.value.long_value);
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
-//
-// allData.clear();
-// allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 160));
-// valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-// assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30},
-// {bucketSizeNs - 8, bucketSizeNs - 24});
-//}
-//
-// TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_aggregation_type(ValueMetric::MIN);
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-// event1->write(1);
-// event1->write(10);
-// event1->init();
-// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
-// event2->write(1);
-// event2->write(20);
-// event2->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(10, curInterval.value.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-//
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(10, curInterval.value.long_value);
-//
-// valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
-// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs});
-//}
-//
-// TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_aggregation_type(ValueMetric::MAX);
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-// event1->write(1);
-// event1->write(10);
-// event1->init();
-// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
-// event2->write(1);
-// event2->write(20);
-// event2->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(10, curInterval.value.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-//
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(20, curInterval.value.long_value);
-//
-// valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
-// /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); */
-// /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); */
-// /* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */
-//}
-//
-// TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_aggregation_type(ValueMetric::AVG);
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-// event1->write(1);
-// event1->write(10);
-// event1->init();
-// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
-// event2->write(1);
-// event2->write(15);
-// event2->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval;
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(10, curInterval.value.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-// EXPECT_EQ(1, curInterval.sampleSize);
-//
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(25, curInterval.value.long_value);
-// EXPECT_EQ(2, curInterval.sampleSize);
-//
-// valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-// EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-//
-// EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value
-// -
-// 12.5) < epsilon);
-//}
-//
-// TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_aggregation_type(ValueMetric::SUM);
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-// event1->write(1);
-// event1->write(10);
-// event1->init();
-// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
-// event2->write(1);
-// event2->write(15);
-// event2->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(10, curInterval.value.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-//
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(25, curInterval.value.long_value);
-//
-// valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
-// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs});
-//}
-//
-// TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.set_aggregation_type(ValueMetric::MIN);
-// metric.set_use_diff(true);
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-// event1->write(1);
-// event1->write(10);
-// event1->init();
-// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15);
-// event2->write(1);
-// event2->write(15);
-// event2->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(10, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-//
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// EXPECT_EQ(true, curInterval.hasValue);
-// EXPECT_EQ(5, curInterval.value.long_value);
-//
-// // no change in data.
-// shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-// event3->write(1);
-// event3->write(15);
-// event3->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(15, curBaseInfo.base.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-//
-// shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15);
-// event4->write(1);
-// event4->write(15);
-// event4->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(15, curBaseInfo.base.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-//
-// valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
-// EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-// EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-// assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs});
-//}
-//
-// TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
-// ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-// metric.mutable_value_field()->add_child()->set_field(3);
-// metric.set_aggregation_type(ValueMetric::MIN);
-// metric.set_use_diff(true);
-//
-// UidMap uidMap;
-// SimpleAtomMatcher atomMatcher;
-// atomMatcher.set_atom_id(tagId);
-// sp<EventMatcherWizard> eventMatcherWizard =
-// new EventMatcherWizard({new SimpleLogMatchingTracker(
-// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-// ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-// eventMatcherWizard, -1, bucketStartTimeNs,
-// bucketStartTimeNs, pullerManager);
-//
-// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-// event1->write(1);
-// event1->write(10);
-// event1->write(20);
-// event1->init();
-// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15);
-// event2->write(1);
-// event2->write(15);
-// event2->write(22);
-// event2->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// ValueMetricProducer::Interval curInterval =
-// valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(10, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(20, curBaseInfo.base.long_value);
-// EXPECT_EQ(false, curInterval.hasValue);
-//
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-//
-// // has one slice
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curInterval.hasValue);
-// EXPECT_EQ(5, curInterval.value.long_value);
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
-// EXPECT_EQ(true, curInterval.hasValue);
-// EXPECT_EQ(2, curInterval.value.long_value);
-//
-// // no change in first value field
-// shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-// event3->write(1);
-// event3->write(15);
-// event3->write(25);
-// event3->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
-//
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(15, curBaseInfo.base.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(25, curBaseInfo.base.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-//
-// shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15);
-// event4->write(1);
-// event4->write(15);
-// event4->write(29);
-// event4->init();
-// valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
-// EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(15, curBaseInfo.base.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-// curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-// curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
-// EXPECT_EQ(true, curBaseInfo.hasBase);
-// EXPECT_EQ(29, curBaseInfo.base.long_value);
-// EXPECT_EQ(true, curInterval.hasValue);
-//
-// valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
-//
-// EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-// EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
-// EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size());
-// EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size());
-//
-// EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[0].mConditionTrueNs);
-// EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
-// EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].valueIndex[0]);
-// EXPECT_EQ(2, valueProducer.mPastBuckets.begin()->second[0].values[1].long_value);
-// EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].valueIndex[1]);
-//
-// EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[1].mConditionTrueNs);
-// EXPECT_EQ(3, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value);
-// EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[1].valueIndex[0]);
-//}
-//
///*
// * Tests zero default base.
// */
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f4ee8faaf9bf..b3a0be1bec1e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3628,11 +3628,40 @@ public class ActivityManager {
}
}
+ /**
+ * Set custom state data for this process. It will be included in the record of
+ * {@link ApplicationExitInfo} on the death of the current calling process; the new process
+ * of the app can retrieve this state data by calling
+ * {@link ApplicationExitInfo#getProcessStateSummary} on the record returned by
+ * {@link #getHistoricalProcessExitReasons}.
+ *
+ * <p> This would be useful for the calling app to save its stateful data: if it's
+ * killed later for any reason, the new process of the app can know what the
+ * previous process of the app was doing. For instance, you could use this to encode
+ * the current level in a game, or a set of features/experiments that were enabled. Later you
+ * could analyze under what circumstances the app tends to crash or use too much memory.
+ * However, it's not suggested to rely on this to restore the applications previous UI state
+ * or so, it's only meant for analyzing application healthy status.</p>
+ *
+ * <p> System might decide to throttle the calls to this API; so call this API in a reasonable
+ * manner, excessive calls to this API could result a {@link java.lang.RuntimeException}.
+ * </p>
+ *
+ * @param state The state data
+ */
+ public void setProcessStateSummary(@Nullable byte[] state) {
+ try {
+ getService().setProcessStateSummary(state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/*
* @return Whether or not the low memory kill will be reported in
* {@link #getHistoricalProcessExitReasons}.
*
- * @see {@link ApplicationExitInfo#REASON_LOW_MEMORY}
+ * @see ApplicationExitInfo#REASON_LOW_MEMORY
*/
public static boolean isLowMemoryKillReportSupported() {
return SystemProperties.getBoolean("persist.sys.lmk.reportkills", false);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 4aacf4879c43..7fd0211215c5 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -209,6 +209,12 @@ public class ActivityOptions {
"android.activity.pendingIntentLaunchFlags";
/**
+ * See {@link #setTaskAlwaysOnTop}.
+ * @hide
+ */
+ private static final String KEY_TASK_ALWAYS_ON_TOP = "android.activity.alwaysOnTop";
+
+ /**
* See {@link #setTaskOverlay}.
* @hide
*/
@@ -337,6 +343,7 @@ public class ActivityOptions {
private int mSplitScreenCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
private boolean mLockTaskMode = false;
private boolean mDisallowEnterPictureInPictureWhileLaunching;
+ private boolean mTaskAlwaysOnTop;
private boolean mTaskOverlay;
private boolean mTaskOverlayCanResume;
private boolean mAvoidMoveToFront;
@@ -971,6 +978,7 @@ public class ActivityOptions {
mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED);
mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
mPendingIntentLaunchFlags = opts.getInt(KEY_PENDING_INTENT_LAUNCH_FLAGS, 0);
+ mTaskAlwaysOnTop = opts.getBoolean(KEY_TASK_ALWAYS_ON_TOP, false);
mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false);
@@ -1300,6 +1308,22 @@ public class ActivityOptions {
}
/**
+ * Set's whether the task for the activity launched with this option should always be on top.
+ * @hide
+ */
+ @TestApi
+ public void setTaskAlwaysOnTop(boolean alwaysOnTop) {
+ mTaskAlwaysOnTop = alwaysOnTop;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean getTaskAlwaysOnTop() {
+ return mTaskAlwaysOnTop;
+ }
+
+ /**
* Set's whether the activity launched with this option should be a task overlay. That is the
* activity will always be the top activity of the task.
* @param canResume {@code false} if the task will also not be moved to the front of the stack.
@@ -1556,6 +1580,9 @@ public class ActivityOptions {
if (mPendingIntentLaunchFlags != 0) {
b.putInt(KEY_PENDING_INTENT_LAUNCH_FLAGS, mPendingIntentLaunchFlags);
}
+ if (mTaskAlwaysOnTop) {
+ b.putBoolean(KEY_TASK_ALWAYS_ON_TOP, mTaskAlwaysOnTop);
+ }
if (mTaskOverlay) {
b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
}
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 5df3257f2444..61be01f9f6c0 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -23,7 +23,9 @@ import android.annotation.Nullable;
import android.app.ActivityManager.RunningAppProcessInfo.Importance;
import android.icu.text.SimpleDateFormat;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.DebugUtils;
@@ -31,12 +33,17 @@ import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
import android.util.proto.WireTypeMismatchException;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Date;
import java.util.Objects;
+import java.util.zip.GZIPInputStream;
/**
* Describes the information of an application process's death.
@@ -321,85 +328,105 @@ public final class ApplicationExitInfo implements Parcelable {
// be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000.
/**
- * @see {@link #getPid}
+ * @see #getPid
*/
private int mPid;
/**
- * @see {@link #getRealUid}
+ * @see #getRealUid
*/
private int mRealUid;
/**
- * @see {@link #getPackageUid}
+ * @see #getPackageUid
*/
private int mPackageUid;
/**
- * @see {@link #getDefiningUid}
+ * @see #getDefiningUid
*/
private int mDefiningUid;
/**
- * @see {@link #getProcessName}
+ * @see #getProcessName
*/
private String mProcessName;
/**
- * @see {@link #getReason}
+ * @see #getReason
*/
private @Reason int mReason;
/**
- * @see {@link #getStatus}
+ * @see #getStatus
*/
private int mStatus;
/**
- * @see {@link #getImportance}
+ * @see #getImportance
*/
private @Importance int mImportance;
/**
- * @see {@link #getPss}
+ * @see #getPss
*/
private long mPss;
/**
- * @see {@link #getRss}
+ * @see #getRss
*/
private long mRss;
/**
- * @see {@link #getTimestamp}
+ * @see #getTimestamp
*/
private @CurrentTimeMillisLong long mTimestamp;
/**
- * @see {@link #getDescription}
+ * @see #getDescription
*/
private @Nullable String mDescription;
/**
- * @see {@link #getSubReason}
+ * @see #getSubReason
*/
private @SubReason int mSubReason;
/**
- * @see {@link #getConnectionGroup}
+ * @see #getConnectionGroup
*/
private int mConnectionGroup;
/**
- * @see {@link #getPackageName}
+ * @see #getPackageName
*/
private String mPackageName;
/**
- * @see {@link #getPackageList}
+ * @see #getPackageList
*/
private String[] mPackageList;
+ /**
+ * @see #getProcessStateSummary
+ */
+ private byte[] mState;
+
+ /**
+ * The file to the trace file in the storage;
+ *
+ * for system internal use only, will not retain across processes.
+ *
+ * @see #getTraceInputStream
+ */
+ private File mTraceFile;
+
+ /**
+ * The Binder interface to retrieve the file descriptor to
+ * the trace file from the system.
+ */
+ private IAppTraceRetriever mAppTraceRetriever;
+
/** @hide */
@IntDef(prefix = { "REASON_" }, value = {
REASON_UNKNOWN,
@@ -557,6 +584,54 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
+ * Return the state data set by calling {@link ActivityManager#setProcessStateSummary}
+ * from the process before its death.
+ *
+ * @return The process-customized data
+ * @see ActivityManager#setProcessStateSummary(byte[])
+ */
+ public @Nullable byte[] getProcessStateSummary() {
+ return mState;
+ }
+
+ /**
+ * Return the InputStream to the traces that was taken by the system
+ * prior to the death of the process; typically it'll be available when
+ * the reason is {@link #REASON_ANR}, though if the process gets an ANR
+ * but recovers, and dies for another reason later, this trace will be included
+ * in the record of {@link ApplicationExitInfo} still.
+ *
+ * @return The input stream to the traces that was taken by the system
+ * prior to the death of the process.
+ */
+ public @Nullable InputStream getTraceInputStream() throws IOException {
+ if (mAppTraceRetriever == null) {
+ return null;
+ }
+ try {
+ final ParcelFileDescriptor fd = mAppTraceRetriever.getTraceFileDescriptor(
+ mPackageName, mPackageUid, mPid);
+ if (fd == null) {
+ return null;
+ }
+ return new GZIPInputStream(new ParcelFileDescriptor.AutoCloseInputStream(fd));
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Similar to {@link #getTraceInputStream} but return the File object.
+ *
+ * For internal use only.
+ *
+ * @hide
+ */
+ public @Nullable File getTraceFile() {
+ return mTraceFile;
+ }
+
+ /**
* A subtype reason in conjunction with {@link #mReason}.
*
* For internal use only.
@@ -569,7 +644,7 @@ public final class ApplicationExitInfo implements Parcelable {
/**
* The connection group this process belongs to, if there is any.
- * @see {@link android.content.Context#updateServiceGroup}.
+ * @see android.content.Context#updateServiceGroup
*
* For internal use only.
*
@@ -582,8 +657,6 @@ public final class ApplicationExitInfo implements Parcelable {
/**
* Name of first package running in this process;
*
- * For system internal use only, will not retain across processes.
- *
* @hide
*/
public String getPackageName() {
@@ -602,7 +675,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getPid}
+ * @see #getPid
*
* @hide
*/
@@ -611,7 +684,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getRealUid}
+ * @see #getRealUid
*
* @hide
*/
@@ -620,7 +693,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getPackageUid}
+ * @see #getPackageUid
*
* @hide
*/
@@ -629,7 +702,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getDefiningUid}
+ * @see #getDefiningUid
*
* @hide
*/
@@ -638,7 +711,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getProcessName}
+ * @see #getProcessName
*
* @hide
*/
@@ -647,7 +720,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getReason}
+ * @see #getReason
*
* @hide
*/
@@ -656,7 +729,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getStatus}
+ * @see #getStatus
*
* @hide
*/
@@ -665,7 +738,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getImportance}
+ * @see #getImportance
*
* @hide
*/
@@ -674,7 +747,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getPss}
+ * @see #getPss
*
* @hide
*/
@@ -683,7 +756,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getRss}
+ * @see #getRss
*
* @hide
*/
@@ -692,7 +765,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getTimestamp}
+ * @see #getTimestamp
*
* @hide
*/
@@ -701,7 +774,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getDescription}
+ * @see #getDescription
*
* @hide
*/
@@ -710,7 +783,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getSubReason}
+ * @see #getSubReason
*
* @hide
*/
@@ -719,7 +792,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getConnectionGroup}
+ * @see #getConnectionGroup
*
* @hide
*/
@@ -728,7 +801,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getPackageName}
+ * @see #getPackageName
*
* @hide
*/
@@ -737,7 +810,7 @@ public final class ApplicationExitInfo implements Parcelable {
}
/**
- * @see {@link #getPackageList}
+ * @see #getPackageList
*
* @hide
*/
@@ -745,6 +818,33 @@ public final class ApplicationExitInfo implements Parcelable {
mPackageList = packageList;
}
+ /**
+ * @see #getProcessStateSummary
+ *
+ * @hide
+ */
+ public void setProcessStateSummary(final byte[] state) {
+ mState = state;
+ }
+
+ /**
+ * @see #getTraceFile
+ *
+ * @hide
+ */
+ public void setTraceFile(final File traceFile) {
+ mTraceFile = traceFile;
+ }
+
+ /**
+ * @see #mAppTraceRetriever
+ *
+ * @hide
+ */
+ public void setAppTraceRetriever(final IAppTraceRetriever retriever) {
+ mAppTraceRetriever = retriever;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -757,6 +857,7 @@ public final class ApplicationExitInfo implements Parcelable {
dest.writeInt(mPackageUid);
dest.writeInt(mDefiningUid);
dest.writeString(mProcessName);
+ dest.writeString(mPackageName);
dest.writeInt(mConnectionGroup);
dest.writeInt(mReason);
dest.writeInt(mSubReason);
@@ -766,6 +867,13 @@ public final class ApplicationExitInfo implements Parcelable {
dest.writeLong(mRss);
dest.writeLong(mTimestamp);
dest.writeString(mDescription);
+ dest.writeByteArray(mState);
+ if (mAppTraceRetriever != null) {
+ dest.writeInt(1);
+ dest.writeStrongBinder(mAppTraceRetriever.asBinder());
+ } else {
+ dest.writeInt(0);
+ }
}
/** @hide */
@@ -779,6 +887,7 @@ public final class ApplicationExitInfo implements Parcelable {
mPackageUid = other.mPackageUid;
mDefiningUid = other.mDefiningUid;
mProcessName = other.mProcessName;
+ mPackageName = other.mPackageName;
mConnectionGroup = other.mConnectionGroup;
mReason = other.mReason;
mStatus = other.mStatus;
@@ -790,6 +899,9 @@ public final class ApplicationExitInfo implements Parcelable {
mDescription = other.mDescription;
mPackageName = other.mPackageName;
mPackageList = other.mPackageList;
+ mState = other.mState;
+ mTraceFile = other.mTraceFile;
+ mAppTraceRetriever = other.mAppTraceRetriever;
}
private ApplicationExitInfo(@NonNull Parcel in) {
@@ -798,6 +910,7 @@ public final class ApplicationExitInfo implements Parcelable {
mPackageUid = in.readInt();
mDefiningUid = in.readInt();
mProcessName = in.readString();
+ mPackageName = in.readString();
mConnectionGroup = in.readInt();
mReason = in.readInt();
mSubReason = in.readInt();
@@ -807,6 +920,10 @@ public final class ApplicationExitInfo implements Parcelable {
mRss = in.readLong();
mTimestamp = in.readLong();
mDescription = in.readString();
+ mState = in.createByteArray();
+ if (in.readInt() == 1) {
+ mAppTraceRetriever = IAppTraceRetriever.Stub.asInterface(in.readStrongBinder());
+ }
}
public @NonNull static final Creator<ApplicationExitInfo> CREATOR =
@@ -839,6 +956,9 @@ public final class ApplicationExitInfo implements Parcelable {
pw.print(prefix + " pss="); DebugUtils.printSizeValue(pw, mPss << 10); pw.println();
pw.print(prefix + " rss="); DebugUtils.printSizeValue(pw, mRss << 10); pw.println();
pw.println(prefix + " description=" + mDescription);
+ pw.println(prefix + " state=" + (ArrayUtils.isEmpty(mState)
+ ? "empty" : Integer.toString(mState.length) + " bytes"));
+ pw.println(prefix + " trace=" + mTraceFile);
}
@Override
@@ -859,6 +979,9 @@ public final class ApplicationExitInfo implements Parcelable {
sb.append(" pss="); DebugUtils.sizeValueToString(mPss << 10, sb);
sb.append(" rss="); DebugUtils.sizeValueToString(mRss << 10, sb);
sb.append(" description=").append(mDescription);
+ sb.append(" state=").append(ArrayUtils.isEmpty(mState)
+ ? "empty" : Integer.toString(mState.length) + " bytes");
+ sb.append(" trace=").append(mTraceFile);
return sb.toString();
}
@@ -961,6 +1084,9 @@ public final class ApplicationExitInfo implements Parcelable {
proto.write(ApplicationExitInfoProto.RSS, mRss);
proto.write(ApplicationExitInfoProto.TIMESTAMP, mTimestamp);
proto.write(ApplicationExitInfoProto.DESCRIPTION, mDescription);
+ proto.write(ApplicationExitInfoProto.STATE, mState);
+ proto.write(ApplicationExitInfoProto.TRACE_FILE,
+ mTraceFile == null ? null : mTraceFile.getAbsolutePath());
proto.end(token);
}
@@ -1019,6 +1145,15 @@ public final class ApplicationExitInfo implements Parcelable {
case (int) ApplicationExitInfoProto.DESCRIPTION:
mDescription = proto.readString(ApplicationExitInfoProto.DESCRIPTION);
break;
+ case (int) ApplicationExitInfoProto.STATE:
+ mState = proto.readBytes(ApplicationExitInfoProto.STATE);
+ break;
+ case (int) ApplicationExitInfoProto.TRACE_FILE:
+ final String path = proto.readString(ApplicationExitInfoProto.TRACE_FILE);
+ if (!TextUtils.isEmpty(path)) {
+ mTraceFile = new File(path);
+ }
+ break;
}
}
proto.end(token);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 6f0611e68cda..b8221b4efa2f 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -652,4 +652,27 @@ interface IActivityManager {
*/
void setActivityLocusContext(in ComponentName activity, in LocusId locusId,
in IBinder appToken);
+
+ /**
+ * Set custom state data for this process. It will be included in the record of
+ * {@link ApplicationExitInfo} on the death of the current calling process; the new process
+ * of the app can retrieve this state data by calling
+ * {@link ApplicationExitInfo#getProcessStateSummary} on the record returned by
+ * {@link #getHistoricalProcessExitReasons}.
+ *
+ * <p> This would be useful for the calling app to save its stateful data: if it's
+ * killed later for any reason, the new process of the app can know what the
+ * previous process of the app was doing. For instance, you could use this to encode
+ * the current level in a game, or a set of features/experiments that were enabled. Later you
+ * could analyze under what circumstances the app tends to crash or use too much memory.
+ * However, it's not suggested to rely on this to restore the applications previous UI state
+ * or so, it's only meant for analyzing application healthy status.</p>
+ *
+ * <p> System might decide to throttle the calls to this API; so call this API in a reasonable
+ * manner, excessive calls to this API could result a {@link java.lang.RuntimeException}.
+ * </p>
+ *
+ * @param state The customized state data
+ */
+ void setProcessStateSummary(in byte[] state);
}
diff --git a/core/java/android/app/IAppTraceRetriever.aidl b/core/java/android/app/IAppTraceRetriever.aidl
new file mode 100644
index 000000000000..1463da7db5d1
--- /dev/null
+++ b/core/java/android/app/IAppTraceRetriever.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.ParcelFileDescriptor;
+
+/**
+ * An interface that's to be used by {@link ApplicationExitInfo#getTraceFile()}
+ * to retrieve the actual file descriptor to its trace file.
+ *
+ * @hide
+ */
+interface IAppTraceRetriever {
+ /**
+ * Retrieve the trace file with given packageName/uid/pid.
+ *
+ * @param packagename The target package name of the trace
+ * @param uid The target UID of the trace
+ * @param pid The target PID of the trace
+ * @return The file descriptor to the trace file, or null if it's not found.
+ */
+ ParcelFileDescriptor getTraceFileDescriptor(in String packageName,
+ int uid, int pid);
+}
diff --git a/core/java/android/app/ITaskOrganizerController.aidl b/core/java/android/app/ITaskOrganizerController.aidl
index 9d6c3d64aaf2..a448e132bcc9 100644
--- a/core/java/android/app/ITaskOrganizerController.aidl
+++ b/core/java/android/app/ITaskOrganizerController.aidl
@@ -32,6 +32,11 @@ interface ITaskOrganizerController {
void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode);
/**
+ * Unregisters a previously registered task organizer.
+ */
+ void unregisterTaskOrganizer(ITaskOrganizer organizer);
+
+ /**
* Apply multiple WindowContainer operations at once.
* @param organizer If non-null this transaction will use the synchronization
* scheme described in BLASTSyncEngine.java. The SurfaceControl transaction
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e8f30df614f3..1aabd24d6df6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -359,14 +359,6 @@ public final class SystemServiceRegistry {
}
});
- registerService(Context.NETWORK_STACK_SERVICE, IBinder.class,
- new StaticServiceFetcher<IBinder>() {
- @Override
- public IBinder createService() {
- return ServiceManager.getService(Context.NETWORK_STACK_SERVICE);
- }
- });
-
registerService(Context.TETHERING_SERVICE, TetheringManager.class,
new CachedServiceFetcher<TetheringManager>() {
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 32e815e5b170..37f1a6559bfe 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6832,6 +6832,10 @@ public class DevicePolicyManager {
* package will no longer be suspended. The admin can block this by using
* {@link #setUninstallBlocked}.
*
+ * <p>Some apps cannot be suspended, such as device admins, the active launcher, the required
+ * package installer, the required package uninstaller, the required package verifier, the
+ * default dialer, and the permission controller.
+ *
* @param admin The name of the admin component to check, or {@code null} if the caller is a
* package access delegate.
* @param packageNames The package names to suspend or unsuspend.
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 0a67802fe92b..0d66198db038 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -204,16 +204,6 @@ public final class UsageStatsManager {
/** @hide */
public static final int REASON_SUB_MASK = 0x00FF;
/**
- * The reason for using the default main reason is unknown or undefined.
- * @hide
- */
- public static final int REASON_SUB_DEFAULT_UNDEFINED = 0x0000;
- /**
- * The app was updated.
- * @hide
- */
- public static final int REASON_SUB_DEFAULT_APP_UPDATE = 0x0001;
- /**
* The app was interacted with in some way by the system.
* @hide
*/
@@ -1079,14 +1069,6 @@ public final class UsageStatsManager {
switch (standbyReason & REASON_MAIN_MASK) {
case REASON_MAIN_DEFAULT:
sb.append("d");
- switch (subReason) {
- case REASON_SUB_DEFAULT_UNDEFINED:
- // Historically, undefined didn't have a string, so don't add anything here.
- break;
- case REASON_SUB_DEFAULT_APP_UPDATE:
- sb.append("-au");
- break;
- }
break;
case REASON_MAIN_FORCED_BY_SYSTEM:
sb.append("s");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 318ae11759db..6f4606659c3e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3430,7 +3430,7 @@ public abstract class Context {
TELEPHONY_SUBSCRIPTION_SERVICE,
CARRIER_CONFIG_SERVICE,
EUICC_SERVICE,
- MMS_SERVICE,
+ //@hide: MMS_SERVICE,
TELECOM_SERVICE,
CLIPBOARD_SERVICE,
INPUT_METHOD_SERVICE,
@@ -3984,8 +3984,6 @@ public abstract class Context {
* @hide
* @see NetworkStackClient
*/
- @SystemApi
- @TestApi
public static final String NETWORK_STACK_SERVICE = "network_stack";
/**
@@ -4344,6 +4342,7 @@ public abstract class Context {
*
* @see #getSystemService(String)
* @see android.telephony.MmsManager
+ * @hide
*/
public static final String MMS_SERVICE = "mms";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 38c1890cfc42..95385ee9b79c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3675,9 +3675,7 @@ public class Intent implements Parcelable, Cloneable {
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @SystemApi
+ @UnsupportedAppUsage
public static final String ACTION_USER_SWITCHED =
"android.intent.action.USER_SWITCHED";
diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
index 5011faafa2f7..9819b5d4eeb9 100644
--- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl
+++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
@@ -31,9 +31,7 @@ oneway interface IDataLoaderStatusListener {
const int DATA_LOADER_IMAGE_READY = 4;
const int DATA_LOADER_IMAGE_NOT_READY = 5;
- const int DATA_LOADER_SLOW_CONNECTION = 6;
- const int DATA_LOADER_NO_CONNECTION = 7;
- const int DATA_LOADER_CONNECTION_OK = 8;
+ const int DATA_LOADER_UNRECOVERABLE = 6;
/** Data loader status callback */
void onStatusChanged(in int dataLoaderId, in int status);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index e41ed85d2438..6f8acb6413d2 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -1820,8 +1820,8 @@ public class ParsingPackageUtils {
.setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
.setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
.setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
- .setDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_requestDontAutoRevokePermissions, sa))
- .setAllowDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_allowDontAutoRevokePermissions, sa))
+ .setDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_requestAutoRevokePermissionsExemption, sa))
+ .setAllowDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_allowAutoRevokePermissionsExemption, sa))
// targetSdkVersion gated
.setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
.setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 7e72b73db358..737fe60c1541 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2860,36 +2860,20 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
/**
- * <p>List of rotate-and-crop modes for {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop} that are supported by this camera device.</p>
- * <p>This entry lists the valid modes for {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop} for this camera device.</p>
- * <p>Starting with API level 30, all devices will list at least <code>ROTATE_AND_CROP_NONE</code>.
- * Devices with support for rotate-and-crop will additionally list at least
- * <code>ROTATE_AND_CROP_AUTO</code> and <code>ROTATE_AND_CROP_90</code>.</p>
- * <p><b>Range of valid values:</b><br>
- * Any value listed in {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop}</p>
- * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
- *
- * @see CaptureRequest#SCALER_ROTATE_AND_CROP
- */
- @PublicKey
- @NonNull
- public static final Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES =
- new Key<int[]>("android.scaler.availableRotateAndCropModes", int[].class);
- /**
* <p>An array of mandatory concurrent stream combinations.
* This is an app-readable conversion of the concurrent mandatory stream combination
* {@link android.hardware.camera2.CameraDevice#createCaptureSession tables}.</p>
* <p>The array of
* {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
* generated according to the documented
- * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each device
- * which has its Id present in the set returned by
- * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds}.
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for each
+ * device which has its Id present in the set returned by
+ * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds }.
* Clients can use the array as a quick reference to find an appropriate camera stream
* combination.
- * The mandatory stream combination array will be {@code null} in case the device is not a part
- * of at least one set of combinations returned by
- * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds}.</p>
+ * The mandatory stream combination array will be {@code null} in case the device is not a
+ * part of at least one set of combinations returned by
+ * {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds }.</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
*/
@PublicKey
@@ -2897,6 +2881,24 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
@SyntheticKey
public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS =
new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryConcurrentStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
+
+ /**
+ * <p>List of rotate-and-crop modes for {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop} that are supported by this camera device.</p>
+ * <p>This entry lists the valid modes for {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop} for this camera device.</p>
+ * <p>Starting with API level 30, all devices will list at least <code>ROTATE_AND_CROP_NONE</code>.
+ * Devices with support for rotate-and-crop will additionally list at least
+ * <code>ROTATE_AND_CROP_AUTO</code> and <code>ROTATE_AND_CROP_90</code>.</p>
+ * <p><b>Range of valid values:</b><br>
+ * Any value listed in {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop}</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#SCALER_ROTATE_AND_CROP
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES =
+ new Key<int[]>("android.scaler.availableRotateAndCropModes", int[].class);
+
/**
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 732ceb560cab..2c356e43d9fe 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -167,7 +167,19 @@ public final class LinkProperties implements Parcelable {
this(source, false /* parcelSensitiveFields */);
}
- private LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
+ /**
+ * Create a copy of a {@link LinkProperties} that may preserve fields that were set
+ * based on the permissions of the process that originally received it.
+ *
+ * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as
+ * they should not be shared outside of the process that receives them without appropriate
+ * checks.
+ * @param parcelSensitiveFields Whether the sensitive fields should be kept when parceling
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
mParcelSensitiveFields = parcelSensitiveFields;
if (source == null) return;
mIfaceName = source.mIfaceName;
@@ -674,17 +686,29 @@ public final class LinkProperties implements Parcelable {
route.getDestination(),
route.getGateway(),
mIfaceName,
- route.getType());
+ route.getType(),
+ route.getMtu());
+ }
+
+ private int findRouteIndexByDestination(RouteInfo route) {
+ for (int i = 0; i < mRoutes.size(); i++) {
+ if (mRoutes.get(i).isSameDestinationAs(route)) {
+ return i;
+ }
+ }
+ return -1;
}
/**
- * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the
- * {@link RouteInfo} had an interface name set and that differs from the interface set for this
- * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The proper
+ * Adds a {@link RouteInfo} to this {@code LinkProperties}, if a {@link RouteInfo}
+ * with the same destination exists with different properties (e.g., different MTU),
+ * it will be updated. If the {@link RouteInfo} had an interface name set and
+ * that differs from the interface set for this {@code LinkProperties} an
+ * {@link IllegalArgumentException} will be thrown. The proper
* course is to add either un-named or properly named {@link RouteInfo}.
*
* @param route A {@link RouteInfo} to add to this object.
- * @return {@code false} if the route was already present, {@code true} if it was added.
+ * @return {@code true} was added or updated, false otherwise.
*/
public boolean addRoute(@NonNull RouteInfo route) {
String routeIface = route.getInterface();
@@ -694,11 +718,20 @@ public final class LinkProperties implements Parcelable {
+ " vs. " + mIfaceName);
}
route = routeWithInterface(route);
- if (!mRoutes.contains(route)) {
+
+ int i = findRouteIndexByDestination(route);
+ if (i == -1) {
+ // Route was not present. Add it.
mRoutes.add(route);
return true;
+ } else if (mRoutes.get(i).equals(route)) {
+ // Route was present and has same properties. Do nothing.
+ return false;
+ } else {
+ // Route was present and has different properties. Update it.
+ mRoutes.set(i, route);
+ return true;
}
- return false;
}
/**
@@ -706,6 +739,7 @@ public final class LinkProperties implements Parcelable {
* specify an interface and the interface must match the interface of this
* {@code LinkProperties}, or it will not be removed.
*
+ * @param route A {@link RouteInfo} specifying the route to remove.
* @return {@code true} if the route was removed, {@code false} if it was not present.
*
* @hide
@@ -1561,22 +1595,6 @@ public final class LinkProperties implements Parcelable {
}
/**
- * Create a copy of this {@link LinkProperties} that will preserve fields that were set
- * based on the permissions of the process that received this {@link LinkProperties}.
- *
- * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as
- * they should not be shared outside of the process that receives them without appropriate
- * checks.
- * @hide
- */
- @SystemApi
- @TestApi
- @NonNull
- public LinkProperties makeSensitiveFieldsParcelingCopy() {
- return new LinkProperties(this, true /* parcelSensitiveFields */);
- }
-
- /**
* Compares this {@code LinkProperties} instance against the target
* LinkProperties in {@code obj}. Two LinkPropertieses are equal if
* all their fields are equal in values.
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index a46c410bd55e..a6d52d133ce9 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -19,15 +19,17 @@ import static android.Manifest.permission.NETWORK_STACK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
+import android.os.IBinder;
+import android.os.ServiceManager;
import java.util.ArrayList;
import java.util.Arrays;
/**
- *
- * Constants for client code communicating with the network stack service.
+ * Constants and utilities for client code communicating with the network stack service.
* @hide
*/
@SystemApi
@@ -43,6 +45,17 @@ public class NetworkStack {
public static final String PERMISSION_MAINLINE_NETWORK_STACK =
"android.permission.MAINLINE_NETWORK_STACK";
+ /**
+ * Get an {@link IBinder} representing the NetworkStack stable AIDL Interface, if registered.
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ @TestApi
+ public static IBinder getService() {
+ return ServiceManager.getService(Context.NETWORK_STACK_SERVICE);
+ }
+
private NetworkStack() {}
/**
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 2b9e9fe81b1b..fec2df412adb 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -527,6 +527,26 @@ public final class RouteInfo implements Parcelable {
}
/**
+ * Compares this RouteInfo object against the specified object and indicates if the
+ * destinations of both routes are equal.
+ * @return {@code true} if the route destinations are equal, {@code false} otherwise.
+ *
+ * @hide
+ */
+ public boolean isSameDestinationAs(@Nullable Object obj) {
+ if (this == obj) return true;
+
+ if (!(obj instanceof RouteInfo)) return false;
+
+ RouteInfo target = (RouteInfo) obj;
+
+ if (Objects.equals(mDestination, target.getDestination())) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Returns a hashcode for this <code>RouteInfo</code> object.
*/
public int hashCode() {
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 4a42230ad15a..82a7d788100d 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -58,6 +58,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -214,6 +215,7 @@ public abstract class PermissionControllerService extends Service {
@BinderThread
public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable callback);
+
/**
* Called by system to update the
* {@link PackageManager}{@code .FLAG_PERMISSION_USER_SENSITIVE_WHEN_*} flags for permissions.
@@ -223,13 +225,25 @@ public abstract class PermissionControllerService extends Service {
*
* Typically called by the system when a new app is installed or updated or when creating a
* new user or upgrading either system or permission controller package.
+ *
+ * The callback will be executed by the provided Executor.
*/
@BinderThread
- public void onUpdateUserSensitivePermissionFlags(int uid, @NonNull Runnable callback) {
+ public void onUpdateUserSensitivePermissionFlags(int uid, @NonNull Executor executor,
+ @NonNull Runnable callback) {
throw new AbstractMethodError("Must be overridden in implementing class");
}
/**
+ * Runs {@link #onUpdateUserSensitivePermissionFlags(int, Executor, Runnable)} with the main
+ * executor.
+ */
+ @BinderThread
+ public void onUpdateUserSensitivePermissionFlags(int uid, @NonNull Runnable callback) {
+ onUpdateUserSensitivePermissionFlags(uid, getMainExecutor(), callback);
+ }
+
+ /**
* Set the runtime permission state from a device admin.
*
* @param callerPackageName The package name of the admin requesting the change
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 06b5fa07a554..bc08b84d31fd 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -88,8 +88,6 @@ public final class FillResponse implements Parcelable {
private final @Nullable UserData mUserData;
private final @Nullable int[] mCancelIds;
private final boolean mSupportsInlineSuggestions;
- // TODO(b/149240554): revert back to use ParceledListSlice after the bug is resolved.
- private final @Nullable ArrayList<InlineAction> mInlineActions;
private FillResponse(@NonNull Builder builder) {
mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -109,7 +107,6 @@ public final class FillResponse implements Parcelable {
mUserData = builder.mUserData;
mCancelIds = builder.mCancelIds;
mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions;
- mInlineActions = builder.mInlineActions;
}
/** @hide */
@@ -212,11 +209,6 @@ public final class FillResponse implements Parcelable {
return mSupportsInlineSuggestions;
}
- /** @hide */
- public @Nullable List<InlineAction> getInlineActions() {
- return mInlineActions;
- }
-
/**
* Builder for {@link FillResponse} objects. You must to provide at least
* one dataset or set an authentication intent with a presentation view.
@@ -239,7 +231,6 @@ public final class FillResponse implements Parcelable {
private UserData mUserData;
private int[] mCancelIds;
private boolean mSupportsInlineSuggestions;
- private ArrayList<InlineAction> mInlineActions;
/**
* Triggers a custom UI before before autofilling the screen with any data set in this
@@ -656,22 +647,6 @@ public final class FillResponse implements Parcelable {
}
/**
- * Adds a new {@link InlineAction} to this response representing an action UI.
- *
- * @return This builder.
- */
- @NonNull
- public Builder addInlineAction(@NonNull InlineAction inlineAction) {
- throwIfDestroyed();
- throwIfAuthenticationCalled();
- if (mInlineActions == null) {
- mInlineActions = new ArrayList<>();
- }
- mInlineActions.add(inlineAction);
- return this;
- }
-
- /**
* Builds a new {@link FillResponse} instance.
*
* @throws IllegalStateException if any of the following conditions occur:
@@ -788,9 +763,6 @@ public final class FillResponse implements Parcelable {
builder.append(", mCancelIds=").append(mCancelIds.length);
}
builder.append(", mSupportInlinePresentations=").append(mSupportsInlineSuggestions);
- if (mInlineActions != null) {
- builder.append(", mInlineActions=" + mInlineActions);
- }
return builder.append("]").toString();
}
@@ -820,7 +792,6 @@ public final class FillResponse implements Parcelable {
parcel.writeParcelableArray(mFieldClassificationIds, flags);
parcel.writeInt(mFlags);
parcel.writeIntArray(mCancelIds);
- parcel.writeTypedList(mInlineActions, flags);
parcel.writeInt(mRequestId);
}
@@ -878,14 +849,6 @@ public final class FillResponse implements Parcelable {
final int[] cancelIds = parcel.createIntArray();
builder.setPresentationCancelIds(cancelIds);
- final List<InlineAction> inlineActions = parcel.createTypedArrayList(
- InlineAction.CREATOR);
- if (inlineActions != null) {
- for (InlineAction inlineAction : inlineActions) {
- builder.addInlineAction(inlineAction);
- }
- }
-
final FillResponse response = builder.build();
response.setRequestId(parcel.readInt());
diff --git a/core/java/android/service/autofill/InlineAction.aidl b/core/java/android/service/autofill/InlineAction.aidl
deleted file mode 100644
index 7f8511825d73..000000000000
--- a/core/java/android/service/autofill/InlineAction.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2020 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.service.autofill;
-
-parcelable InlineAction;
diff --git a/core/java/android/service/autofill/InlineAction.java b/core/java/android/service/autofill/InlineAction.java
deleted file mode 100644
index 17c4b33ba96b..000000000000
--- a/core/java/android/service/autofill/InlineAction.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2020 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.service.autofill;
-
-import android.annotation.NonNull;
-import android.content.IntentSender;
-import android.os.Parcelable;
-
-import com.android.internal.util.DataClass;
-
-/**
- * Represents an inline action as part of the autofill/augmented autofill response.
- *
- * <p> It includes both the action intent and the UI presentation. For example, the UI can be
- * associated with an intent which can open an activity for the user to manage the Autofill provider
- * settings.
- */
-@DataClass(
- genToString = true,
- genHiddenConstDefs = true,
- genEqualsHashCode = true)
-public final class InlineAction implements Parcelable {
-
- /**
- * Representation of the inline action.
- */
- private final @NonNull InlinePresentation mInlinePresentation;
-
- /**
- * The associated intent which will be triggered when the action is selected. It will only be
- * called by the OS.
- */
- private final @NonNull IntentSender mAction;
-
-
-
- // Code below generated by codegen v1.0.15.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/InlineAction.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- /**
- * Creates a new InlineAction.
- *
- * @param inlinePresentation
- * Representation of the inline action.
- * @param action
- * The associated intent which will be triggered when the action is selected. It will only be
- * invoked by the OS.
- */
- @DataClass.Generated.Member
- public InlineAction(
- @NonNull InlinePresentation inlinePresentation,
- @NonNull IntentSender action) {
- this.mInlinePresentation = inlinePresentation;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mInlinePresentation);
- this.mAction = action;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mAction);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- /**
- * Representation of the inline action.
- */
- @DataClass.Generated.Member
- public @NonNull InlinePresentation getInlinePresentation() {
- return mInlinePresentation;
- }
-
- /**
- * The associated intent which will be triggered when the action is selected. It will only be
- * invoked by the OS.
- */
- @DataClass.Generated.Member
- public @NonNull IntentSender getAction() {
- return mAction;
- }
-
- @Override
- @DataClass.Generated.Member
- public String toString() {
- // You can override field toString logic by defining methods like:
- // String fieldNameToString() { ... }
-
- return "InlineAction { " +
- "inlinePresentation = " + mInlinePresentation + ", " +
- "action = " + mAction +
- " }";
- }
-
- @Override
- @DataClass.Generated.Member
- public boolean equals(@android.annotation.Nullable Object o) {
- // You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(InlineAction other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
- InlineAction that = (InlineAction) o;
- //noinspection PointlessBooleanExpression
- return true
- && java.util.Objects.equals(mInlinePresentation, that.mInlinePresentation)
- && java.util.Objects.equals(mAction, that.mAction);
- }
-
- @Override
- @DataClass.Generated.Member
- public int hashCode() {
- // You can override field hashCode logic by defining methods like:
- // int fieldNameHashCode() { ... }
-
- int _hash = 1;
- _hash = 31 * _hash + java.util.Objects.hashCode(mInlinePresentation);
- _hash = 31 * _hash + java.util.Objects.hashCode(mAction);
- return _hash;
- }
-
- @Override
- @DataClass.Generated.Member
- public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- dest.writeTypedObject(mInlinePresentation, flags);
- dest.writeTypedObject(mAction, flags);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ InlineAction(@NonNull android.os.Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- InlinePresentation inlinePresentation = (InlinePresentation) in.readTypedObject(InlinePresentation.CREATOR);
- IntentSender action = (IntentSender) in.readTypedObject(IntentSender.CREATOR);
-
- this.mInlinePresentation = inlinePresentation;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mInlinePresentation);
- this.mAction = action;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mAction);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<InlineAction> CREATOR
- = new Parcelable.Creator<InlineAction>() {
- @Override
- public InlineAction[] newArray(int size) {
- return new InlineAction[size];
- }
-
- @Override
- public InlineAction createFromParcel(@NonNull android.os.Parcel in) {
- return new InlineAction(in);
- }
- };
-
- @DataClass.Generated(
- time = 1583798182424L,
- codegenVersion = "1.0.15",
- sourceFile = "frameworks/base/core/java/android/service/autofill/InlineAction.java",
- inputSignatures = "private final @android.annotation.NonNull android.service.autofill.InlinePresentation mInlinePresentation\nprivate final @android.annotation.NonNull android.content.IntentSender mAction\nclass InlineAction extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 5b08ae20f071..1efa3dd0c865 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -40,7 +40,6 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.Dataset;
import android.service.autofill.FillEventHistory;
-import android.service.autofill.InlineAction;
import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
import android.util.Log;
import android.util.Pair;
@@ -559,10 +558,9 @@ public abstract class AugmentedAutofillService extends Service {
}
}
- void reportResult(@Nullable List<Dataset> inlineSuggestionsData,
- @Nullable List<InlineAction> inlineActions) {
+ void reportResult(@Nullable List<Dataset> inlineSuggestionsData) {
try {
- mCallback.onSuccess(inlineSuggestionsData, inlineActions);
+ mCallback.onSuccess(inlineSuggestionsData);
} catch (RemoteException e) {
Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
}
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 6b4e1185703c..526a749c95a1 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -55,14 +55,14 @@ public final class FillCallback {
if (response == null) {
mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
- mProxy.reportResult(/* inlineSuggestionsData */ null, /* inlineActions */null);
+ mProxy.reportResult(/* inlineSuggestionsData */ null);
return;
}
List<Dataset> inlineSuggestions = response.getInlineSuggestions();
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
- mProxy.reportResult(inlineSuggestions, response.getInlineActions());
+ mProxy.reportResult(inlineSuggestions);
return;
}
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index f564b3ba616a..f72eb782c407 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -21,7 +21,6 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Bundle;
import android.service.autofill.Dataset;
-import android.service.autofill.InlineAction;
import com.android.internal.util.DataClass;
@@ -53,12 +52,6 @@ public final class FillResponse {
private @Nullable List<Dataset> mInlineSuggestions;
/**
- * Defaults to null if no inline actions are provided.
- */
- @DataClass.PluralOf("inlineAction")
- private @Nullable List<InlineAction> mInlineActions;
-
- /**
* The client state that {@link AugmentedAutofillService} implementation can put anything in to
* identify the request and the response when calling
* {@link AugmentedAutofillService#getFillEventHistory()}.
@@ -73,10 +66,6 @@ public final class FillResponse {
return null;
}
- private static List<InlineAction> defaultInlineActions() {
- return null;
- }
-
private static Bundle defaultClientState() {
return null;
}
@@ -85,7 +74,6 @@ public final class FillResponse {
/** @hide */
abstract static class BaseBuilder {
abstract FillResponse.Builder addInlineSuggestion(@NonNull Dataset value);
- abstract FillResponse.Builder addInlineAction(@NonNull InlineAction value);
}
@@ -107,11 +95,9 @@ public final class FillResponse {
/* package-private */ FillResponse(
@Nullable FillWindow fillWindow,
@Nullable List<Dataset> inlineSuggestions,
- @Nullable List<InlineAction> inlineActions,
@Nullable Bundle clientState) {
this.mFillWindow = fillWindow;
this.mInlineSuggestions = inlineSuggestions;
- this.mInlineActions = inlineActions;
this.mClientState = clientState;
// onConstructed(); // You can define this method to get a callback
@@ -139,16 +125,6 @@ public final class FillResponse {
}
/**
- * Defaults to null if no inline actions are provided.
- *
- * @hide
- */
- @DataClass.Generated.Member
- public @Nullable List<InlineAction> getInlineActions() {
- return mInlineActions;
- }
-
- /**
* The client state that {@link AugmentedAutofillService} implementation can put anything in to
* identify the request and the response when calling
* {@link AugmentedAutofillService#getFillEventHistory()}.
@@ -169,7 +145,6 @@ public final class FillResponse {
private @Nullable FillWindow mFillWindow;
private @Nullable List<Dataset> mInlineSuggestions;
- private @Nullable List<InlineAction> mInlineActions;
private @Nullable Bundle mClientState;
private long mBuilderFieldsSet = 0L;
@@ -210,26 +185,6 @@ public final class FillResponse {
}
/**
- * Defaults to null if no inline actions are provided.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setInlineActions(@NonNull List<InlineAction> value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4;
- mInlineActions = value;
- return this;
- }
-
- /** @see #setInlineActions */
- @DataClass.Generated.Member
- @Override
- @NonNull FillResponse.Builder addInlineAction(@NonNull InlineAction value) {
- if (mInlineActions == null) setInlineActions(new ArrayList<>());
- mInlineActions.add(value);
- return this;
- }
-
- /**
* The client state that {@link AugmentedAutofillService} implementation can put anything in to
* identify the request and the response when calling
* {@link AugmentedAutofillService#getFillEventHistory()}.
@@ -237,7 +192,7 @@ public final class FillResponse {
@DataClass.Generated.Member
public @NonNull Builder setClientState(@NonNull Bundle value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x8;
+ mBuilderFieldsSet |= 0x4;
mClientState = value;
return this;
}
@@ -245,7 +200,7 @@ public final class FillResponse {
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull FillResponse build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x10; // Mark builder used
+ mBuilderFieldsSet |= 0x8; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mFillWindow = defaultFillWindow();
@@ -254,21 +209,17 @@ public final class FillResponse {
mInlineSuggestions = defaultInlineSuggestions();
}
if ((mBuilderFieldsSet & 0x4) == 0) {
- mInlineActions = defaultInlineActions();
- }
- if ((mBuilderFieldsSet & 0x8) == 0) {
mClientState = defaultClientState();
}
FillResponse o = new FillResponse(
mFillWindow,
mInlineSuggestions,
- mInlineActions,
mClientState);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x10) != 0) {
+ if ((mBuilderFieldsSet & 0x8) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -276,10 +227,10 @@ public final class FillResponse {
}
@DataClass.Generated(
- time = 1583793032373L,
+ time = 1584480900526L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/service/autofill/augmented/FillResponse.java",
- inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineAction\") @android.annotation.Nullable java.util.List<android.service.autofill.InlineAction> mInlineActions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static java.util.List<android.service.autofill.InlineAction> defaultInlineActions()\nprivate static android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineAction(android.service.autofill.InlineAction)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index 545dab284067..24af1e51dd56 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -20,7 +20,6 @@ import android.os.Bundle;
import android.os.ICancellationSignal;
import android.service.autofill.Dataset;
-import android.service.autofill.InlineAction;
import java.util.List;
@@ -31,8 +30,7 @@ import java.util.List;
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
- void onSuccess(in @nullable List<Dataset> inlineSuggestionsData,
- in @nullable List<InlineAction> inlineActions);
+ void onSuccess(in @nullable List<Dataset> inlineSuggestionsData);
boolean isCompleted();
void cancel();
}
diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEvent.java b/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
index fb524be852fa..5ee92da59164 100644
--- a/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
+++ b/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
@@ -40,10 +40,16 @@ public final class WalletServiceEvent implements Parcelable {
public static final int TYPE_NFC_PAYMENT_STARTED = 1;
/**
+ * Indicates that the wallet cards have changed and should be refreshed.
+ * @hide
+ */
+ public static final int TYPE_WALLET_CARDS_UPDATED = 2;
+
+ /**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({TYPE_NFC_PAYMENT_STARTED})
+ @IntDef({TYPE_NFC_PAYMENT_STARTED, TYPE_WALLET_CARDS_UPDATED})
public @interface EventType {
}
diff --git a/core/java/android/text/style/ReplacementSpan.java b/core/java/android/text/style/ReplacementSpan.java
index 05532326cd84..9430fd3a26c0 100644
--- a/core/java/android/text/style/ReplacementSpan.java
+++ b/core/java/android/text/style/ReplacementSpan.java
@@ -63,7 +63,7 @@ public abstract class ReplacementSpan extends MetricAffectingSpan {
int top, int y, int bottom, @NonNull Paint paint);
/**
- * Gets a brief description of this ImageSpan for use in accessibility support.
+ * Gets a brief description of this ReplacementSpan for use in accessibility support.
*
* @return The content description.
*/
@@ -73,7 +73,7 @@ public abstract class ReplacementSpan extends MetricAffectingSpan {
}
/**
- * Sets the specific content description into ImageSpan.
+ * Sets the specific content description into ReplacementSpan.
* ReplacementSpans are shared with accessibility services,
* but only the content description is available from them.
*
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index b587fbe24767..cd22ad6151b8 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -98,7 +98,10 @@ public class SurfaceControlViewHost {
}
/**
- * Release the SurfaceControl associated with the SurfacePackage.
+ * Release the {@link SurfaceControl} associated with this package.
+ * It's not necessary to call this if you pass the package to
+ * {@link SurfaceView#setChildSurfacePackage} as {@link SurfaceView} will
+ * take ownership in that case.
*/
public void release() {
if (mSurfaceControl != null) {
@@ -230,7 +233,7 @@ public class SurfaceControlViewHost {
* and render the object unusable.
*/
public void release() {
- mViewRoot.dispatchDetachedFromWindow();
+ mViewRoot.die(false /* immediate */);
mSurfaceControl.release();
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1f7c3504560f..3e1e3939d570 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -496,8 +496,17 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
updateSurface();
releaseSurfaces();
- mHaveFrame = false;
+ // We don't release this as part of releaseSurfaces as
+ // that is also called on transient visibility changes. We can't
+ // recreate this Surface, so only release it when we are fully
+ // detached.
+ if (mSurfacePackage != null) {
+ mSurfacePackage.release();
+ mSurfacePackage = null;
+ }
+
+ mHaveFrame = false;
super.onDetachedFromWindow();
}
@@ -1546,7 +1555,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* Display the view-hierarchy embedded within a {@link SurfaceControlViewHost.SurfacePackage}
* within this SurfaceView. If this SurfaceView is above it's host Surface (see
* {@link #setZOrderOnTop} then the embedded Surface hierarchy will be able to receive
- * input.
+ * input. This will take ownership of the SurfaceControl contained inside the SurfacePackage
+ * and free the caller of the obligation to call
+ * {@link SurfaceControlViewHost.SurfacePackage#release}.
*
* @param p The SurfacePackage to embed.
*/
@@ -1556,6 +1567,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mSurfacePackage.getSurfaceControl() : null;
if (mSurfaceControl != null && lastSc != null) {
mTmpTransaction.reparent(lastSc, null).apply();
+ mSurfacePackage.release();
} else if (mSurfaceControl != null) {
reparentSurfacePackage(mTmpTransaction, p);
mTmpTransaction.apply();
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 5fccf4031008..4980b335646b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -2876,7 +2876,7 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
- * Replaces any ClickableSpans in mText with placeholders.
+ * Replaces any ClickableSpan in the given {@code text} with placeholders.
*
* @param text The text.
*
@@ -2910,7 +2910,7 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
- * Replace any ImageSpans in mText with its content description.
+ * Replaces any ReplacementSpan in the given {@code text} if the object has content description.
*
* @param text The text.
*
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 728824c2f0dc..d661bc68ab06 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -24,7 +24,6 @@ import android.text.TextUtils;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityNodeInfo;
/*
* This is supposed to be a *very* thin veneer over TextView.
@@ -179,13 +178,4 @@ public class EditText extends TextView {
protected boolean supportsAutoSizeText() {
return false;
}
-
- /** @hide */
- @Override
- public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfoInternal(info);
- if (isEnabled()) {
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT);
- }
- }
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 16d9ed369f54..4aeea10eee68 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -317,6 +317,7 @@ public class Editor {
private SelectionActionModeHelper mSelectionActionModeHelper;
boolean mIsBeingLongClicked;
+ boolean mIsBeingLongClickedByAccessibility;
private SuggestionsPopupWindow mSuggestionsPopupWindow;
SuggestionRangeSpan mSuggestionRangeSpan;
@@ -1312,6 +1313,12 @@ public class Editor {
if (TextView.DEBUG_CURSOR) {
logCursor("performLongClick", "handled=%s", handled);
}
+ if (mIsBeingLongClickedByAccessibility) {
+ if (!handled) {
+ toggleInsertionActionMode();
+ }
+ return true;
+ }
// Long press in empty space moves cursor and starts the insertion action mode.
if (!handled && !isPositionOnText(mTouchState.getLastDownX(), mTouchState.getLastDownY())
&& !mTouchState.isOnHandle() && mInsertionControllerEnabled) {
@@ -1359,6 +1366,14 @@ public class Editor {
return handled;
}
+ private void toggleInsertionActionMode() {
+ if (mTextActionMode != null) {
+ stopTextActionMode();
+ } else {
+ startInsertionActionMode();
+ }
+ }
+
float getLastUpPositionX() {
return mTouchState.getLastUpX();
}
@@ -5436,11 +5451,7 @@ public class Editor {
config.getScaledTouchSlop());
if (isWithinTouchSlop) {
// Tapping on the handle toggles the insertion action mode.
- if (mTextActionMode != null) {
- stopTextActionMode();
- } else {
- startInsertionActionMode();
- }
+ toggleInsertionActionMode();
}
} else {
if (mTextActionMode != null) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2168018e12be..e1783181457f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12108,6 +12108,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
onEditorAction(getImeActionId());
}
} return true;
+ case AccessibilityNodeInfo.ACTION_LONG_CLICK: {
+ if (isLongClickable()) {
+ boolean handled;
+ if (isEnabled() && (mBufferType == BufferType.EDITABLE)) {
+ mEditor.mIsBeingLongClickedByAccessibility = true;
+ try {
+ handled = performLongClick();
+ } finally {
+ mEditor.mIsBeingLongClickedByAccessibility = false;
+ }
+ } else {
+ handled = performLongClick();
+ }
+ return handled;
+ }
+ }
+ return false;
default: {
return super.performAccessibilityActionInternal(action, arguments);
}
diff --git a/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
index 9aee879f21da..ef8d0184c6ed 100644
--- a/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
+++ b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
@@ -50,6 +50,16 @@ public class ImsUceManager {
public static final String ACTION_UCE_SERVICE_DOWN =
"com.android.ims.internal.uce.UCE_SERVICE_DOWN";
+ /**
+ * Uce Service status received in IUceListener.setStatus() callback
+ */
+ public static final int UCE_SERVICE_STATUS_FAILURE = 0;
+ /** indicate UI to call Presence/Options API. */
+ public static final int UCE_SERVICE_STATUS_ON = 1;
+ /** Indicate UI destroy Presence/Options */
+ public static final int UCE_SERVICE_STATUS_CLOSED = 2;
+ /** Service up and trying to register for network events */
+ public static final int UCE_SERVICE_STATUS_READY = 3;
/**
* Gets the instance of UCE Manager
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index 9ba025988126..ab890d277f43 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -93,43 +93,6 @@ public class CompatibilityChangeInfo implements Parcelable {
dest.writeString(mDescription);
}
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("CompatibilityChangeInfo(")
- .append(getId());
- if (getName() != null) {
- sb.append("; name=").append(getName());
- }
- if (getEnableAfterTargetSdk() != -1) {
- sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk());
- }
- if (getDisabled()) {
- sb.append("; disabled");
- }
- if (getLoggingOnly()) {
- sb.append("; loggingOnly");
- }
- return sb.append(")").toString();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || !(o instanceof CompatibilityChangeInfo)) {
- return false;
- }
- CompatibilityChangeInfo that = (CompatibilityChangeInfo) o;
- return this.mChangeId == that.mChangeId
- && this.mName.equals(that.mName)
- && this.mEnableAfterTargetSdk == that.mEnableAfterTargetSdk
- && this.mDisabled == that.mDisabled
- && this.mLoggingOnly == that.mLoggingOnly
- && this.mDescription.equals(that.mDescription);
-
- }
-
public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR =
new Parcelable.Creator<CompatibilityChangeInfo>() {
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 6408def7eeac..523ed6fa9a4f 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -222,14 +222,6 @@ interface IPlatformCompat
CompatibilityChangeInfo[] listAllChanges();
/**
- * List the compatibility changes that should be present in the UI.
- * Filters out certain changes like e.g. logging only.
- *
- * @return An array of {@link CompatChangeInfo}.
- */
- CompatibilityChangeInfo[] listUIChanges();
-
- /**
* Get an instance that can determine whether a changeid can be overridden for a package name.
*/
IOverrideValidator getOverrideValidator();
diff --git a/core/proto/android/app/appexitinfo.proto b/core/proto/android/app/appexitinfo.proto
index 66173f60bfd5..4b9444e83e1c 100644
--- a/core/proto/android/app/appexitinfo.proto
+++ b/core/proto/android/app/appexitinfo.proto
@@ -42,4 +42,6 @@ message ApplicationExitInfoProto {
optional int64 rss = 12;
optional int64 timestamp = 13;
optional string description = 14;
+ optional bytes state = 15;
+ optional string trace_file = 16;
}
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 93b20638e7df..2496900f632e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1832,7 +1832,7 @@
revoked when the app is unused for an extended amount of time.
The default value is {@code false}. -->
- <attr name="requestDontAutoRevokePermissions" format="boolean" />
+ <attr name="requestAutoRevokePermissionsExemption" format="boolean" />
<!-- If {@code true} its permissions shouldn't get automatically
revoked when the app is unused for an extended amount of time.
@@ -1840,7 +1840,7 @@
This implies {@code requestDontAutoRevokePermissions=true}
The default value is {@code false}. -->
- <attr name="allowDontAutoRevokePermissions" format="boolean" />
+ <attr name="allowAutoRevokePermissionsExemption" format="boolean" />
</declare-styleable>
<!-- An attribution is a logical part of an app and is identified by a tag.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index cf68aff43c05..5306518af91a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3014,8 +3014,8 @@
<!-- @hide @SystemApi -->
<public name="minExtensionVersion" />
<public name="allowNativeHeapPointerTagging" />
- <public name="requestDontAutoRevokePermissions" />
- <public name="allowDontAutoRevokePermissions" />
+ <public name="requestAutoRevokePermissionsExemption" />
+ <public name="allowAutoRevokePermissionsExemption" />
<public name="preserveLegacyExternalStorage" />
<public name="mimeGroup" />
<public name="enableGwpAsan" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 26024ed34bc3..2040bed24827 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4444,6 +4444,20 @@
<string name="accessibility_shortcut_spoken_feedback">Press and hold both volume keys for three seconds to use
<xliff:g id="service_name" example="TalkBack">%1$s</xliff:g></string>
+ <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. [CHAR LIMIT=none]-->
+ <string name="accessibility_button_prompt_text">Choose a app to use when you tap the accessibility button:</string>
+ <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button when gesture navigation is enabled [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_prompt_text">Choose a app to use with the accessibility gesture (swipe up from the bottom of the screen with two fingers):</string>
+ <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button when gesture navigation and TalkBack is enabled [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_3finger_prompt_text">Choose a app to use with the accessibility gesture (swipe up from the bottom of the screen with three fingers):</string>
+
+ <!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. [CHAR LIMIT=none]-->
+ <string name="accessibility_button_instructional_text">To switch between apps, touch &amp; hold the accessibility button.</string>
+ <!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button when gesture navigation is enabled. [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_instructional_text">To switch between apps, swipe up with two fingers and hold.</string>
+ <!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button when gesture navigation and TalkBack is enabled. [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_3finger_instructional_text">To switch between apps, swipe up with three fingers and hold.</string>
+
<!-- Text used to describe system navigation features, shown within a UI allowing a user to assign system magnification features to the Accessibility button in the navigation bar. -->
<string name="accessibility_magnification_chooser_text">Magnification</string>
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index a72be25fedb5..45d4b38d82aa 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -65,6 +65,7 @@ import android.app.Activity;
import android.app.Instrumentation;
import android.content.ClipData;
import android.content.ClipboardManager;
+import android.os.Bundle;
import android.support.test.uiautomator.UiDevice;
import android.text.InputType;
import android.text.Selection;
@@ -75,6 +76,7 @@ import android.view.ActionMode;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
@@ -358,6 +360,20 @@ public class TextViewActivityTest {
}
@Test
+ public void testToolbarAppearsAccessibilityLongClick() throws Throwable {
+ final String text = "Toolbar appears after performing accessibility's ACTION_LONG_CLICK.";
+ mActivityRule.runOnUiThread(() -> {
+ final TextView textView = mActivity.findViewById(R.id.textview);
+ final Bundle args = new Bundle();
+ textView.performAccessibilityAction(AccessibilityNodeInfo.ACTION_LONG_CLICK, args);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ }
+
+ @Test
public void testSelectionRemovedWhenNonselectableTextLosesFocus() throws Throwable {
final TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
final int position = (textLink.getStart() + textLink.getEnd()) / 2;
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 5466ac8e2cfa..87b081796c27 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -36,11 +36,6 @@ applications that come with the platform
<permission name="android.permission.CRYPT_KEEPER"/>
</privapp-permissions>
- <privapp-permissions package="com.android.captiveportallogin">
- <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
- <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.cellbroadcastreceiver">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.MANAGE_USERS"/>
@@ -400,6 +395,8 @@ applications that come with the platform
<permission name="android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS" />
<!-- Permission required for testing registering pull atom callbacks. -->
<permission name="android.permission.REGISTER_STATS_PULL_ATOM"/>
+ <!-- Permission required for testing system audio effect APIs. -->
+ <permission name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java
index 3dc7cfce2d92..36b86899f2d8 100644
--- a/location/java/android/location/AbstractListenerManager.java
+++ b/location/java/android/location/AbstractListenerManager.java
@@ -85,11 +85,13 @@ abstract class AbstractListenerManager<TRequest, TListener> {
}
}
- @GuardedBy("mListeners")
- private final ArrayMap<Object, Registration<TRequest, TListener>> mListeners =
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private volatile ArrayMap<Object, Registration<TRequest, TListener>> mListeners =
new ArrayMap<>();
- @GuardedBy("mListeners")
+ @GuardedBy("mLock")
@Nullable
private TRequest mMergedRequest;
@@ -129,10 +131,16 @@ abstract class AbstractListenerManager<TRequest, TListener> {
throws RemoteException {
Preconditions.checkNotNull(registration);
- synchronized (mListeners) {
+ synchronized (mLock) {
boolean initialRequest = mListeners.isEmpty();
- Registration<TRequest, TListener> oldRegistration = mListeners.put(key, registration);
+ ArrayMap<Object, Registration<TRequest, TListener>> newListeners = new ArrayMap<>(
+ mListeners.size() + 1);
+ newListeners.putAll(mListeners);
+ Registration<TRequest, TListener> oldRegistration = newListeners.put(key,
+ registration);
+ mListeners = newListeners;
+
if (oldRegistration != null) {
oldRegistration.unregister();
}
@@ -151,8 +159,12 @@ abstract class AbstractListenerManager<TRequest, TListener> {
}
public void removeListener(Object listener) throws RemoteException {
- synchronized (mListeners) {
- Registration<TRequest, TListener> oldRegistration = mListeners.remove(listener);
+ synchronized (mLock) {
+ ArrayMap<Object, Registration<TRequest, TListener>> newListeners = new ArrayMap<>(
+ mListeners);
+ Registration<TRequest, TListener> oldRegistration = newListeners.remove(listener);
+ mListeners = newListeners;
+
if (oldRegistration == null) {
return;
}
@@ -190,18 +202,16 @@ abstract class AbstractListenerManager<TRequest, TListener> {
}
protected void execute(Consumer<TListener> operation) {
- synchronized (mListeners) {
- for (Registration<TRequest, TListener> registration : mListeners.values()) {
- registration.execute(operation);
- }
+ for (Registration<TRequest, TListener> registration : mListeners.values()) {
+ registration.execute(operation);
}
}
- @GuardedBy("mListeners")
+ @GuardedBy("mLock")
@SuppressWarnings("unchecked")
@Nullable
private TRequest mergeRequests() {
- Preconditions.checkState(Thread.holdsLock(mListeners));
+ Preconditions.checkState(Thread.holdsLock(mLock));
if (mListeners.isEmpty()) {
return null;
diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java
index e67ba5905aae..1a9517cafdde 100644
--- a/media/java/android/media/AudioMetadata.java
+++ b/media/java/android/media/AudioMetadata.java
@@ -41,43 +41,47 @@ public final class AudioMetadata {
private static final String TAG = "AudioMetadata";
/**
- * Key interface for the map.
+ * Key interface for the {@code AudioMetadata} map.
*
- * The presence of this {@code Key} interface on an object allows
- * it to be used to reference metadata in the Audio Framework.
+ * <p>The presence of this {@code Key} interface on an object allows
+ * it to reference metadata in the Audio Framework.</p>
+ *
+ * <p>Vendors are allowed to implement this {@code Key} interface for their debugging or
+ * private application use. To avoid name conflicts, vendor key names should be qualified by
+ * the vendor company name followed by a dot; for example, "vendorCompany.someVolume".</p>
*
* @param <T> type of value associated with {@code Key}.
*/
- // Conceivably metadata keys exposing multiple interfaces
- // could be eligible to work in multiple framework domains.
+ /*
+ * Internal details:
+ * Conceivably metadata keys exposing multiple interfaces
+ * could be eligible to work in multiple framework domains.
+ */
public interface Key<T> {
/**
- * Returns the internal name of the key.
+ * Returns the internal name of the key. The name should be unique in the
+ * {@code AudioMetadata} namespace. Vendors should prefix their keys with
+ * the company name followed by a dot.
*/
@NonNull
String getName();
/**
- * Returns the class type of the associated value.
+ * Returns the class type {@code T} of the associated value. Valid class types for
+ * {@link android.os.Build.VERSION_CODES#R} are
+ * {@code Integer.class}, {@code Long.class}, {@code Float.class}, {@code Double.class},
+ * {@code String.class}.
*/
@NonNull
Class<T> getValueClass();
// TODO: consider adding bool isValid(@NonNull T value)
-
- /**
- * Do not allow non-framework apps to create their own keys
- * by implementing this interface; keep a method hidden.
- *
- * @hide
- */
- boolean isFromFramework();
}
/**
* A read only {@code Map} interface of {@link Key} value pairs.
*
- * Using a {@link Key} interface, look up the corresponding value.
+ * <p>Using a {@link Key} interface, the map looks up the corresponding value.</p>
*/
public interface ReadMap {
/**
@@ -301,12 +305,6 @@ public final class AudioMetadata {
return mType;
}
- // hidden interface method to prevent user class implements the of Key interface.
- @Override
- public boolean isFromFramework() {
- return true;
- }
-
/**
* Return true if the name and the type of two objects are the same.
*/
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 525ee4d05ade..9ce895e25b49 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -19,7 +19,6 @@ package android.media.tv.tuner;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
@@ -173,10 +172,8 @@ public class Lnb implements AutoCloseable {
* @param voltage the power voltage constant the Lnb to use.
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int setVoltage(@Voltage int voltage) {
- TunerUtils.checkTunerPermission(mContext);
return nativeSetVoltage(voltage);
}
@@ -186,10 +183,8 @@ public class Lnb implements AutoCloseable {
* @param tone the tone mode the Lnb to use.
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int setTone(@Tone int tone) {
- TunerUtils.checkTunerPermission(mContext);
return nativeSetTone(tone);
}
@@ -199,10 +194,8 @@ public class Lnb implements AutoCloseable {
* @param position the position the Lnb to use.
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int setSatellitePosition(@Position int position) {
- TunerUtils.checkTunerPermission(mContext);
return nativeSetSatellitePosition(position);
}
@@ -216,19 +209,15 @@ public class Lnb implements AutoCloseable {
*
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int sendDiseqcMessage(@NonNull byte[] message) {
- TunerUtils.checkTunerPermission(mContext);
return nativeSendDiseqcMessage(message);
}
/**
* Releases the LNB instance.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void close() {
- TunerUtils.checkTunerPermission(mContext);
nativeClose();
}
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index d24d75260395..3eb77d5da8b0 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -43,6 +43,8 @@ import android.media.tv.tuner.frontend.FrontendStatus.FrontendStatusType;
import android.media.tv.tuner.frontend.OnTuneEventListener;
import android.media.tv.tuner.frontend.ScanCallback;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
+import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
@@ -218,6 +220,8 @@ public class Tuner implements AutoCloseable {
@Nullable
private Executor mOnResourceLostListenerExecutor;
+ private Integer mDemuxHandle;
+ private Integer mDescramblerHandle;
private final TunerResourceManager.ResourcesReclaimListener mResourceListener =
new TunerResourceManager.ResourcesReclaimListener() {
@@ -255,7 +259,6 @@ public class Tuner implements AutoCloseable {
* @param executor the executor on which the listener should be invoked.
* @param listener the listener that will be run.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void setResourceLostListener(@NonNull @CallbackExecutor Executor executor,
@NonNull OnResourceLostListener listener) {
Objects.requireNonNull(executor, "OnResourceLostListener must not be null");
@@ -267,7 +270,6 @@ public class Tuner implements AutoCloseable {
/**
* Removes the listener for resource lost.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void clearResourceLostListener() {
mOnResourceLostListener = null;
mOnResourceLostListenerExecutor = null;
@@ -278,9 +280,10 @@ public class Tuner implements AutoCloseable {
*
* @param tuner the Tuner instance to share frontend resource with.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void shareFrontendFromTuner(@NonNull Tuner tuner) {
- // TODO: implementation.
+ mTunerResourceManager.shareFrontend(mClientId, tuner.mClientId);
+ mFrontendHandle = tuner.mFrontendHandle;
+ nativeOpenFrontendByHandle(mFrontendHandle);
}
/**
@@ -294,9 +297,8 @@ public class Tuner implements AutoCloseable {
* @param priority the new priority.
* @param niceValue the nice value.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void updateResourcePriority(int priority, int niceValue) {
- // TODO: implementation.
+ mTunerResourceManager.updateClientPriority(mClientId, priority, niceValue);
}
private long mNativeContext; // used by native jMediaTuner
@@ -304,10 +306,16 @@ public class Tuner implements AutoCloseable {
/**
* Releases the Tuner instance.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Override
public void close() {
- // TODO: implementation.
+ if (mFrontendHandle != null) {
+ mTunerResourceManager.releaseFrontend(mFrontendHandle);
+ mFrontendHandle = null;
+ }
+ if (mLnb != null) {
+ mTunerResourceManager.releaseLnb(mLnbHandle);
+ mLnb = null;
+ }
}
/**
@@ -429,10 +437,8 @@ public class Tuner implements AutoCloseable {
* @throws SecurityException if the caller does not have appropriate permissions.
* @see #tune(FrontendSettings)
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void setOnTuneEventListener(@NonNull @CallbackExecutor Executor executor,
@NonNull OnTuneEventListener eventListener) {
- TunerUtils.checkTunerPermission(mContext);
mOnTuneEventListener = eventListener;
mOnTunerEventExecutor = executor;
}
@@ -443,7 +449,6 @@ public class Tuner implements AutoCloseable {
* @throws SecurityException if the caller does not have appropriate permissions.
* @see #setOnTuneEventListener(Executor, OnTuneEventListener)
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public void clearOnTuneEventListener() {
TunerUtils.checkTunerPermission(mContext);
mOnTuneEventListener = null;
@@ -474,7 +479,6 @@ public class Tuner implements AutoCloseable {
* @throws SecurityException if the caller does not have appropriate permissions.
* @see #setOnTuneEventListener(Executor, OnTuneEventListener)
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int tune(@NonNull FrontendSettings settings) {
mFrontendType = settings.getType();
@@ -491,7 +495,6 @@ public class Tuner implements AutoCloseable {
*
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int cancelTuning() {
TunerUtils.checkTunerPermission(mContext);
@@ -509,7 +512,6 @@ public class Tuner implements AutoCloseable {
* @throws IllegalStateException if {@code scan} is called again before
* {@link #cancelScanning()} is called.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int scan(@NonNull FrontendSettings settings, @ScanType int scanType,
@NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
@@ -537,10 +539,8 @@ public class Tuner implements AutoCloseable {
*
* @throws SecurityException if the caller does not have appropriate permissions.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int cancelScanning() {
- TunerUtils.checkTunerPermission(mContext);
int retVal = nativeStopScan();
mScanCallback = null;
mScanCallbackExecutor = null;
@@ -548,11 +548,11 @@ public class Tuner implements AutoCloseable {
}
private boolean requestFrontend() {
- int[] feId = new int[1];
+ int[] feHandle = new int[1];
TunerFrontendRequest request = new TunerFrontendRequest(mClientId, mFrontendType);
- boolean granted = mTunerResourceManager.requestFrontend(request, feId);
+ boolean granted = mTunerResourceManager.requestFrontend(request, feHandle);
if (granted) {
- mFrontendHandle = feId[0];
+ mFrontendHandle = feHandle[0];
}
return granted;
}
@@ -579,10 +579,8 @@ public class Tuner implements AutoCloseable {
*
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int setLnaEnabled(boolean enable) {
- TunerUtils.checkTunerPermission(mContext);
return nativeSetLna(enable);
}
@@ -605,9 +603,8 @@ public class Tuner implements AutoCloseable {
* @param filter the filter instance for the hardware sync ID.
* @return the id of hardware A/V sync.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public int getAvSyncHwId(@NonNull Filter filter) {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
Integer id = nativeGetAvSyncHwId(filter);
return id == null ? INVALID_AV_SYNC_ID : id;
}
@@ -621,9 +618,8 @@ public class Tuner implements AutoCloseable {
* @param avSyncHwId the hardware id of A/V sync.
* @return the current timestamp of hardware A/V sync.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
public long getAvSyncTime(int avSyncHwId) {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
Long time = nativeGetAvSyncTime(avSyncHwId);
return time == null ? INVALID_TIMESTAMP : time;
}
@@ -637,10 +633,9 @@ public class Tuner implements AutoCloseable {
* @param ciCamId specify CI-CAM Id to connect.
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int connectCiCam(int ciCamId) {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
return nativeConnectCiCam(ciCamId);
}
@@ -651,10 +646,9 @@ public class Tuner implements AutoCloseable {
*
* @return result status of the operation.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
public int disconnectCiCam() {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
return nativeDisconnectCiCam();
}
@@ -663,10 +657,9 @@ public class Tuner implements AutoCloseable {
*
* @return The frontend information. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public FrontendInfo getFrontendInfo() {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
if (mFrontend == null) {
throw new IllegalStateException("frontend is not initialized");
}
@@ -679,14 +672,11 @@ public class Tuner implements AutoCloseable {
/**
* Gets Demux capabilities.
*
- * @param context the context of the caller.
* @return A {@link DemuxCapabilities} instance that represents the demux capabilities.
* {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
- public static DemuxCapabilities getDemuxCapabilities(@NonNull Context context) {
- TunerUtils.checkTunerPermission(context);
+ public DemuxCapabilities getDemuxCapabilities() {
return nativeGetDemuxCapabilities();
}
@@ -792,12 +782,11 @@ public class Tuner implements AutoCloseable {
* @param cb the callback to receive notifications from filter.
* @return the opened filter. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public Filter openFilter(@Type int mainType, @Subtype int subType,
@BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor,
@Nullable FilterCallback cb) {
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
Filter filter = nativeOpenFilter(
mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize);
if (filter != null) {
@@ -821,13 +810,11 @@ public class Tuner implements AutoCloseable {
* @param cb the callback to receive notifications from LNB.
* @return the opened LNB object. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public Lnb openLnb(@CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(cb, "LnbCallback must not be null");
checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB);
- // TODO: update JNI code for LNB handle,
return nativeOpenLnbByHandle(mLnbHandle);
}
@@ -840,23 +827,22 @@ public class Tuner implements AutoCloseable {
* @param cb the callback to receive notifications from LNB.
* @return the opened LNB object. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public Lnb openLnbByName(@NonNull String name, @CallbackExecutor @NonNull Executor executor,
@NonNull LnbCallback cb) {
Objects.requireNonNull(name, "LNB name must not be null");
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(cb, "LnbCallback must not be null");
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB);
return nativeOpenLnbByName(name);
}
private boolean requestLnb() {
- int[] lnbId = new int[1];
+ int[] lnbHandle = new int[1];
TunerLnbRequest request = new TunerLnbRequest(mClientId);
- boolean granted = mTunerResourceManager.requestLnb(request, lnbId);
+ boolean granted = mTunerResourceManager.requestLnb(request, lnbHandle);
if (granted) {
- mLnbHandle = lnbId[0];
+ mLnbHandle = lnbHandle[0];
}
return granted;
}
@@ -868,6 +854,7 @@ public class Tuner implements AutoCloseable {
*/
@Nullable
public TimeFilter openTimeFilter() {
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
return nativeOpenTimeFilter();
}
@@ -885,7 +872,7 @@ public class Tuner implements AutoCloseable {
@RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER)
@Nullable
public Descrambler openDescrambler() {
- TunerUtils.checkDescramblerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DESCRAMBLER);
return nativeOpenDescrambler();
}
@@ -899,7 +886,6 @@ public class Tuner implements AutoCloseable {
* @param l the listener to receive notifications from DVR recorder.
* @return the opened DVR recorder object. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public DvrRecorder openDvrRecorder(
@BytesLong long bufferSize,
@@ -907,7 +893,7 @@ public class Tuner implements AutoCloseable {
@NonNull OnRecordStatusChangedListener l) {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(l, "OnRecordStatusChangedListener must not be null");
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
DvrRecorder dvr = nativeOpenDvrRecorder(bufferSize);
return dvr;
}
@@ -922,7 +908,6 @@ public class Tuner implements AutoCloseable {
* @param l the listener to receive notifications from DVR recorder.
* @return the opened DVR playback object. {@code null} if the operation failed.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
public DvrPlayback openDvrPlayback(
@BytesLong long bufferSize,
@@ -930,13 +915,32 @@ public class Tuner implements AutoCloseable {
@NonNull OnPlaybackStatusChangedListener l) {
Objects.requireNonNull(executor, "executor must not be null");
Objects.requireNonNull(l, "OnPlaybackStatusChangedListener must not be null");
- TunerUtils.checkTunerPermission(mContext);
+ checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX);
DvrPlayback dvr = nativeOpenDvrPlayback(bufferSize);
return dvr;
}
+ private boolean requestDemux() {
+ int[] demuxHandle = new int[1];
+ TunerDemuxRequest request = new TunerDemuxRequest(mClientId);
+ boolean granted = mTunerResourceManager.requestDemux(request, demuxHandle);
+ if (granted) {
+ mDemuxHandle = demuxHandle[0];
+ }
+ return granted;
+ }
+
+ private boolean requestDescrambler() {
+ int[] descramblerHandle = new int[1];
+ TunerDescramblerRequest request = new TunerDescramblerRequest(mClientId);
+ boolean granted = mTunerResourceManager.requestDescrambler(request, descramblerHandle);
+ if (granted) {
+ mDescramblerHandle = descramblerHandle[0];
+ }
+ return granted;
+ }
+
private boolean checkResource(int resourceType) {
- // TODO: demux and descrambler
switch (resourceType) {
case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
if (mFrontendHandle == null && !requestFrontend()) {
@@ -950,6 +954,18 @@ public class Tuner implements AutoCloseable {
}
break;
}
+ case TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX: {
+ if (mDemuxHandle == null && !requestDemux()) {
+ return false;
+ }
+ break;
+ }
+ case TunerResourceManager.TUNER_RESOURCE_TYPE_DESCRAMBLER: {
+ if (mDescramblerHandle == null && !requestDescrambler()) {
+ return false;
+ }
+ break;
+ }
}
return true;
}
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index c3be12a0bf5e..a41b39725672 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -60,6 +60,7 @@ public final class TunerUtils {
* @throws SecurityException if the caller doesn't have the permission.
*/
public static void checkPermission(Context context, String permission) {
+ // TODO: remove checkPermission methods
if (context.checkCallingOrSelfPermission(permission)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller must have " + permission + " permission.");
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index f07cd5e34062..6e47741b488b 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -32,6 +32,7 @@ import com.android.internal.telephony.IMms;
/**
* Manages MMS operations such as sending multimedia messages.
* Get this object by calling Context#getSystemService(Context#MMS_SERVICE).
+ * @hide
*/
@SystemService(Context.MMS_SERVICE)
public class MmsManager {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 8f859b25faef..810511488257 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -278,6 +278,9 @@
<!-- Permission needed to modify settings overrideable by restore in CTS tests -->
<uses-permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE" />
+ <!-- Permission required for testing system audio effect APIs. -->
+ <uses-permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"/>
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/layout/controls_no_favorites.xml b/packages/SystemUI/res/layout/controls_no_favorites.xml
index 8074efd18492..74fc167c4632 100644
--- a/packages/SystemUI/res/layout/controls_no_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_no_favorites.xml
@@ -40,14 +40,20 @@
android:paddingBottom="8dp" />
<TextView
+ style="@style/TextAppearance.ControlSetup.Title"
android:id="@+id/controls_title"
android:text="@string/quick_controls_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:singleLine="true"
- android:layout_gravity="center"
- android:textSize="25sp"
- android:textColor="@*android:color/foreground_material_dark"
- android:fontFamily="@*android:string/config_headlineFontFamily" />
+ android:layout_gravity="center" />
+
+ <TextView
+ style="@style/TextAppearance.ControlSetup.Subtitle"
+ android:id="@+id/controls_subtitle"
+ android:visibility="gone"
+ android:layout_marginTop="12dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
</LinearLayout>
</merge>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
index 44c409e08e82..b5822c889f1c 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
@@ -35,7 +35,34 @@
android:forceHasOverlappingRendering="false"
android:clipChildren="false"
>
- <include layout="@layout/status_bar_notification_section_header_contents"/>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:layout_weight="1">
+
+ <TextView
+ style="@style/TextAppearance.NotificationSectionHeaderButton"
+ android:id="@+id/header_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:forceHasOverlappingRendering="false"
+ android:text="@string/notification_section_header_gentle"
+ />
+
+ </FrameLayout>
+ <ImageView
+ android:id="@+id/btn_clear_all"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:src="@drawable/status_bar_notification_section_header_clear_btn"
+ android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
+ android:scaleType="center"
+ android:tint="?attr/wallpaperTextColor"
+ android:tintMode="src_in"
+ android:visibility="gone"
+ android:forceHasOverlappingRendering="false"
+ />
</LinearLayout>
</com.android.systemui.statusbar.notification.stack.SectionHeaderView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
deleted file mode 100644
index 3b9c44d1b5df..000000000000
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<!--
- ~ Copyright (C) 2019 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
- -->
-
-<!-- Used by both status_bar_notification_header and SectionHeaderView -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android" >
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:layout_weight="1">
-
- <TextView
- style="@style/TextAppearance.NotificationSectionHeaderButton"
- android:id="@+id/header_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:forceHasOverlappingRendering="false"
- android:text="@string/notification_section_header_gentle"
- />
-
- </FrameLayout>
- <ImageView
- android:id="@+id/btn_clear_all"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:src="@drawable/status_bar_notification_section_header_clear_btn"
- android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
- android:scaleType="center"
- android:tint="?attr/wallpaperTextColor"
- android:tintMode="src_in"
- android:visibility="gone"
- android:forceHasOverlappingRendering="false"
- />
-</merge>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 06e027d7cae7..4d6b759c86e4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -532,4 +532,8 @@
<!-- Respect drawable/rounded.xml intrinsic size for multiple radius corner path customization -->
<bool name="config_roundedCornerMultipleRadius">false</bool>
+ <!-- Controls can query a preferred application for limited number of suggested controls.
+ This config value should contain the package name of that preferred application.
+ -->
+ <string translatable="false" name="config_controlsPreferredPackage"></string>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 93bafdbee91d..cb08840d02c8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2657,4 +2657,8 @@
<!-- Tooltip to show in management screen when there are multiple structures [CHAR_LIMIT=50] -->
<string name="controls_structure_tooltip">Swipe to see other structures</string>
+
+ <!-- Message to tell the user to wait while systemui attempts to load a set of
+ recommended controls [CHAR_LIMIT=30] -->
+ <string name="controls_seeding_in_progress">Loading recommendations</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 20b88a1a550d..1283fe0a9181 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -699,6 +699,20 @@
<item name="*android:colorPopupBackground">@color/control_list_popup_background</item>
</style>
+ <style name="TextAppearance.ControlSetup">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@color/control_primary_text</item>
+ <item name="android:singleLine">true</item>
+ </style>
+
+ <style name="TextAppearance.ControlSetup.Title">
+ <item name="android:textSize">25sp</item>
+ </style>
+
+ <style name="TextAppearance.ControlSetup.Subtitle">
+ <item name="android:textSize">16sp</item>
+ </style>
+
<style name="Theme.ControlsRequestDialog" parent="@style/Theme.SystemUI.MediaProjectionAlertDialog"/>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index c5af436e7e92..d4d4d2a7d8fe 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -43,6 +43,14 @@ interface ControlsBindingController : UserAwareController {
fun bindAndLoad(component: ComponentName, callback: LoadCallback): Runnable
/**
+ * Request bind to a service and load a limited number of suggested controls.
+ *
+ * @param component The [ComponentName] of the service to bind
+ * @param callback a callback to return the loaded controls to (or an error).
+ */
+ fun bindAndLoadSuggested(component: ComponentName, callback: LoadCallback)
+
+ /**
* Request to bind to the given service.
*
* @param component The [ComponentName] of the service to bind
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index f8d4a39a98fe..5d03fc51004d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -44,6 +44,8 @@ open class ControlsBindingControllerImpl @Inject constructor(
companion object {
private const val TAG = "ControlsBindingControllerImpl"
+ private const val MAX_CONTROLS_REQUEST = 100000L
+ private const val SUGGESTED_CONTROLS_REQUEST = 4L
}
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
@@ -97,24 +99,37 @@ open class ControlsBindingControllerImpl @Inject constructor(
component: ComponentName,
callback: ControlsBindingController.LoadCallback
): Runnable {
- val subscriber = LoadSubscriber(callback)
+ val subscriber = LoadSubscriber(callback, MAX_CONTROLS_REQUEST)
retrieveLifecycleManager(component).maybeBindAndLoad(subscriber)
return subscriber.loadCancel()
}
+ override fun bindAndLoadSuggested(
+ component: ComponentName,
+ callback: ControlsBindingController.LoadCallback
+ ) {
+ val subscriber = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST)
+ retrieveLifecycleManager(component).maybeBindAndLoadSuggested(subscriber)
+ }
+
override fun subscribe(structureInfo: StructureInfo) {
// make sure this has happened. only allow one active subscription
unsubscribe()
- statefulControlSubscriber = null
val provider = retrieveLifecycleManager(structureInfo.componentName)
- val scs = StatefulControlSubscriber(lazyController.get(), provider, backgroundExecutor)
+ val scs = StatefulControlSubscriber(
+ lazyController.get(),
+ provider,
+ backgroundExecutor,
+ MAX_CONTROLS_REQUEST
+ )
statefulControlSubscriber = scs
provider.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId }, scs)
}
override fun unsubscribe() {
statefulControlSubscriber?.cancel()
+ statefulControlSubscriber = null
}
override fun action(
@@ -201,10 +216,11 @@ open class ControlsBindingControllerImpl @Inject constructor(
private inner class OnSubscribeRunnable(
token: IBinder,
- val subscription: IControlsSubscription
+ val subscription: IControlsSubscription,
+ val requestLimit: Long
) : CallbackRunnable(token) {
override fun doRun() {
- provider?.startSubscription(subscription)
+ provider?.startSubscription(subscription, requestLimit)
}
}
@@ -234,7 +250,8 @@ open class ControlsBindingControllerImpl @Inject constructor(
}
private inner class LoadSubscriber(
- val callback: ControlsBindingController.LoadCallback
+ val callback: ControlsBindingController.LoadCallback,
+ val requestLimit: Long
) : IControlsSubscriber.Stub() {
val loadedControls = ArrayList<Control>()
var hasError = false
@@ -246,7 +263,7 @@ open class ControlsBindingControllerImpl @Inject constructor(
override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
_loadCancelInternal = subs::cancel
- backgroundExecutor.execute(OnSubscribeRunnable(token, subs))
+ backgroundExecutor.execute(OnSubscribeRunnable(token, subs, requestLimit))
}
override fun onNext(token: IBinder, c: Control) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 9e0d26c3935f..ae75dd4d94ab 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -114,6 +114,25 @@ interface ControlsController : UserAwareController {
// FAVORITE MANAGEMENT
/**
+ * Send a request to seed favorites into the persisted XML file
+ *
+ * @param componentName the component to seed controls from
+ * @param callback true if the favorites were persisted
+ */
+ fun seedFavoritesForComponent(
+ componentName: ComponentName,
+ callback: Consumer<Boolean>
+ )
+
+ /**
+ * Callback to be informed when the seeding process has finished
+ *
+ * @param callback consumer accepts true if successful
+ * @return true if seeding is in progress and the callback was added
+ */
+ fun addSeedingFavoritesCallback(callback: Consumer<Boolean>): Boolean
+
+ /**
* Get all the favorites.
*
* @return a list of the structures that have at least one favorited control
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 9cb902f51f22..2e34ea55ebe4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -31,6 +31,7 @@ import android.os.UserHandle
import android.provider.Settings
import android.service.controls.Control
import android.service.controls.actions.ControlAction
+import android.util.ArrayMap
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
@@ -74,6 +75,9 @@ class ControlsControllerImpl @Inject constructor (
private var loadCanceller: Runnable? = null
+ private var seedingInProgress = false
+ private val seedingCallbacks = mutableListOf<Consumer<Boolean>>()
+
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
override val currentUserId
get() = currentUser.identifier
@@ -280,6 +284,84 @@ class ControlsControllerImpl @Inject constructor (
)
}
+ override fun addSeedingFavoritesCallback(callback: Consumer<Boolean>): Boolean {
+ if (!seedingInProgress) return false
+ executor.execute {
+ // status may have changed by this point, so check again and inform the
+ // caller if necessary
+ if (seedingInProgress) seedingCallbacks.add(callback)
+ else callback.accept(false)
+ }
+ return true
+ }
+
+ override fun seedFavoritesForComponent(
+ componentName: ComponentName,
+ callback: Consumer<Boolean>
+ ) {
+ Log.i(TAG, "Beginning request to seed favorites for: $componentName")
+ if (!confirmAvailability()) {
+ if (userChanging) {
+ // Try again later, userChanging should not last forever. If so, we have bigger
+ // problems. This will return a runnable that allows to cancel the delayed version,
+ // it will not be able to cancel the load if
+ executor.executeDelayed(
+ { seedFavoritesForComponent(componentName, callback) },
+ USER_CHANGE_RETRY_DELAY,
+ TimeUnit.MILLISECONDS
+ )
+ } else {
+ callback.accept(false)
+ }
+ return
+ }
+ seedingInProgress = true
+ bindingController.bindAndLoadSuggested(
+ componentName,
+ object : ControlsBindingController.LoadCallback {
+ override fun accept(controls: List<Control>) {
+ executor.execute {
+ val structureToControls =
+ ArrayMap<CharSequence, MutableList<ControlInfo>>()
+
+ controls.forEach {
+ val structure = it.structure ?: ""
+ val list = structureToControls.get(structure)
+ ?: mutableListOf<ControlInfo>()
+ list.add(ControlInfo(it.controlId, it.title, it.deviceType))
+ structureToControls.put(structure, list)
+ }
+
+ structureToControls.forEach {
+ (s, cs) -> Favorites.replaceControls(
+ StructureInfo(componentName, s, cs))
+ }
+
+ persistenceWrapper.storeFavorites(Favorites.getAllStructures())
+ callback.accept(true)
+ endSeedingCall(true)
+ }
+ }
+
+ override fun error(message: String) {
+ Log.e(TAG, "Unable to seed favorites: $message")
+ executor.execute {
+ callback.accept(false)
+ endSeedingCall(false)
+ }
+ }
+ }
+ )
+ }
+
+ private fun endSeedingCall(state: Boolean) {
+ seedingInProgress = false
+ seedingCallbacks.forEach {
+ it.accept(state)
+ }
+ seedingCallbacks.clear()
+ }
+
override fun cancelLoad() {
loadCanceller?.let {
executor.execute(it)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 4918bd7b3349..209d056260a9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -66,22 +66,17 @@ class ControlsProviderLifecycleManager(
@GuardedBy("subscriptions")
private val subscriptions = mutableListOf<IControlsSubscription>()
private var requiresBound = false
- @GuardedBy("queuedMessages")
- private val queuedMessages: MutableSet<Message> = ArraySet()
+ @GuardedBy("queuedServiceMethods")
+ private val queuedServiceMethods: MutableSet<ServiceMethod> = ArraySet()
private var wrapper: ServiceWrapper? = null
private var bindTryCount = 0
private val TAG = javaClass.simpleName
private var onLoadCanceller: Runnable? = null
companion object {
- private const val MSG_LOAD = 0
- private const val MSG_SUBSCRIBE = 1
- private const val MSG_ACTION = 2
- private const val MSG_UNBIND = 3
private const val BIND_RETRY_DELAY = 1000L // ms
private const val LOAD_TIMEOUT_SECONDS = 30L // seconds
private const val MAX_BIND_RETRIES = 5
- private const val MAX_CONTROLS_REQUEST = 100000L
private const val DEBUG = true
private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or
Context.BIND_WAIVE_PRIORITY
@@ -130,7 +125,7 @@ class ControlsProviderLifecycleManager(
try {
service.linkToDeath(this@ControlsProviderLifecycleManager, 0)
} catch (_: RemoteException) {}
- handlePendingMessages()
+ handlePendingServiceMethods()
}
override fun onServiceDisconnected(name: ComponentName?) {
@@ -140,29 +135,14 @@ class ControlsProviderLifecycleManager(
}
}
- private fun handlePendingMessages() {
- val queue = synchronized(queuedMessages) {
- ArraySet(queuedMessages).also {
- queuedMessages.clear()
+ private fun handlePendingServiceMethods() {
+ val queue = synchronized(queuedServiceMethods) {
+ ArraySet(queuedServiceMethods).also {
+ queuedServiceMethods.clear()
}
}
- if (Message.Unbind in queue) {
- bindService(false)
- return
- }
-
- queue.filter { it is Message.Load }.forEach {
- val msg = it as Message.Load
- load(msg.subscriber)
- }
-
- queue.filter { it is Message.Subscribe }.forEach {
- val msg = it as Message.Subscribe
- subscribe(msg.list, msg.subscriber)
- }
- queue.filter { it is Message.Action }.forEach {
- val msg = it as Message.Action
- action(msg.id, msg.action)
+ queue.forEach {
+ it.run()
}
}
@@ -177,33 +157,17 @@ class ControlsProviderLifecycleManager(
}
}
- private fun queueMessage(message: Message) {
- synchronized(queuedMessages) {
- queuedMessages.add(message)
+ private fun queueServiceMethod(sm: ServiceMethod) {
+ synchronized(queuedServiceMethods) {
+ queuedServiceMethods.add(sm)
}
}
- private fun unqueueMessageType(type: Int) {
- synchronized(queuedMessages) {
- queuedMessages.removeIf { it.type == type }
- }
- }
-
- private fun load(subscriber: IControlsSubscriber.Stub) {
- if (DEBUG) {
- Log.d(TAG, "load $componentName")
- }
- if (!(wrapper?.load(subscriber) ?: false)) {
- queueMessage(Message.Load(subscriber))
- binderDied()
- }
- }
-
- private inline fun invokeOrQueue(f: () -> Unit, msg: Message) {
+ private fun invokeOrQueue(sm: ServiceMethod) {
wrapper?.run {
- f()
+ sm.run()
} ?: run {
- queueMessage(msg)
+ queueServiceMethod(sm)
bindService(true)
}
}
@@ -217,7 +181,6 @@ class ControlsProviderLifecycleManager(
* @param subscriber the subscriber that manages coordination for loading controls
*/
fun maybeBindAndLoad(subscriber: IControlsSubscriber.Stub) {
- unqueueMessageType(MSG_UNBIND)
onLoadCanceller = executor.executeDelayed({
// Didn't receive a response in time, log and send back error
Log.d(TAG, "Timeout waiting onLoad for $componentName")
@@ -225,7 +188,26 @@ class ControlsProviderLifecycleManager(
unbindService()
}, LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
- invokeOrQueue({ load(subscriber) }, Message.Load(subscriber))
+ invokeOrQueue(Load(subscriber))
+ }
+
+ /**
+ * Request a call to [IControlsProvider.loadSuggested].
+ *
+ * If the service is not bound, the call will be queued and the service will be bound first.
+ * The service will be unbound after the controls are returned or the call times out.
+ *
+ * @param subscriber the subscriber that manages coordination for loading controls
+ */
+ fun maybeBindAndLoadSuggested(subscriber: IControlsSubscriber.Stub) {
+ onLoadCanceller = executor.executeDelayed({
+ // Didn't receive a response in time, log and send back error
+ Log.d(TAG, "Timeout waiting onLoadSuggested for $componentName")
+ subscriber.onError(token, "Timeout waiting onLoadSuggested")
+ unbindService()
+ }, LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ invokeOrQueue(Suggest(subscriber))
}
fun cancelLoadTimeout() {
@@ -240,23 +222,8 @@ class ControlsProviderLifecycleManager(
*
* @param controlIds a list of the ids of controls to send status back.
*/
- fun maybeBindAndSubscribe(controlIds: List<String>, subscriber: IControlsSubscriber) {
- invokeOrQueue(
- { subscribe(controlIds, subscriber) },
- Message.Subscribe(controlIds, subscriber)
- )
- }
-
- private fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber) {
- if (DEBUG) {
- Log.d(TAG, "subscribe $componentName - $controlIds")
- }
-
- if (!(wrapper?.subscribe(controlIds, subscriber) ?: false)) {
- queueMessage(Message.Subscribe(controlIds, subscriber))
- binderDied()
- }
- }
+ fun maybeBindAndSubscribe(controlIds: List<String>, subscriber: IControlsSubscriber) =
+ invokeOrQueue(Subscribe(controlIds, subscriber))
/**
* Request a call to [ControlsProviderService.performControlAction].
@@ -266,19 +233,8 @@ class ControlsProviderLifecycleManager(
* @param controlId the id of the [Control] the action is performed on
* @param action the action performed
*/
- fun maybeBindAndSendAction(controlId: String, action: ControlAction) {
- invokeOrQueue({ action(controlId, action) }, Message.Action(controlId, action))
- }
-
- private fun action(controlId: String, action: ControlAction) {
- if (DEBUG) {
- Log.d(TAG, "onAction $componentName - $controlId")
- }
- if (!(wrapper?.action(controlId, action, actionCallbackService) ?: false)) {
- queueMessage(Message.Action(controlId, action))
- binderDied()
- }
- }
+ fun maybeBindAndSendAction(controlId: String, action: ControlAction) =
+ invokeOrQueue(Action(controlId, action))
/**
* Starts the subscription to the [ControlsProviderService] and requests status of controls.
@@ -286,14 +242,14 @@ class ControlsProviderLifecycleManager(
* @param subscription the subscription to use to request controls
* @see maybeBindAndLoad
*/
- fun startSubscription(subscription: IControlsSubscription) {
+ fun startSubscription(subscription: IControlsSubscription, requestLimit: Long) {
if (DEBUG) {
Log.d(TAG, "startSubscription: $subscription")
}
synchronized(subscriptions) {
subscriptions.add(subscription)
}
- wrapper?.request(subscription, MAX_CONTROLS_REQUEST)
+ wrapper?.request(subscription, requestLimit)
}
/**
@@ -316,7 +272,6 @@ class ControlsProviderLifecycleManager(
* Request bind to the service.
*/
fun bindService() {
- unqueueMessageType(MSG_UNBIND)
bindService(true)
}
@@ -350,21 +305,55 @@ class ControlsProviderLifecycleManager(
}
/**
- * Messages for the internal queue.
+ * Service methods that can be queued or invoked, and are retryable for failure scenarios
*/
- sealed class Message {
- abstract val type: Int
- class Load(val subscriber: IControlsSubscriber.Stub) : Message() {
- override val type = MSG_LOAD
+ abstract inner class ServiceMethod {
+ fun run() {
+ if (!callWrapper()) {
+ queueServiceMethod(this)
+ binderDied()
+ }
+ }
+
+ internal abstract fun callWrapper(): Boolean
+ }
+
+ inner class Load(val subscriber: IControlsSubscriber.Stub) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "load $componentName")
+ }
+ return wrapper?.load(subscriber) ?: false
}
- object Unbind : Message() {
- override val type = MSG_UNBIND
+ }
+
+ inner class Suggest(val subscriber: IControlsSubscriber.Stub) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "suggest $componentName")
+ }
+ return wrapper?.loadSuggested(subscriber) ?: false
}
- class Subscribe(val list: List<String>, val subscriber: IControlsSubscriber) : Message() {
- override val type = MSG_SUBSCRIBE
+ }
+ inner class Subscribe(
+ val list: List<String>,
+ val subscriber: IControlsSubscriber
+ ) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "subscribe $componentName - $list")
+ }
+
+ return wrapper?.subscribe(list, subscriber) ?: false
}
- class Action(val id: String, val action: ControlAction) : Message() {
- override val type = MSG_ACTION
+ }
+
+ inner class Action(val id: String, val action: ControlAction) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "onAction $componentName - $id")
+ }
+ return wrapper?.action(id, action, actionCallbackService) ?: false
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
index b2afd3c144d1..2c717f5af8da 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
@@ -50,6 +50,12 @@ class ServiceWrapper(val service: IControlsProvider) {
}
}
+ fun loadSuggested(subscriber: IControlsSubscriber): Boolean {
+ return callThroughService {
+ service.loadSuggested(subscriber)
+ }
+ }
+
fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber): Boolean {
return callThroughService {
service.subscribe(controlIds, subscriber)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
index a371aa64df6e..e3eabff49d59 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
@@ -31,7 +31,8 @@ import com.android.systemui.util.concurrency.DelayableExecutor
class StatefulControlSubscriber(
private val controller: ControlsController,
private val provider: ControlsProviderLifecycleManager,
- private val bgExecutor: DelayableExecutor
+ private val bgExecutor: DelayableExecutor,
+ private val requestLimit: Long
) : IControlsSubscriber.Stub() {
private var subscriptionOpen = false
private var subscription: IControlsSubscription? = null
@@ -50,7 +51,7 @@ class StatefulControlSubscriber(
run(token) {
subscriptionOpen = true
subscription = subs
- provider.startSubscription(subs)
+ provider.startSubscription(subs, requestLimit)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
index a7fc2ac80070..74a6c87d754b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
@@ -55,7 +55,7 @@ class ControlsRequestDialog @Inject constructor(
private lateinit var control: Control
private var dialog: Dialog? = null
private val callback = object : ControlsListingController.ControlsListingCallback {
- override fun onServicesUpdated(candidates: List<ControlsServiceInfo>) {}
+ override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {}
}
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 138cd47caae7..ffae4653eba8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -52,6 +52,7 @@ import com.android.systemui.R
import dagger.Lazy
import java.text.Collator
+import java.util.function.Consumer
import javax.inject.Inject
import javax.inject.Singleton
@@ -89,6 +90,7 @@ class ControlsUiControllerImpl @Inject constructor (
private var popup: ListPopupWindow? = null
private var activeDialog: Dialog? = null
private val addControlsItem: SelectionItem
+ private var hidden = true
init {
val addDrawable = context.getDrawable(R.drawable.ic_add).apply {
@@ -134,11 +136,15 @@ class ControlsUiControllerImpl @Inject constructor (
override fun show(parent: ViewGroup) {
Log.d(ControlsUiController.TAG, "show()")
this.parent = parent
+ hidden = false
allStructures = controlsController.get().getFavorites()
selectedStructure = loadPreference(allStructures)
- if (selectedStructure.controls.isEmpty() && allStructures.size <= 1) {
+ val cb = Consumer<Boolean> { _ -> reload(parent) }
+ if (controlsController.get().addSeedingFavoritesCallback(cb)) {
+ listingCallback = createCallback(::showSeedingView)
+ } else if (selectedStructure.controls.isEmpty() && allStructures.size <= 1) {
// only show initial view if there are really no favorites across any structure
listingCallback = createCallback(::showInitialSetupView)
} else {
@@ -154,6 +160,20 @@ class ControlsUiControllerImpl @Inject constructor (
controlsListingController.get().addCallback(listingCallback)
}
+ private fun reload(parent: ViewGroup) {
+ if (hidden) return
+ show(parent)
+ }
+
+ private fun showSeedingView(items: List<SelectionItem>) {
+ parent.removeAllViews()
+
+ val inflater = LayoutInflater.from(context)
+ inflater.inflate(R.layout.controls_no_favorites, parent, true)
+ val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
+ subtitle.setVisibility(View.VISIBLE)
+ }
+
private fun showInitialSetupView(items: List<SelectionItem>) {
parent.removeAllViews()
@@ -320,13 +340,14 @@ class ControlsUiControllerImpl @Inject constructor (
selectedStructure = newSelection
updatePreferences(selectedStructure)
controlsListingController.get().removeCallback(listingCallback)
- show(parent)
+ reload(parent)
}
}
}
override fun hide() {
Log.d(ControlsUiController.TAG, "hide()")
+ hidden = true
popup?.dismiss()
activeDialog?.dismiss()
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index b99d7655c425..73539f9380e6 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -31,11 +31,13 @@ import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -92,6 +94,8 @@ import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.controls.ControlsServiceInfo;
+import com.android.systemui.controls.controller.ControlsController;
import com.android.systemui.controls.management.ControlsListingController;
import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.dagger.qualifiers.Background;
@@ -148,6 +152,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
+ private static final String PREFS_CONTROLS_SEEDING_COMPLETED = "ControlsSeedingCompleted";
+ private static final String PREFS_CONTROLS_FILE = "controls_prefs";
+
private final Context mContext;
private final GlobalActionsManager mWindowManagerFuncs;
private final AudioManager mAudioManager;
@@ -215,7 +222,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
NotificationShadeWindowController notificationShadeWindowController,
ControlsUiController controlsUiController, IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
- ControlsListingController controlsListingController) {
+ ControlsListingController controlsListingController,
+ ControlsController controlsController) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -279,9 +287,46 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
});
- mControlsListingController.addCallback(list -> mAnyControlsProviders = !list.isEmpty());
+ String preferredControlsPackage = mContext.getResources()
+ .getString(com.android.systemui.R.string.config_controlsPreferredPackage);
+ mControlsListingController.addCallback(list -> {
+ mAnyControlsProviders = !list.isEmpty();
+
+ /*
+ * See if any service providers match the preferred component. If they do,
+ * and there are no current favorites, and we haven't successfully loaded favorites to
+ * date, query the preferred component for a limited number of suggested controls.
+ */
+ ComponentName preferredComponent = null;
+ for (ControlsServiceInfo info : list) {
+ if (info.componentName.getPackageName().equals(preferredControlsPackage)) {
+ preferredComponent = info.componentName;
+ break;
+ }
+ }
+
+ if (preferredComponent == null) return;
+
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_CONTROLS_FILE,
+ Context.MODE_PRIVATE);
+ boolean isSeeded = prefs.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false);
+ boolean hasFavorites = controlsController.getFavorites().size() > 0;
+ if (!isSeeded && !hasFavorites) {
+ controlsController.seedFavoritesForComponent(
+ preferredComponent,
+ (accepted) -> {
+ Log.i(TAG, "Controls seeded: " + accepted);
+ prefs.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED,
+ accepted).apply();
+ }
+ );
+ }
+ });
}
+
+
+
/**
* Show the global actions dialog (creating if necessary)
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 42a7c6a07e0f..f6f836335c45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -447,7 +447,6 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
}
-
@VisibleForTesting
ExpandableView getGentleHeaderView() {
return mGentleHeader;
@@ -471,7 +470,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override
public void onLocaleListChanged() {
- mGentleHeader.reinflateContents();
+ reinflateViews(LayoutInflater.from(mParent.getContext()));
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
index 1b4f98f84c5b..bc25c71e4fe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -76,9 +76,7 @@ class PeopleHubView(context: Context, attrs: AttributeSet) :
}
}
- override fun needsClippingToShelf(): Boolean {
- return true
- }
+ override fun needsClippingToShelf(): Boolean = true
override fun applyContentTransformation(contentAlpha: Float, translationY: Float) {
super.applyContentTransformation(contentAlpha, translationY)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index deb5532ca0f2..a3d8eecdfd68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -20,7 +20,6 @@ import android.annotation.Nullable;
import android.annotation.StringRes;
import android.content.Context;
import android.util.AttributeSet;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -30,16 +29,17 @@ import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
-import java.util.Objects;
-
/**
- * Similar in size and appearance to the NotificationShelf, appears at the beginning of some
- * notification sections. Currently only used for gentle notifications.
+ * Header displayed above a notification section in the shade. Currently used for Alerting and
+ * Silent sections.
*/
public class SectionHeaderView extends StackScrollerDecorView {
+
private ViewGroup mContents;
private TextView mLabelView;
private ImageView mClearAllButton;
+ @StringRes @Nullable private Integer mLabelTextId;
+ @Nullable private View.OnClickListener mLabelClickListener = null;
@Nullable private View.OnClickListener mOnClearClickListener = null;
public SectionHeaderView(Context context, AttributeSet attrs) {
@@ -48,18 +48,24 @@ public class SectionHeaderView extends StackScrollerDecorView {
@Override
protected void onFinishInflate() {
- mContents = Objects.requireNonNull(findViewById(R.id.content));
+ mContents = requireViewById(R.id.content);
bindContents();
super.onFinishInflate();
setVisible(true /* nowVisible */, false /* animate */);
}
private void bindContents() {
- mLabelView = Objects.requireNonNull(findViewById(R.id.header_label));
- mClearAllButton = Objects.requireNonNull(findViewById(R.id.btn_clear_all));
+ mLabelView = requireViewById(R.id.header_label);
+ mClearAllButton = requireViewById(R.id.btn_clear_all);
if (mOnClearClickListener != null) {
mClearAllButton.setOnClickListener(mOnClearClickListener);
}
+ if (mLabelClickListener != null) {
+ mLabelView.setOnClickListener(mLabelClickListener);
+ }
+ if (mLabelTextId != null) {
+ mLabelView.setText(mLabelTextId);
+ }
}
@Override
@@ -72,21 +78,6 @@ public class SectionHeaderView extends StackScrollerDecorView {
return null;
}
- /**
- * Destroys and reinflates the visible contents of the section header. For use on configuration
- * changes or any other time that layout values might need to be re-evaluated.
- *
- * Does not reinflate the base content view itself ({@link #findContentView()} or any of the
- * decorator views, such as the background view or shadow view.
- */
- void reinflateContents() {
- mContents.removeAllViews();
- LayoutInflater.from(getContext()).inflate(
- R.layout.status_bar_notification_section_header_contents,
- mContents);
- bindContents();
- }
-
@Override
public boolean isTransparent() {
return true;
@@ -105,6 +96,7 @@ public class SectionHeaderView extends StackScrollerDecorView {
* Fired whenever the user clicks on the body of the header (e.g. no sub-buttons or anything).
*/
void setOnHeaderClickListener(View.OnClickListener listener) {
+ mLabelClickListener = listener;
mLabelView.setOnClickListener(listener);
}
@@ -129,6 +121,7 @@ public class SectionHeaderView extends StackScrollerDecorView {
}
void setHeaderText(@StringRes int resId) {
+ mLabelTextId = resId;
mLabelView.setText(resId);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index c25d4e2d4b30..2ff2b2a4eac7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -207,6 +207,56 @@ class ControlsBindingControllerImplTest : SysuiTestCase() {
}
@Test
+ fun testBindAndLoadSuggested() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoadSuggested(any())
+ }
+
+ @Test
+ fun testLoadSuggested_onCompleteRemovesTimeout() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ val subscription = mock(IControlsSubscription::class.java)
+
+ controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoadSuggested(capture(subscriberCaptor))
+ val b = Binder()
+ subscriberCaptor.value.onSubscribe(b, subscription)
+
+ subscriberCaptor.value.onComplete(b)
+ verify(providers[0]).cancelLoadTimeout()
+ }
+
+ @Test
+ fun testLoadSuggested_onErrorRemovesTimeout() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ val subscription = mock(IControlsSubscription::class.java)
+
+ controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoadSuggested(capture(subscriberCaptor))
+ val b = Binder()
+ subscriberCaptor.value.onSubscribe(b, subscription)
+
+ subscriberCaptor.value.onError(b, "")
+ verify(providers[0]).cancelLoadTimeout()
+ }
+
+ @Test
fun testBindService() {
controller.bindService(TEST_COMPONENT_NAME_1)
executor.runAllReady()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index f9c98157edb8..971d14ceafbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -80,6 +80,10 @@ class ControlsControllerImplTest : SysuiTestCase() {
@Captor
private lateinit var structureInfoCaptor: ArgumentCaptor<StructureInfo>
+
+ @Captor
+ private lateinit var booleanConsumer: ArgumentCaptor<Consumer<Boolean>>
+
@Captor
private lateinit var controlLoadCallbackCaptor:
ArgumentCaptor<ControlsBindingController.LoadCallback>
@@ -155,9 +159,13 @@ class ControlsControllerImplTest : SysuiTestCase() {
verify(listingController).addCallback(capture(listingCallbackCaptor))
}
- private fun builderFromInfo(controlInfo: ControlInfo): Control.StatelessBuilder {
+ private fun builderFromInfo(
+ controlInfo: ControlInfo,
+ structure: CharSequence = ""
+ ): Control.StatelessBuilder {
return Control.StatelessBuilder(controlInfo.controlId, pendingIntent)
.setDeviceType(controlInfo.deviceType).setTitle(controlInfo.controlTitle)
+ .setStructure(structure)
}
@Test
@@ -746,4 +754,70 @@ class ControlsControllerImplTest : SysuiTestCase() {
inOrder.verify(persistenceWrapper).readFavorites()
inOrder.verify(listingController).addCallback(listingCallbackCaptor.value)
}
+
+ @Test
+ fun testSeedFavoritesForComponent() {
+ var succeeded = false
+ val control = builderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure).build()
+
+ controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
+ succeeded = accepted
+ })
+
+ verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT),
+ capture(controlLoadCallbackCaptor))
+
+ controlLoadCallbackCaptor.value.accept(listOf(control))
+
+ delayableExecutor.runAllReady()
+
+ assertEquals(listOf(TEST_STRUCTURE_INFO),
+ controller.getFavoritesForComponent(TEST_COMPONENT))
+ assertTrue(succeeded)
+ }
+
+ @Test
+ fun testSeedFavoritesForComponent_error() {
+ var succeeded = false
+
+ controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
+ succeeded = accepted
+ })
+
+ verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT),
+ capture(controlLoadCallbackCaptor))
+
+ controlLoadCallbackCaptor.value.error("Error loading")
+
+ delayableExecutor.runAllReady()
+
+ assertEquals(listOf<StructureInfo>(), controller.getFavoritesForComponent(TEST_COMPONENT))
+ assertFalse(succeeded)
+ }
+
+ @Test
+ fun testSeedFavoritesForComponent_inProgressCallback() {
+ var succeeded = false
+ var seeded = false
+ val control = builderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure).build()
+
+ controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
+ succeeded = accepted
+ })
+
+ verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT),
+ capture(controlLoadCallbackCaptor))
+
+ controller.addSeedingFavoritesCallback(Consumer { accepted ->
+ seeded = accepted
+ })
+ controlLoadCallbackCaptor.value.accept(listOf(control))
+
+ delayableExecutor.runAllReady()
+
+ assertEquals(listOf(TEST_STRUCTURE_INFO),
+ controller.getFavoritesForComponent(TEST_COMPONENT))
+ assertTrue(succeeded)
+ assertTrue(seeded)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
index cd82844fcc50..789d6dfebf72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
@@ -92,6 +92,22 @@ class ServiceWrapperTest : SysuiTestCase() {
}
@Test
+ fun testLoadSuggested_happyPath() {
+ val result = wrapper.loadSuggested(subscriber)
+
+ assertTrue(result)
+ verify(service).loadSuggested(subscriber)
+ }
+
+ @Test
+ fun testLoadSuggested_error() {
+ `when`(service.loadSuggested(any())).thenThrow(exception)
+ val result = wrapper.loadSuggested(subscriber)
+
+ assertFalse(result)
+ }
+
+ @Test
fun testSubscribe_happyPath() {
val list = listOf("TEST_ID")
val result = wrapper.subscribe(list, subscriber)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
index ff5c8d44eee3..267520ef7f1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
@@ -59,13 +59,15 @@ class StatefulControlSubscriberTest : SysuiTestCase() {
private lateinit var scs: StatefulControlSubscriber
+ private val REQUEST_LIMIT = 5L
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
`when`(provider.componentName).thenReturn(TEST_COMPONENT)
`when`(provider.token).thenReturn(token)
- scs = StatefulControlSubscriber(controller, provider, executor)
+ scs = StatefulControlSubscriber(controller, provider, executor, REQUEST_LIMIT)
}
@Test
@@ -73,7 +75,7 @@ class StatefulControlSubscriberTest : SysuiTestCase() {
scs.onSubscribe(token, subscription)
executor.runAllReady()
- verify(provider).startSubscription(subscription)
+ verify(provider).startSubscription(subscription, REQUEST_LIMIT)
}
@Test
@@ -81,7 +83,7 @@ class StatefulControlSubscriberTest : SysuiTestCase() {
scs.onSubscribe(badToken, subscription)
executor.runAllReady()
- verify(provider, never()).startSubscription(subscription)
+ verify(provider, never()).startSubscription(subscription, REQUEST_LIMIT)
}
@Test
diff --git a/packages/Tethering/res/values-mcc204-mnc04/strings.xml b/packages/Tethering/res/values-mcc204-mnc04/strings.xml
new file mode 100644
index 000000000000..6bc2e2aa9205
--- /dev/null
+++ b/packages/Tethering/res/values-mcc204-mnc04/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources>
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_title">Tethering &amp; Mobile Hotspot has no internet access</string>
+
+ <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_title">Roaming</string>
+ <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+ <string name="upstream_roaming_notification_message">You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available</string>
+ <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_continue_button">Continue</string>
+</resources> \ No newline at end of file
diff --git a/packages/Tethering/res/values-mcc310-mnc004/strings.xml b/packages/Tethering/res/values-mcc310-mnc004/strings.xml
new file mode 100644
index 000000000000..6bc2e2aa9205
--- /dev/null
+++ b/packages/Tethering/res/values-mcc310-mnc004/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources>
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_title">Tethering &amp; Mobile Hotspot has no internet access</string>
+
+ <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_title">Roaming</string>
+ <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+ <string name="upstream_roaming_notification_message">You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available</string>
+ <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_continue_button">Continue</string>
+</resources> \ No newline at end of file
diff --git a/packages/Tethering/res/values-mcc311-mnc480/strings.xml b/packages/Tethering/res/values-mcc311-mnc480/strings.xml
new file mode 100644
index 000000000000..6bc2e2aa9205
--- /dev/null
+++ b/packages/Tethering/res/values-mcc311-mnc480/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<resources>
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_title">Tethering &amp; Mobile Hotspot has no internet access</string>
+
+ <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_title">Roaming</string>
+ <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+ <string name="upstream_roaming_notification_message">You will be charged on a per MB basis for all data sent or received while using this service outside the Verizon Network. Please check our website for current international rates. To minimize charges, visit My Verizon periodically to monitor your usage, check your device settings to confirm which devices are connected, and consider using alternate data connections when available</string>
+ <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_continue_button">Continue</string>
+</resources> \ No newline at end of file
diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml
index ba98a66ff7e0..d50884b2a7b0 100644
--- a/packages/Tethering/res/values/strings.xml
+++ b/packages/Tethering/res/values/strings.xml
@@ -32,4 +32,14 @@
Internet" settings page. That is currently the tether_settings_title_all string. -->
<!-- String for tether notification channel name [CHAR LIMIT=200] -->
<string name="notification_channel_tethering_status">Hotspot &amp; tethering status</string>
+
+ <!-- String for no upstream notification title [CHAR LIMIT=200] -->
+ <string name="no_upstream_notification_title"></string>
+
+ <!-- String for cellular roaming notification title [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_title"></string>
+ <!-- String for cellular roaming notification message [CHAR LIMIT=500] -->
+ <string name="upstream_roaming_notification_message"></string>
+ <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] -->
+ <string name="upstream_roaming_notification_continue_button"></string>
</resources> \ No newline at end of file
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index 020b32adcfd7..c5329d8d3316 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -33,6 +33,7 @@ import android.net.ITetheringConnector;
import android.net.ITetheringEventCallback;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.NetworkStack;
import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
@@ -364,8 +365,7 @@ public class TetheringService extends Service {
IBinder connector;
try {
final long before = System.currentTimeMillis();
- while ((connector = (IBinder) mContext.getSystemService(
- Context.NETWORK_STACK_SERVICE)) == null) {
+ while ((connector = NetworkStack.getService()) == null) {
if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
return null;
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index ad21075809ec..260703db190c 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -3647,10 +3647,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
private void updateAppOpsLocked(Host host, boolean visible) {
- // The launcher must be at TOP.
- final int procState = mActivityManagerInternal.getUidProcessState(host.id.uid);
- if (procState > ActivityManager.PROCESS_STATE_TOP) {
- return;
+ if (visible) {
+ final int procState = mActivityManagerInternal.getUidProcessState(host.id.uid);
+ if (procState > ActivityManager.PROCESS_STATE_TOP) {
+ // The launcher must be at TOP.
+ return;
+ }
}
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 53afa6e283d9..b4b0641e2f2a 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -37,7 +37,6 @@ import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.Dataset;
-import android.service.autofill.InlineAction;
import android.service.autofill.augmented.AugmentedAutofillService;
import android.service.autofill.augmented.IAugmentedAutofillService;
import android.service.autofill.augmented.IFillCallback;
@@ -167,12 +166,12 @@ final class RemoteAugmentedAutofillService
focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
new IFillCallback.Stub() {
@Override
- public void onSuccess(@Nullable List<Dataset> inlineSuggestionsData,
- @Nullable List<InlineAction> inlineActions) {
+ public void onSuccess(
+ @Nullable List<Dataset> inlineSuggestionsData) {
mCallbacks.resetLastResponse();
maybeRequestShowInlineSuggestions(sessionId,
inlineSuggestionsRequest, inlineSuggestionsData,
- inlineActions, focusedId, inlineSuggestionsCallback,
+ focusedId, inlineSuggestionsCallback,
client, onErrorCallback, remoteRenderService);
requestAutofill.complete(null);
}
@@ -237,8 +236,7 @@ final class RemoteAugmentedAutofillService
private void maybeRequestShowInlineSuggestions(int sessionId,
@Nullable InlineSuggestionsRequest request,
- @Nullable List<Dataset> inlineSuggestionsData,
- @Nullable List<InlineAction> inlineActions, @NonNull AutofillId focusedId,
+ @Nullable List<Dataset> inlineSuggestionsData, @NonNull AutofillId focusedId,
@Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback,
@NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
@@ -251,7 +249,7 @@ final class RemoteAugmentedAutofillService
final InlineSuggestionsResponse inlineSuggestionsResponse =
InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
- request, inlineSuggestionsData, inlineActions, focusedId,
+ request, inlineSuggestionsData, focusedId,
dataset -> {
mCallbacks.logAugmentedAutofillSelected(sessionId,
dataset.getId());
@@ -260,8 +258,13 @@ final class RemoteAugmentedAutofillService
final int size = fieldIds.size();
final boolean hideHighlight = size == 1
&& fieldIds.get(0).equals(focusedId);
- client.autofill(sessionId, fieldIds, dataset.getFieldValues(),
- hideHighlight);
+ if (dataset.getAuthentication() != null) {
+ client.startIntentSender(dataset.getAuthentication(),
+ new Intent());
+ } else {
+ client.autofill(sessionId, fieldIds, dataset.getFieldValues(),
+ hideHighlight);
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Encounter exception autofilling the values");
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index de3111867ab8..8032e9b70e0d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -311,6 +311,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
+ /**
+ * TODO(b/151867668): improve how asynchronous data dependencies are handled, without using
+ * CountDownLatch.
+ */
private final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub {
@GuardedBy("mLock")
@@ -318,7 +322,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private FillRequest mPendingFillRequest;
@GuardedBy("mLock")
- private CountDownLatch mCountDownLatch;
+ private CountDownLatch mCountDownLatch = new CountDownLatch(0);
@Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(
boolean isInlineRequest) {
@@ -327,6 +331,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mPendingInlineSuggestionsRequest = null;
return isInlineRequest ? (inlineSuggestionsRequest) -> {
synchronized (mLock) {
+ if (mCountDownLatch.getCount() == 0) {
+ return;
+ }
mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
mCountDownLatch.countDown();
maybeRequestFillLocked();
@@ -335,8 +342,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
void maybeRequestFillLocked() {
- if (mCountDownLatch == null || mCountDownLatch.getCount() > 0
- || mPendingFillRequest == null) {
+ if (mCountDownLatch.getCount() > 0 || mPendingFillRequest == null) {
return;
}
if (mPendingInlineSuggestionsRequest != null) {
@@ -347,7 +353,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mRemoteFillService.onFillRequest(mPendingFillRequest);
mPendingInlineSuggestionsRequest = null;
mPendingFillRequest = null;
- mCountDownLatch = null;
}
@Override
@@ -447,9 +452,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
request = new FillRequest(requestId, contexts, mClientState, flags,
/*inlineSuggestionsRequest=*/null);
- mPendingFillRequest = request;
- mCountDownLatch.countDown();
- maybeRequestFillLocked();
+ if (mCountDownLatch.getCount() > 0) {
+ mPendingFillRequest = request;
+ mCountDownLatch.countDown();
+ maybeRequestFillLocked();
+ } else {
+ // TODO(b/151867668): ideally this case should not happen, but it was
+ // observed, we should figure out why and fix.
+ mRemoteFillService.onFillRequest(request);
+ }
}
if (mActivityToken != null) {
@@ -2726,8 +2737,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
InlineSuggestionsResponse inlineSuggestionsResponse =
InlineSuggestionFactory.createInlineSuggestionsResponse(
- inlineSuggestionsRequest.get(),
- response, filterText, response.getInlineActions(), mCurrentViewId,
+ inlineSuggestionsRequest.get(), response, filterText, mCurrentViewId,
this, () -> {
synchronized (mLock) {
mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId);
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 0ca9dd92877f..71d4acec7c6a 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -21,13 +21,11 @@ import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.IntentSender;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.service.autofill.IInlineSuggestionUiCallback;
-import android.service.autofill.InlineAction;
import android.service.autofill.InlinePresentation;
import android.text.TextUtils;
import android.util.Slog;
@@ -73,8 +71,7 @@ public final class InlineSuggestionFactory {
@Nullable
public static InlineSuggestionsResponse createInlineSuggestionsResponse(
@NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
- @Nullable String filterText, @Nullable List<InlineAction> inlineActions,
- @NonNull AutofillId autofillId,
+ @Nullable String filterText, @NonNull AutofillId autofillId,
@NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
@@ -96,7 +93,7 @@ public final class InlineSuggestionFactory {
final InlinePresentation inlineAuthentication =
response.getAuthentication() == null ? null : response.getInlinePresentation();
return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request,
- response.getDatasets(), filterText, inlineAuthentication, inlineActions, autofillId,
+ response.getDatasets(), filterText, inlineAuthentication, autofillId,
onErrorCallback, onClickFactory, remoteRenderService);
}
@@ -107,15 +104,14 @@ public final class InlineSuggestionFactory {
@Nullable
public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
@NonNull InlineSuggestionsRequest request, @NonNull List<Dataset> datasets,
- @Nullable List<InlineAction> inlineActions, @NonNull AutofillId autofillId,
+ @NonNull AutofillId autofillId,
@NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
@NonNull Runnable onErrorCallback,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
datasets, /* filterText= */ null, /* inlineAuthentication= */ null,
- inlineActions, autofillId, onErrorCallback,
- (dataset, datasetIndex) ->
+ autofillId, onErrorCallback, (dataset, datasetIndex) ->
inlineSuggestionUiCallback.autofill(dataset), remoteRenderService);
}
@@ -123,8 +119,7 @@ public final class InlineSuggestionFactory {
private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
boolean isAugmented, @NonNull InlineSuggestionsRequest request,
@Nullable List<Dataset> datasets, @Nullable String filterText,
- @Nullable InlinePresentation inlineAuthentication,
- @Nullable List<InlineAction> inlineActions, @NonNull AutofillId autofillId,
+ @Nullable InlinePresentation inlineAuthentication, @NonNull AutofillId autofillId,
@NonNull Runnable onErrorCallback, @NonNull BiConsumer<Dataset, Integer> onClickFactory,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
@@ -167,16 +162,6 @@ public final class InlineSuggestionFactory {
inlineSuggestions.add(inlineSuggestion);
}
- if (inlineActions != null) {
- for (InlineAction inlineAction : inlineActions) {
- final InlineSuggestion inlineActionSuggestion = createInlineAction(isAugmented,
- mergedInlinePresentation(request, 0, inlineAction.getInlinePresentation()),
- inlineAction.getAction(),
- remoteRenderService, onErrorCallback, request.getHostInputToken(),
- request.getHostDisplayId());
- inlineSuggestions.add(inlineActionSuggestion);
- }
- }
return new InlineSuggestionsResponse(inlineSuggestions);
}
@@ -211,35 +196,6 @@ public final class InlineSuggestionFactory {
return valueText.toLowerCase().startsWith(constraintLowerCase);
}
-
- private static InlineSuggestion createInlineAction(boolean isAugmented,
- @NonNull InlinePresentation presentation,
- @NonNull IntentSender action,
- @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
- @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
- int displayId) {
- final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
- presentation.getInlinePresentationSpec(),
- isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
- : InlineSuggestionInfo.SOURCE_AUTOFILL,
- presentation.getAutofillHints(), InlineSuggestionInfo.TYPE_ACTION,
- presentation.isPinned());
- final Runnable onClickAction = () -> {
- try {
- // TODO(b/150499490): route the intent to the client app to have it fired there,
- // so that it will appear as a part of the same task as the client app (similar
- // to the authentication flow).
- action.sendIntent(null, 0, null, null, null);
- } catch (IntentSender.SendIntentException e) {
- onErrorCallback.run();
- Slog.w(TAG, "Error sending inline action intent");
- }
- };
- return new InlineSuggestion(inlineSuggestionInfo,
- createInlineContentProvider(presentation, onClickAction, onErrorCallback,
- remoteRenderService, hostInputToken, displayId));
- }
-
private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
@NonNull Dataset dataset, int datasetIndex,
@NonNull InlinePresentation inlinePresentation,
@@ -247,12 +203,15 @@ public final class InlineSuggestionFactory {
@NonNull RemoteInlineSuggestionRenderService remoteRenderService,
@NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
int displayId) {
+ final String suggestionSource = isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
+ : InlineSuggestionInfo.SOURCE_AUTOFILL;
+ final String suggestionType =
+ dataset.getAuthentication() == null ? InlineSuggestionInfo.TYPE_SUGGESTION
+ : InlineSuggestionInfo.TYPE_ACTION;
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
- inlinePresentation.getInlinePresentationSpec(),
- isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
- : InlineSuggestionInfo.SOURCE_AUTOFILL,
- inlinePresentation.getAutofillHints(),
- InlineSuggestionInfo.TYPE_SUGGESTION, inlinePresentation.isPinned());
+ inlinePresentation.getInlinePresentationSpec(), suggestionSource,
+ inlinePresentation.getAutofillHints(), suggestionType,
+ inlinePresentation.isPinned());
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation,
@@ -270,7 +229,7 @@ public final class InlineSuggestionFactory {
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
InlineSuggestionInfo.SOURCE_AUTOFILL, inlinePresentation.getAutofillHints(),
- InlineSuggestionInfo.TYPE_SUGGESTION, inlinePresentation.isPinned());
+ InlineSuggestionInfo.TYPE_ACTION, inlinePresentation.isPinned());
return new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation,
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b89b1eb9c85f..4309b849d419 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -110,6 +110,7 @@ import android.net.NetworkWatchlistManager;
import android.net.PrivateDnsConfigParcel;
import android.net.ProxyInfo;
import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.TetheringManager;
import android.net.UidRange;
@@ -120,6 +121,7 @@ import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
import android.net.shared.PrivateDnsConfig;
+import android.net.util.LinkPropertiesUtils.CompareOrUpdateResult;
import android.net.util.LinkPropertiesUtils.CompareResult;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
@@ -232,6 +234,7 @@ import java.util.SortedSet;
import java.util.StringJoiner;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
/**
* @hide
@@ -1710,7 +1713,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (checkSettingsPermission(callerPid, callerUid)) {
- return lp.makeSensitiveFieldsParcelingCopy();
+ return new LinkProperties(lp, true /* parcelSensitiveFields */);
}
final LinkProperties newLp = new LinkProperties(lp);
@@ -5951,15 +5954,49 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ // TODO: move to frameworks/libs/net.
+ private RouteInfoParcel convertRouteInfo(RouteInfo route) {
+ final String nextHop;
+
+ switch (route.getType()) {
+ case RouteInfo.RTN_UNICAST:
+ if (route.hasGateway()) {
+ nextHop = route.getGateway().getHostAddress();
+ } else {
+ nextHop = INetd.NEXTHOP_NONE;
+ }
+ break;
+ case RouteInfo.RTN_UNREACHABLE:
+ nextHop = INetd.NEXTHOP_UNREACHABLE;
+ break;
+ case RouteInfo.RTN_THROW:
+ nextHop = INetd.NEXTHOP_THROW;
+ break;
+ default:
+ nextHop = INetd.NEXTHOP_NONE;
+ break;
+ }
+
+ final RouteInfoParcel rip = new RouteInfoParcel();
+ rip.ifName = route.getInterface();
+ rip.destination = route.getDestination().toString();
+ rip.nextHop = nextHop;
+ rip.mtu = route.getMtu();
+
+ return rip;
+ }
+
/**
* Have netd update routes from oldLp to newLp.
* @return true if routes changed between oldLp and newLp
*/
private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
- // Compare the route diff to determine which routes should be added and removed.
- CompareResult<RouteInfo> routeDiff = new CompareResult<>(
+ Function<RouteInfo, IpPrefix> getDestination = (r) -> r.getDestination();
+ // compare the route diff to determine which routes have been updated
+ CompareOrUpdateResult<IpPrefix, RouteInfo> routeDiff = new CompareOrUpdateResult<>(
oldLp != null ? oldLp.getAllRoutes() : null,
- newLp != null ? newLp.getAllRoutes() : null);
+ newLp != null ? newLp.getAllRoutes() : null,
+ getDestination);
// add routes before removing old in case it helps with continuous connectivity
@@ -5968,10 +6005,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNMS.addRoute(netId, route);
+ mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
} catch (Exception e) {
if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
- loge("Exception in addRoute for non-gateway: " + e);
+ loge("Exception in networkAddRouteParcel for non-gateway: " + e);
}
}
}
@@ -5979,10 +6016,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (!route.hasGateway()) continue;
if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNMS.addRoute(netId, route);
+ mNetd.networkAddRouteParcel(netId, convertRouteInfo(route));
} catch (Exception e) {
if ((route.getGateway() instanceof Inet4Address) || VDBG) {
- loge("Exception in addRoute for gateway: " + e);
+ loge("Exception in networkAddRouteParcel for gateway: " + e);
}
}
}
@@ -5990,12 +6027,22 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (RouteInfo route : routeDiff.removed) {
if (VDBG || DDBG) log("Removing Route [" + route + "] from network " + netId);
try {
- mNMS.removeRoute(netId, route);
+ mNetd.networkRemoveRouteParcel(netId, convertRouteInfo(route));
+ } catch (Exception e) {
+ loge("Exception in networkRemoveRouteParcel: " + e);
+ }
+ }
+
+ for (RouteInfo route : routeDiff.updated) {
+ if (VDBG || DDBG) log("Updating Route [" + route + "] from network " + netId);
+ try {
+ mNetd.networkUpdateRouteParcel(netId, convertRouteInfo(route));
} catch (Exception e) {
- loge("Exception in removeRoute: " + e);
+ loge("Exception in networkUpdateRouteParcel: " + e);
}
}
- return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty();
+ return !routeDiff.added.isEmpty() || !routeDiff.removed.isEmpty()
+ || !routeDiff.updated.isEmpty();
}
private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ebca1f7e97a4..a45849823a1b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -579,6 +579,13 @@ public class ActivityManagerService extends IActivityManager.Stub
static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
+ /**
+ * The maximum number of bytes that {@link #setProcessStateSummary} accepts.
+ *
+ * @see {@link android.app.ActivityManager#setProcessStateSummary(byte[])}
+ */
+ static final int MAX_STATE_DATA_SIZE = 128;
+
/** All system services */
SystemServiceManager mSystemServiceManager;
@@ -3202,7 +3209,7 @@ public class ActivityManagerService extends IActivityManager.Stub
return mAtmInternal.compatibilityInfoForPackage(ai);
}
- private void enforceNotIsolatedCaller(String caller) {
+ /* package */ void enforceNotIsolatedCaller(String caller) {
if (UserHandle.isIsolated(Binder.getCallingUid())) {
throw new SecurityException("Isolated process not allowed to call " + caller);
}
@@ -3887,6 +3894,18 @@ public class ActivityManagerService extends IActivityManager.Stub
public static File dumpStackTraces(ArrayList<Integer> firstPids,
ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile) {
+ return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePids,
+ logExceptionCreatingFile, null);
+ }
+
+ /**
+ * @param firstPidOffsets Optional, when it's set, it receives the start/end offset
+ * of the very first pid to be dumped.
+ */
+ /* package */ static File dumpStackTraces(ArrayList<Integer> firstPids,
+ ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
+ ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile,
+ long[] firstPidOffsets) {
ArrayList<Integer> extraPids = null;
Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);
@@ -3938,12 +3957,22 @@ public class ActivityManagerService extends IActivityManager.Stub
return null;
}
- dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
+ Pair<Long, Long> offsets = dumpStackTraces(
+ tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
+ if (firstPidOffsets != null) {
+ if (offsets == null) {
+ firstPidOffsets[0] = firstPidOffsets[1] = -1;
+ } else {
+ firstPidOffsets[0] = offsets.first; // Start offset to the ANR trace file
+ firstPidOffsets[1] = offsets.second; // End offset to the ANR trace file
+ }
+ }
return tracesFile;
}
@GuardedBy("ActivityManagerService.class")
private static SimpleDateFormat sAnrFileDateFormat;
+ static final String ANR_FILE_PREFIX = "anr_";
private static synchronized File createAnrDumpFile(File tracesDir) throws IOException {
if (sAnrFileDateFormat == null) {
@@ -3951,7 +3980,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
final String formattedDate = sAnrFileDateFormat.format(new Date());
- final File anrFile = new File(tracesDir, "anr_" + formattedDate);
+ final File anrFile = new File(tracesDir, ANR_FILE_PREFIX + formattedDate);
if (anrFile.createNewFile()) {
FileUtils.setPermissions(anrFile.getAbsolutePath(), 0600, -1, -1); // -rw-------
@@ -4020,7 +4049,10 @@ public class ActivityManagerService extends IActivityManager.Stub
return SystemClock.elapsedRealtime() - timeStart;
}
- public static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
+ /**
+ * @return The start/end offset of the trace of the very first PID
+ */
+ public static Pair<Long, Long> dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
Slog.i(TAG, "Dumping to " + tracesFile);
@@ -4032,21 +4064,39 @@ public class ActivityManagerService extends IActivityManager.Stub
// We must complete all stack dumps within 20 seconds.
long remainingTime = 20 * 1000;
+ // As applications are usually interested with the ANR stack traces, but we can't share with
+ // them the stack traces other than their own stacks. So after the very first PID is
+ // dumped, remember the current file size.
+ long firstPidStart = -1;
+ long firstPidEnd = -1;
+
// First collect all of the stacks of the most important pids.
if (firstPids != null) {
int num = firstPids.size();
for (int i = 0; i < num; i++) {
- Slog.i(TAG, "Collecting stacks for pid " + firstPids.get(i));
- final long timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile,
+ final int pid = firstPids.get(i);
+ // We don't copy ANR traces from the system_server intentionally.
+ final boolean firstPid = i == 0 && MY_PID != pid;
+ File tf = null;
+ if (firstPid) {
+ tf = new File(tracesFile);
+ firstPidStart = tf.exists() ? tf.length() : 0;
+ }
+
+ Slog.i(TAG, "Collecting stacks for pid " + pid);
+ final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,
remainingTime);
remainingTime -= timeTaken;
if (remainingTime <= 0) {
- Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
- "); deadline exceeded.");
- return;
+ Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid
+ + "); deadline exceeded.");
+ return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
}
+ if (firstPid) {
+ firstPidEnd = tf.length();
+ }
if (DEBUG_ANR) {
Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
}
@@ -4068,7 +4118,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
"); deadline exceeded.");
- return;
+ return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
}
if (DEBUG_ANR) {
@@ -4088,7 +4138,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
"); deadline exceeded.");
- return;
+ return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
}
if (DEBUG_ANR) {
@@ -4097,6 +4147,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
Slog.i(TAG, "Done dumping");
+ return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
}
@Override
@@ -10280,6 +10331,15 @@ public class ActivityManagerService extends IActivityManager.Stub
return new ParceledListSlice<ApplicationExitInfo>(results);
}
+ @Override
+ public void setProcessStateSummary(@Nullable byte[] state) {
+ if (state != null && state.length > MAX_STATE_DATA_SIZE) {
+ throw new IllegalArgumentException("Data size is too large");
+ }
+ mProcessList.mAppExitInfoTracker.setProcessStateSummary(Binder.getCallingUid(),
+ Binder.getCallingPid(), state);
+ }
+
/**
* Check if the calling process has the permission to dump given package,
* throw SecurityException if it doesn't have the permission.
@@ -10287,7 +10347,7 @@ public class ActivityManagerService extends IActivityManager.Stub
* @return The UID of the given package, or {@link android.os.Process#INVALID_UID}
* if the package is not found.
*/
- private int enforceDumpPermissionForPackage(String packageName, int userId, int callingUid,
+ int enforceDumpPermissionForPackage(String packageName, int userId, int callingUid,
String function) {
long identity = Binder.clearCallingIdentity();
int uid = Process.INVALID_UID;
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 028a059c538a..0c3d02d678bd 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -17,23 +17,31 @@
package com.android.server.am;
import static android.app.ActivityManager.RunningAppProcessInfo.procStateToImportance;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.annotation.Nullable;
import android.app.ApplicationExitInfo;
import android.app.ApplicationExitInfo.Reason;
import android.app.ApplicationExitInfo.SubReason;
+import android.app.IAppTraceRetriever;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.icu.text.SimpleDateFormat;
+import android.os.Binder;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.system.OsConstants;
@@ -52,12 +60,17 @@ import android.util.proto.WireTypeMismatchException;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.IoThread;
import com.android.server.ServiceThread;
import com.android.server.SystemServiceManager;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -68,6 +81,10 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.zip.GZIPOutputStream;
/**
* A class to manage all the {@link android.app.ApplicationExitInfo} records.
@@ -80,16 +97,21 @@ public final class AppExitInfoTracker {
*/
private static final long APP_EXIT_INFO_PERSIST_INTERVAL = TimeUnit.MINUTES.toMillis(30);
- /** These are actions that the forEachPackage should take after each iteration */
+ /** These are actions that the forEach* should take after each iteration */
private static final int FOREACH_ACTION_NONE = 0;
- private static final int FOREACH_ACTION_REMOVE_PACKAGE = 1;
+ private static final int FOREACH_ACTION_REMOVE_ITEM = 1;
private static final int FOREACH_ACTION_STOP_ITERATION = 2;
private static final int APP_EXIT_RAW_INFO_POOL_SIZE = 8;
@VisibleForTesting
+ static final String APP_EXIT_STORE_DIR = "procexitstore";
+
+ @VisibleForTesting
static final String APP_EXIT_INFO_FILE = "procexitinfo";
+ private static final String APP_TRACE_FILE_SUFFIX = ".gz";
+
private final Object mLock = new Object();
/**
@@ -153,6 +175,13 @@ public final class AppExitInfoTracker {
final ArrayList<ApplicationExitInfo> mTmpInfoList2 = new ArrayList<ApplicationExitInfo>();
/**
+ * The path to the directory which includes the historical proc exit info file
+ * as specified in {@link #mProcExitInfoFile}, as well as the associated trace files.
+ */
+ @VisibleForTesting
+ File mProcExitStoreDir;
+
+ /**
* The path to the historical proc exit info file, persisted in the storage.
*/
@VisibleForTesting
@@ -176,6 +205,35 @@ public final class AppExitInfoTracker {
final AppExitInfoExternalSource mAppExitInfoSourceLmkd =
new AppExitInfoExternalSource("lmkd", ApplicationExitInfo.REASON_LOW_MEMORY);
+ /**
+ * The active per-UID/PID state data set by
+ * {@link android.app.ActivityManager#setProcessStateSummary};
+ * these state data are to be "claimed" when its process dies, by then the data will be moved
+ * from this list to the new instance of ApplicationExitInfo.
+ *
+ * <p> The mapping here is UID -> PID -> state </p>
+ *
+ * @see android.app.ActivityManager#setProcessStateSummary(byte[])
+ */
+ @GuardedBy("mLock")
+ final SparseArray<SparseArray<byte[]>> mActiveAppStateSummary = new SparseArray<>();
+
+ /**
+ * The active per-UID/PID trace file when an ANR occurs but the process hasn't been killed yet,
+ * each record is a path to the actual trace file; these files are to be "claimed"
+ * when its process dies, by then the "ownership" of the files will be transferred
+ * from this list to the new instance of ApplicationExitInfo.
+ *
+ * <p> The mapping here is UID -> PID -> file </p>
+ */
+ @GuardedBy("mLock")
+ final SparseArray<SparseArray<File>> mActiveAppTraces = new SparseArray<>();
+
+ /**
+ * The implementation of the interface IAppTraceRetriever.
+ */
+ final AppTraceRetriever mAppTraceRetriever = new AppTraceRetriever();
+
AppExitInfoTracker() {
mData = new ProcessMap<AppExitInfoContainer>();
mRawRecordsPool = new SynchronizedPool<ApplicationExitInfo>(APP_EXIT_RAW_INFO_POOL_SIZE);
@@ -187,7 +245,13 @@ public final class AppExitInfoTracker {
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
thread.start();
mKillHandler = new KillHandler(thread.getLooper());
- mProcExitInfoFile = new File(SystemServiceManager.ensureSystemDir(), APP_EXIT_INFO_FILE);
+
+ mProcExitStoreDir = new File(SystemServiceManager.ensureSystemDir(), APP_EXIT_STORE_DIR);
+ if (!FileUtils.createDir(mProcExitStoreDir)) {
+ Slog.e(TAG, "Unable to create " + mProcExitStoreDir);
+ return;
+ }
+ mProcExitInfoFile = new File(mProcExitStoreDir, APP_EXIT_INFO_FILE);
mAppExitInfoHistoryListSize = service.mContext.getResources().getInteger(
com.android.internal.R.integer.config_app_exit_info_history_list_size);
@@ -304,7 +368,7 @@ public final class AppExitInfoTracker {
+ "(" + raw.getPid() + "/u" + raw.getRealUid() + ")");
}
- ApplicationExitInfo info = getExitInfo(raw.getPackageName(),
+ ApplicationExitInfo info = getExitInfoLocked(raw.getPackageName(),
raw.getPackageUid(), raw.getPid());
// query zygote and lmkd to get the exit info, and clear the saved info
@@ -312,7 +376,7 @@ public final class AppExitInfoTracker {
raw.getPid(), raw.getRealUid());
Pair<Long, Object> lmkd = mAppExitInfoSourceLmkd.remove(
raw.getPid(), raw.getRealUid());
- mIsolatedUidRecords.removeIsolatedUid(raw.getRealUid());
+ mIsolatedUidRecords.removeIsolatedUidLocked(raw.getRealUid());
if (info == null) {
info = addExitInfoLocked(raw);
@@ -333,7 +397,7 @@ public final class AppExitInfoTracker {
@VisibleForTesting
@GuardedBy("mLock")
void handleNoteAppKillLocked(final ApplicationExitInfo raw) {
- ApplicationExitInfo info = getExitInfo(
+ ApplicationExitInfo info = getExitInfoLocked(
raw.getPackageName(), raw.getPackageUid(), raw.getPid());
if (info == null) {
@@ -359,7 +423,7 @@ public final class AppExitInfoTracker {
final String[] packages = raw.getPackageList();
final int uid = raw.getPackageUid();
for (int i = 0; i < packages.length; i++) {
- addExitInfoInner(packages[i], uid, info);
+ addExitInfoInnerLocked(packages[i], uid, info);
}
schedulePersistProcessExitInfo(false);
@@ -400,39 +464,37 @@ public final class AppExitInfoTracker {
*
* @return true if a recond is updated
*/
- private boolean updateExitInfoIfNecessary(int pid, int uid, Integer status, Integer reason) {
- synchronized (mLock) {
- if (UserHandle.isIsolated(uid)) {
- Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
- if (k != null) {
- uid = k;
- }
+ @GuardedBy("mLock")
+ private boolean updateExitInfoIfNecessaryLocked(
+ int pid, int uid, Integer status, Integer reason) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
+ if (k != null) {
+ uid = k;
+ }
+ ArrayList<ApplicationExitInfo> tlist = mTmpInfoList;
+ tlist.clear();
+ final int targetUid = uid;
+ forEachPackageLocked((packageName, records) -> {
+ AppExitInfoContainer container = records.get(targetUid);
+ if (container == null) {
+ return FOREACH_ACTION_NONE;
}
- ArrayList<ApplicationExitInfo> tlist = mTmpInfoList;
tlist.clear();
- final int targetUid = uid;
- forEachPackage((packageName, records) -> {
- AppExitInfoContainer container = records.get(targetUid);
- if (container == null) {
- return FOREACH_ACTION_NONE;
- }
+ container.getExitInfoLocked(pid, 1, tlist);
+ if (tlist.size() == 0) {
+ return FOREACH_ACTION_NONE;
+ }
+ ApplicationExitInfo info = tlist.get(0);
+ if (info.getRealUid() != targetUid) {
tlist.clear();
- container.getExitInfoLocked(pid, 1, tlist);
- if (tlist.size() == 0) {
- return FOREACH_ACTION_NONE;
- }
- ApplicationExitInfo info = tlist.get(0);
- if (info.getRealUid() != targetUid) {
- tlist.clear();
- return FOREACH_ACTION_NONE;
- }
- // Okay found it, update its reason.
- updateExistingExitInfoRecordLocked(info, status, reason);
+ return FOREACH_ACTION_NONE;
+ }
+ // Okay found it, update its reason.
+ updateExistingExitInfoRecordLocked(info, status, reason);
- return FOREACH_ACTION_STOP_ITERATION;
- });
- return tlist.size() > 0;
- }
+ return FOREACH_ACTION_STOP_ITERATION;
+ });
+ return tlist.size() > 0;
}
/**
@@ -441,38 +503,43 @@ public final class AppExitInfoTracker {
@VisibleForTesting
void getExitInfo(final String packageName, final int filterUid,
final int filterPid, final int maxNum, final ArrayList<ApplicationExitInfo> results) {
- synchronized (mLock) {
- boolean emptyPackageName = TextUtils.isEmpty(packageName);
- if (!emptyPackageName) {
- // fast path
- AppExitInfoContainer container = mData.get(packageName, filterUid);
- if (container != null) {
- container.getExitInfoLocked(filterPid, maxNum, results);
- }
- } else {
- // slow path
- final ArrayList<ApplicationExitInfo> list = mTmpInfoList2;
- list.clear();
- // get all packages
- forEachPackage((name, records) -> {
- AppExitInfoContainer container = records.get(filterUid);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ boolean emptyPackageName = TextUtils.isEmpty(packageName);
+ if (!emptyPackageName) {
+ // fast path
+ AppExitInfoContainer container = mData.get(packageName, filterUid);
if (container != null) {
- mTmpInfoList.clear();
- results.addAll(container.toListLocked(mTmpInfoList, filterPid));
+ container.getExitInfoLocked(filterPid, maxNum, results);
}
- return AppExitInfoTracker.FOREACH_ACTION_NONE;
- });
+ } else {
+ // slow path
+ final ArrayList<ApplicationExitInfo> list = mTmpInfoList2;
+ list.clear();
+ // get all packages
+ forEachPackageLocked((name, records) -> {
+ AppExitInfoContainer container = records.get(filterUid);
+ if (container != null) {
+ mTmpInfoList.clear();
+ results.addAll(container.toListLocked(mTmpInfoList, filterPid));
+ }
+ return AppExitInfoTracker.FOREACH_ACTION_NONE;
+ });
- Collections.sort(list, (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
- int size = list.size();
- if (maxNum > 0) {
- size = Math.min(size, maxNum);
- }
- for (int i = 0; i < size; i++) {
- results.add(list.get(i));
+ Collections.sort(list, (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
+ int size = list.size();
+ if (maxNum > 0) {
+ size = Math.min(size, maxNum);
+ }
+ for (int i = 0; i < size; i++) {
+ results.add(list.get(i));
+ }
+ list.clear();
}
- list.clear();
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -480,17 +547,16 @@ public final class AppExitInfoTracker {
* Return the first matching exit info record, for internal use, the parameters are not supposed
* to be empty.
*/
- private ApplicationExitInfo getExitInfo(final String packageName,
+ @GuardedBy("mLock")
+ private ApplicationExitInfo getExitInfoLocked(final String packageName,
final int filterUid, final int filterPid) {
- synchronized (mLock) {
- ArrayList<ApplicationExitInfo> list = mTmpInfoList;
- list.clear();
- getExitInfo(packageName, filterUid, filterPid, 1, list);
+ ArrayList<ApplicationExitInfo> list = mTmpInfoList;
+ list.clear();
+ getExitInfo(packageName, filterUid, filterPid, 1, list);
- ApplicationExitInfo info = list.size() > 0 ? list.get(0) : null;
- list.clear();
- return info;
- }
+ ApplicationExitInfo info = list.size() > 0 ? list.get(0) : null;
+ list.clear();
+ return info;
}
@VisibleForTesting
@@ -498,8 +564,10 @@ public final class AppExitInfoTracker {
mAppExitInfoSourceZygote.removeByUserId(userId);
mAppExitInfoSourceLmkd.removeByUserId(userId);
mIsolatedUidRecords.removeByUserId(userId);
- removeByUserId(userId);
- schedulePersistProcessExitInfo(true);
+ synchronized (mLock) {
+ removeByUserIdLocked(userId);
+ schedulePersistProcessExitInfo(true);
+ }
}
@VisibleForTesting
@@ -507,13 +575,16 @@ public final class AppExitInfoTracker {
if (packageName != null) {
final boolean removeUid = TextUtils.isEmpty(
mService.mPackageManagerInt.getNameForUid(uid));
- if (removeUid) {
- mAppExitInfoSourceZygote.removeByUid(uid, allUsers);
- mAppExitInfoSourceLmkd.removeByUid(uid, allUsers);
- mIsolatedUidRecords.removeAppUid(uid, allUsers);
+ synchronized (mLock) {
+ if (removeUid) {
+ mAppExitInfoSourceZygote.removeByUidLocked(uid, allUsers);
+ mAppExitInfoSourceLmkd.removeByUidLocked(uid, allUsers);
+ mIsolatedUidRecords.removeAppUid(uid, allUsers);
+ }
+ removePackageLocked(packageName, uid, removeUid,
+ allUsers ? UserHandle.USER_ALL : UserHandle.getUserId(uid));
+ schedulePersistProcessExitInfo(true);
}
- removePackage(packageName, allUsers ? UserHandle.USER_ALL : UserHandle.getUserId(uid));
- schedulePersistProcessExitInfo(true);
}
}
@@ -593,6 +664,7 @@ public final class AppExitInfoTracker {
}
}
synchronized (mLock) {
+ pruneAnrTracesIfNecessaryLocked();
mAppExitInfoLoaded = true;
}
}
@@ -634,7 +706,7 @@ public final class AppExitInfoTracker {
ProtoOutputStream proto = new ProtoOutputStream(out);
proto.write(AppsExitInfoProto.LAST_UPDATE_TIMESTAMP, now);
synchronized (mLock) {
- forEachPackage((packageName, records) -> {
+ forEachPackageLocked((packageName, records) -> {
long token = proto.start(AppsExitInfoProto.PACKAGES);
proto.write(AppsExitInfoProto.Package.PACKAGE_NAME, packageName);
int uidArraySize = records.size();
@@ -688,6 +760,9 @@ public final class AppExitInfoTracker {
mProcExitInfoFile.delete();
}
mData.getMap().clear();
+ mActiveAppStateSummary.clear();
+ mActiveAppTraces.clear();
+ pruneAnrTracesIfNecessaryLocked();
}
}
@@ -695,15 +770,15 @@ public final class AppExitInfoTracker {
* Helper function for shell command
*/
void clearHistoryProcessExitInfo(String packageName, int userId) {
- synchronized (mLock) {
- if (TextUtils.isEmpty(packageName)) {
- if (userId == UserHandle.USER_ALL) {
- mData.getMap().clear();
- } else {
- removeByUserId(userId);
- }
- } else {
- removePackage(packageName, userId);
+ if (TextUtils.isEmpty(packageName)) {
+ synchronized (mLock) {
+ removeByUserIdLocked(userId);
+ }
+ } else {
+ final int uid = mService.mPackageManagerInt.getPackageUid(packageName,
+ PackageManager.MATCH_ALL, userId);
+ synchronized (mLock) {
+ removePackageLocked(packageName, uid, true, userId);
}
}
schedulePersistProcessExitInfo(true);
@@ -716,7 +791,7 @@ public final class AppExitInfoTracker {
pw.println("Last Timestamp of Persistence Into Persistent Storage: "
+ sdf.format(new Date(mLastAppExitInfoPersistTimestamp)));
if (TextUtils.isEmpty(packageName)) {
- forEachPackage((name, records) -> {
+ forEachPackageLocked((name, records) -> {
dumpHistoryProcessExitInfoLocked(pw, " ", name, records, sdf);
return AppExitInfoTracker.FOREACH_ACTION_NONE;
});
@@ -741,86 +816,108 @@ public final class AppExitInfoTracker {
}
}
- private void addExitInfoInner(String packageName, int userId, ApplicationExitInfo info) {
- synchronized (mLock) {
- AppExitInfoContainer container = mData.get(packageName, userId);
- if (container == null) {
- container = new AppExitInfoContainer(mAppExitInfoHistoryListSize);
- if (UserHandle.isIsolated(info.getRealUid())) {
- Integer k = mIsolatedUidRecords.getUidByIsolatedUid(info.getRealUid());
- if (k != null) {
- container.mUid = k;
- }
- } else {
- container.mUid = info.getRealUid();
+ @GuardedBy("mLock")
+ private void addExitInfoInnerLocked(String packageName, int userId, ApplicationExitInfo info) {
+ AppExitInfoContainer container = mData.get(packageName, userId);
+ if (container == null) {
+ container = new AppExitInfoContainer(mAppExitInfoHistoryListSize);
+ if (UserHandle.isIsolated(info.getRealUid())) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(info.getRealUid());
+ if (k != null) {
+ container.mUid = k;
}
- mData.put(packageName, userId, container);
+ } else {
+ container.mUid = info.getRealUid();
}
- container.addExitInfoLocked(info);
+ mData.put(packageName, userId, container);
}
+ container.addExitInfoLocked(info);
}
- private void forEachPackage(
+ @GuardedBy("mLocked")
+ private void forEachPackageLocked(
BiFunction<String, SparseArray<AppExitInfoContainer>, Integer> callback) {
if (callback != null) {
- synchronized (mLock) {
- ArrayMap<String, SparseArray<AppExitInfoContainer>> map = mData.getMap();
- for (int i = map.size() - 1; i >= 0; i--) {
- switch (callback.apply(map.keyAt(i), map.valueAt(i))) {
- case FOREACH_ACTION_REMOVE_PACKAGE:
- map.removeAt(i);
- break;
- case FOREACH_ACTION_STOP_ITERATION:
- i = 0;
- break;
- case FOREACH_ACTION_NONE:
- default:
- break;
- }
+ ArrayMap<String, SparseArray<AppExitInfoContainer>> map = mData.getMap();
+ for (int i = map.size() - 1; i >= 0; i--) {
+ switch (callback.apply(map.keyAt(i), map.valueAt(i))) {
+ case FOREACH_ACTION_REMOVE_ITEM:
+ final SparseArray<AppExitInfoContainer> records = map.valueAt(i);
+ for (int j = records.size() - 1; j >= 0; j--) {
+ records.valueAt(j).destroyLocked();
+ }
+ map.removeAt(i);
+ break;
+ case FOREACH_ACTION_STOP_ITERATION:
+ i = 0;
+ break;
+ case FOREACH_ACTION_NONE:
+ default:
+ break;
}
}
}
}
- private void removePackage(String packageName, int userId) {
- synchronized (mLock) {
- if (userId == UserHandle.USER_ALL) {
- mData.getMap().remove(packageName);
- } else {
- ArrayMap<String, SparseArray<AppExitInfoContainer>> map =
- mData.getMap();
- SparseArray<AppExitInfoContainer> array = map.get(packageName);
- if (array == null) {
- return;
- }
+ @GuardedBy("mLocked")
+ private void removePackageLocked(String packageName, int uid, boolean removeUid, int userId) {
+ if (removeUid) {
+ mActiveAppStateSummary.remove(uid);
+ final int idx = mActiveAppTraces.indexOfKey(uid);
+ if (idx >= 0) {
+ final SparseArray<File> array = mActiveAppTraces.valueAt(idx);
for (int i = array.size() - 1; i >= 0; i--) {
- if (UserHandle.getUserId(array.keyAt(i)) == userId) {
- array.removeAt(i);
- break;
- }
+ array.valueAt(i).delete();
}
- if (array.size() == 0) {
- map.remove(packageName);
+ mActiveAppTraces.removeAt(idx);
+ }
+ }
+ ArrayMap<String, SparseArray<AppExitInfoContainer>> map = mData.getMap();
+ SparseArray<AppExitInfoContainer> array = map.get(packageName);
+ if (array == null) {
+ return;
+ }
+ if (userId == UserHandle.USER_ALL) {
+ for (int i = array.size() - 1; i >= 0; i--) {
+ array.valueAt(i).destroyLocked();
+ }
+ mData.getMap().remove(packageName);
+ } else {
+ for (int i = array.size() - 1; i >= 0; i--) {
+ if (UserHandle.getUserId(array.keyAt(i)) == userId) {
+ array.valueAt(i).destroyLocked();
+ array.removeAt(i);
+ break;
}
}
+ if (array.size() == 0) {
+ map.remove(packageName);
+ }
}
}
- private void removeByUserId(final int userId) {
+ @GuardedBy("mLocked")
+ private void removeByUserIdLocked(final int userId) {
if (userId == UserHandle.USER_ALL) {
- synchronized (mLock) {
- mData.getMap().clear();
- }
+ mData.getMap().clear();
+ mActiveAppStateSummary.clear();
+ mActiveAppTraces.clear();
+ pruneAnrTracesIfNecessaryLocked();
return;
}
- forEachPackage((packageName, records) -> {
+ removeFromSparse2dArray(mActiveAppStateSummary,
+ (v) -> UserHandle.getUserId(v) == userId, null, null);
+ removeFromSparse2dArray(mActiveAppTraces,
+ (v) -> UserHandle.getUserId(v) == userId, null, (v) -> v.delete());
+ forEachPackageLocked((packageName, records) -> {
for (int i = records.size() - 1; i >= 0; i--) {
if (UserHandle.getUserId(records.keyAt(i)) == userId) {
+ records.valueAt(i).destroyLocked();
records.removeAt(i);
break;
}
}
- return records.size() == 0 ? FOREACH_ACTION_REMOVE_PACKAGE : FOREACH_ACTION_NONE;
+ return records.size() == 0 ? FOREACH_ACTION_REMOVE_ITEM : FOREACH_ACTION_NONE;
});
}
@@ -862,6 +959,262 @@ public final class AppExitInfoTracker {
}
/**
+ * Called from {@link ActivityManagerService#setProcessStateSummary}.
+ */
+ @VisibleForTesting
+ void setProcessStateSummary(int uid, final int pid, final byte[] data) {
+ synchronized (mLock) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
+ if (k != null) {
+ uid = k;
+ }
+ putToSparse2dArray(mActiveAppStateSummary, uid, pid, data, SparseArray::new, null);
+ }
+ }
+
+ @VisibleForTesting
+ @Nullable byte[] getProcessStateSummary(int uid, final int pid) {
+ synchronized (mLock) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
+ if (k != null) {
+ uid = k;
+ }
+ int index = mActiveAppStateSummary.indexOfKey(uid);
+ if (index < 0) {
+ return null;
+ }
+ return mActiveAppStateSummary.valueAt(index).get(pid);
+ }
+ }
+
+ /**
+ * Called from ProcessRecord when an ANR occurred and the ANR trace is taken.
+ */
+ void scheduleLogAnrTrace(final int pid, final int uid, final String[] packageList,
+ final File traceFile, final long startOff, final long endOff) {
+ mKillHandler.sendMessage(PooledLambda.obtainMessage(
+ this::handleLogAnrTrace, pid, uid, packageList,
+ traceFile, startOff, endOff));
+ }
+
+ /**
+ * Copy and compress the given ANR trace file
+ */
+ @VisibleForTesting
+ void handleLogAnrTrace(final int pid, int uid, final String[] packageList,
+ final File traceFile, final long startOff, final long endOff) {
+ if (!traceFile.exists() || ArrayUtils.isEmpty(packageList)) {
+ return;
+ }
+ final long size = traceFile.length();
+ final long length = endOff - startOff;
+ if (startOff >= size || endOff > size || length <= 0) {
+ return;
+ }
+
+ final File outFile = new File(mProcExitStoreDir, traceFile.getName()
+ + APP_TRACE_FILE_SUFFIX);
+ // Copy & compress
+ if (copyToGzFile(traceFile, outFile, startOff, length)) {
+ // Wrote successfully.
+ synchronized (mLock) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
+ if (k != null) {
+ uid = k;
+ }
+ if (DEBUG_PROCESSES) {
+ Slog.i(TAG, "Stored ANR traces of " + pid + "/u" + uid + " in " + outFile);
+ }
+ boolean pending = true;
+ // Unlikely but possible: the app has died
+ for (int i = 0; i < packageList.length; i++) {
+ final AppExitInfoContainer container = mData.get(packageList[i], uid);
+ // Try to see if we could append this trace to an existing record
+ if (container != null && container.appendTraceIfNecessaryLocked(pid, outFile)) {
+ // Okay someone took it
+ pending = false;
+ }
+ }
+ if (pending) {
+ // Save it into a temporary list for later use (when the app dies).
+ putToSparse2dArray(mActiveAppTraces, uid, pid, outFile,
+ SparseArray::new, (v) -> v.delete());
+ }
+ }
+ }
+ }
+
+ /**
+ * Copy the given portion of the file into a gz file.
+ *
+ * @param inFile The source file.
+ * @param outFile The destination file, which will be compressed in gzip format.
+ * @param start The start offset where the copy should start from.
+ * @param length The number of bytes that should be copied.
+ * @return If the copy was successful or not.
+ */
+ private static boolean copyToGzFile(final File inFile, final File outFile,
+ final long start, final long length) {
+ long remaining = length;
+ try (
+ BufferedInputStream in = new BufferedInputStream(new FileInputStream(inFile));
+ GZIPOutputStream out = new GZIPOutputStream(new BufferedOutputStream(
+ new FileOutputStream(outFile)))) {
+ final byte[] buffer = new byte[8192];
+ in.skip(start);
+ while (remaining > 0) {
+ int t = in.read(buffer, 0, (int) Math.min(buffer.length, remaining));
+ if (t < 0) {
+ break;
+ }
+ out.write(buffer, 0, t);
+ remaining -= t;
+ }
+ } catch (IOException e) {
+ if (DEBUG_PROCESSES) {
+ Slog.e(TAG, "Error in copying ANR trace from " + inFile + " to " + outFile, e);
+ }
+ return false;
+ }
+ return remaining == 0 && outFile.exists();
+ }
+
+ /**
+ * In case there is any orphan ANR trace file, remove it.
+ */
+ @GuardedBy("mLock")
+ private void pruneAnrTracesIfNecessaryLocked() {
+ final ArraySet<String> allFiles = new ArraySet();
+ final File[] files = mProcExitStoreDir.listFiles((f) -> {
+ final String name = f.getName();
+ boolean trace = name.startsWith(ActivityManagerService.ANR_FILE_PREFIX)
+ && name.endsWith(APP_TRACE_FILE_SUFFIX);
+ if (trace) {
+ allFiles.add(name);
+ }
+ return trace;
+ });
+ if (ArrayUtils.isEmpty(files)) {
+ return;
+ }
+ // Find out the owners from the existing records
+ forEachPackageLocked((name, records) -> {
+ for (int i = records.size() - 1; i >= 0; i--) {
+ final AppExitInfoContainer container = records.valueAt(i);
+ container.forEachRecordLocked((pid, info) -> {
+ final File traceFile = info.getTraceFile();
+ if (traceFile != null) {
+ allFiles.remove(traceFile.getName());
+ }
+ return FOREACH_ACTION_NONE;
+ });
+ }
+ return AppExitInfoTracker.FOREACH_ACTION_NONE;
+ });
+ // See if there is any active process owns it.
+ forEachSparse2dArray(mActiveAppTraces, (v) -> allFiles.remove(v.getName()));
+
+ // Remove orphan traces if nobody claims it.
+ for (int i = allFiles.size() - 1; i >= 0; i--) {
+ (new File(mProcExitStoreDir, allFiles.valueAt(i))).delete();
+ }
+ }
+
+ /**
+ * A utility function to add the given value to the given 2d SparseArray
+ */
+ private static <T extends SparseArray<U>, U> void putToSparse2dArray(final SparseArray<T> array,
+ final int outerKey, final int innerKey, final U value, final Supplier<T> newInstance,
+ final Consumer<U> actionToOldValue) {
+ int idx = array.indexOfKey(outerKey);
+ T innerArray = null;
+ if (idx < 0) {
+ innerArray = newInstance.get();
+ array.put(outerKey, innerArray);
+ } else {
+ innerArray = array.valueAt(idx);
+ }
+ idx = innerArray.indexOfKey(innerKey);
+ if (idx >= 0) {
+ if (actionToOldValue != null) {
+ actionToOldValue.accept(innerArray.valueAt(idx));
+ }
+ innerArray.setValueAt(idx, value);
+ } else {
+ innerArray.put(innerKey, value);
+ }
+ }
+
+ /**
+ * A utility function to iterate through the given 2d SparseArray
+ */
+ private static <T extends SparseArray<U>, U> void forEachSparse2dArray(
+ final SparseArray<T> array, final Consumer<U> action) {
+ if (action != null) {
+ for (int i = array.size() - 1; i >= 0; i--) {
+ T innerArray = array.valueAt(i);
+ if (innerArray == null) {
+ continue;
+ }
+ for (int j = innerArray.size() - 1; j >= 0; j--) {
+ action.accept(innerArray.valueAt(j));
+ }
+ }
+ }
+ }
+
+ /**
+ * A utility function to remove elements from the given 2d SparseArray
+ */
+ private static <T extends SparseArray<U>, U> void removeFromSparse2dArray(
+ final SparseArray<T> array, final Predicate<Integer> outerPredicate,
+ final Predicate<Integer> innerPredicate, final Consumer<U> action) {
+ for (int i = array.size() - 1; i >= 0; i--) {
+ if (outerPredicate == null || outerPredicate.test(array.keyAt(i))) {
+ final T innerArray = array.valueAt(i);
+ if (innerArray == null) {
+ continue;
+ }
+ for (int j = innerArray.size() - 1; j >= 0; j--) {
+ if (innerPredicate == null || innerPredicate.test(innerArray.keyAt(j))) {
+ if (action != null) {
+ action.accept(innerArray.valueAt(j));
+ }
+ innerArray.removeAt(j);
+ }
+ }
+ if (innerArray.size() == 0) {
+ array.removeAt(i);
+ }
+ }
+ }
+ }
+
+ /**
+ * A utility function to find and remove elements from the given 2d SparseArray.
+ */
+ private static <T extends SparseArray<U>, U> U findAndRemoveFromSparse2dArray(
+ final SparseArray<T> array, final int outerKey, final int innerKey) {
+ final int idx = array.indexOfKey(outerKey);
+ if (idx >= 0) {
+ T p = array.valueAt(idx);
+ if (p == null) {
+ return null;
+ }
+ final int innerIdx = p.indexOfKey(innerKey);
+ if (innerIdx >= 0) {
+ final U ret = p.valueAt(innerIdx);
+ p.removeAt(innerIdx);
+ if (p.size() == 0) {
+ array.removeAt(idx);
+ }
+ return ret;
+ }
+ }
+ return null;
+ }
+
+ /**
* A container class of {@link android.app.ApplicationExitInfo}
*/
final class AppExitInfoContainer {
@@ -934,10 +1287,68 @@ public final class AppExitInfoTracker {
}
}
if (oldestIndex >= 0) {
+ final File traceFile = mInfos.valueAt(oldestIndex).getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
mInfos.removeAt(oldestIndex);
}
}
- mInfos.append(info.getPid(), info);
+ // Claim the state information if there is any
+ final int uid = info.getPackageUid();
+ final int pid = info.getPid();
+ info.setProcessStateSummary(findAndRemoveFromSparse2dArray(
+ mActiveAppStateSummary, uid, pid));
+ info.setTraceFile(findAndRemoveFromSparse2dArray(mActiveAppTraces, uid, pid));
+ info.setAppTraceRetriever(mAppTraceRetriever);
+ mInfos.append(pid, info);
+ }
+
+ @GuardedBy("mLock")
+ boolean appendTraceIfNecessaryLocked(final int pid, final File traceFile) {
+ final ApplicationExitInfo r = mInfos.get(pid);
+ if (r != null) {
+ r.setTraceFile(traceFile);
+ r.setAppTraceRetriever(mAppTraceRetriever);
+ return true;
+ }
+ return false;
+ }
+
+ @GuardedBy("mLock")
+ void destroyLocked() {
+ for (int i = mInfos.size() - 1; i >= 0; i--) {
+ ApplicationExitInfo ai = mInfos.valueAt(i);
+ final File traceFile = ai.getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
+ ai.setTraceFile(null);
+ ai.setAppTraceRetriever(null);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void forEachRecordLocked(final BiFunction<Integer, ApplicationExitInfo, Integer> callback) {
+ if (callback != null) {
+ for (int i = mInfos.size() - 1; i >= 0; i--) {
+ switch (callback.apply(mInfos.keyAt(i), mInfos.valueAt(i))) {
+ case FOREACH_ACTION_REMOVE_ITEM:
+ final File traceFile = mInfos.valueAt(i).getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
+ mInfos.removeAt(i);
+ break;
+ case FOREACH_ACTION_STOP_ITERATION:
+ i = 0;
+ break;
+ case FOREACH_ACTION_NONE:
+ default:
+ break;
+ }
+ }
+ }
}
@GuardedBy("mLock")
@@ -1033,6 +1444,7 @@ public final class AppExitInfoTracker {
}
}
+ @GuardedBy("mLock")
Integer getUidByIsolatedUid(int isolatedUid) {
if (UserHandle.isIsolated(isolatedUid)) {
synchronized (mLock) {
@@ -1053,6 +1465,7 @@ public final class AppExitInfoTracker {
}
}
+ @VisibleForTesting
void removeAppUid(int uid, boolean allUsers) {
synchronized (mLock) {
if (allUsers) {
@@ -1071,23 +1484,22 @@ public final class AppExitInfoTracker {
}
}
- int removeIsolatedUid(int isolatedUid) {
+ @GuardedBy("mLock")
+ int removeIsolatedUidLocked(int isolatedUid) {
if (!UserHandle.isIsolated(isolatedUid)) {
return isolatedUid;
}
- synchronized (mLock) {
- int uid = mIsolatedUidToUidMap.get(isolatedUid, -1);
- if (uid == -1) {
- return isolatedUid;
- }
- mIsolatedUidToUidMap.remove(isolatedUid);
- ArraySet<Integer> set = mUidToIsolatedUidMap.get(uid);
- if (set != null) {
- set.remove(isolatedUid);
- }
- // let the ArraySet stay in the mUidToIsolatedUidMap even if it's empty
- return uid;
+ int uid = mIsolatedUidToUidMap.get(isolatedUid, -1);
+ if (uid == -1) {
+ return isolatedUid;
}
+ mIsolatedUidToUidMap.remove(isolatedUid);
+ ArraySet<Integer> set = mUidToIsolatedUidMap.get(uid);
+ if (set != null) {
+ set.remove(isolatedUid);
+ }
+ // let the ArraySet stay in the mUidToIsolatedUidMap even if it's empty
+ return uid;
}
void removeByUserId(int userId) {
@@ -1193,33 +1605,29 @@ public final class AppExitInfoTracker {
mPresetReason = reason;
}
- void add(int pid, int uid, Object extra) {
- if (UserHandle.isIsolated(uid)) {
- Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
- if (k != null) {
- uid = k;
- }
+ @GuardedBy("mLock")
+ private void addLocked(int pid, int uid, Object extra) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
+ if (k != null) {
+ uid = k;
}
- synchronized (mLock) {
- SparseArray<Pair<Long, Object>> array = mData.get(uid);
- if (array == null) {
- array = new SparseArray<Pair<Long, Object>>();
- mData.put(uid, array);
- }
- array.put(pid, new Pair<Long, Object>(System.currentTimeMillis(), extra));
+ SparseArray<Pair<Long, Object>> array = mData.get(uid);
+ if (array == null) {
+ array = new SparseArray<Pair<Long, Object>>();
+ mData.put(uid, array);
}
+ array.put(pid, new Pair<Long, Object>(System.currentTimeMillis(), extra));
}
+ @VisibleForTesting
Pair<Long, Object> remove(int pid, int uid) {
- if (UserHandle.isIsolated(uid)) {
+ synchronized (mLock) {
Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
if (k != null) {
uid = k;
}
- }
- synchronized (mLock) {
SparseArray<Pair<Long, Object>> array = mData.get(uid);
if (array != null) {
Pair<Long, Object> p = array.get(pid);
@@ -1228,8 +1636,8 @@ public final class AppExitInfoTracker {
return isFresh(p.first) ? p : null;
}
}
+ return null;
}
- return null;
}
void removeByUserId(int userId) {
@@ -1250,7 +1658,8 @@ public final class AppExitInfoTracker {
}
}
- void removeByUid(int uid, boolean allUsers) {
+ @GuardedBy("mLock")
+ void removeByUidLocked(int uid, boolean allUsers) {
if (UserHandle.isIsolated(uid)) {
Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
if (k != null) {
@@ -1260,17 +1669,13 @@ public final class AppExitInfoTracker {
if (allUsers) {
uid = UserHandle.getAppId(uid);
- synchronized (mLock) {
- for (int i = mData.size() - 1; i >= 0; i--) {
- if (UserHandle.getAppId(mData.keyAt(i)) == uid) {
- mData.removeAt(i);
- }
+ for (int i = mData.size() - 1; i >= 0; i--) {
+ if (UserHandle.getAppId(mData.keyAt(i)) == uid) {
+ mData.removeAt(i);
}
}
} else {
- synchronized (mLock) {
- mData.remove(uid);
- }
+ mData.remove(uid);
}
}
@@ -1292,12 +1697,12 @@ public final class AppExitInfoTracker {
// Unlikely but possible: the record has been created
// Let's update it if we could find a ApplicationExitInfo record
- if (!updateExitInfoIfNecessary(pid, uid, status, mPresetReason)) {
- add(pid, uid, status);
- }
-
- // Notify any interesed party regarding the lmkd kills
synchronized (mLock) {
+ if (!updateExitInfoIfNecessaryLocked(pid, uid, status, mPresetReason)) {
+ addLocked(pid, uid, status);
+ }
+
+ // Notify any interesed party regarding the lmkd kills
final BiConsumer<Integer, Integer> listener = mProcDiedListener;
if (listener != null) {
mService.mHandler.post(()-> listener.accept(pid, uid));
@@ -1305,4 +1710,51 @@ public final class AppExitInfoTracker {
}
}
}
+
+ /**
+ * The implementation to the IAppTraceRetriever interface.
+ */
+ @VisibleForTesting
+ class AppTraceRetriever extends IAppTraceRetriever.Stub {
+ @Override
+ public ParcelFileDescriptor getTraceFileDescriptor(final String packageName,
+ final int uid, final int pid) {
+ mService.enforceNotIsolatedCaller("getTraceFileDescriptor");
+
+ if (TextUtils.isEmpty(packageName)) {
+ throw new IllegalArgumentException("Invalid package name");
+ }
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getCallingUserId();
+ final int userId = UserHandle.getUserId(uid);
+
+ mService.mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
+ ALLOW_NON_FULL, "getTraceFileDescriptor", null);
+ if (mService.enforceDumpPermissionForPackage(packageName, userId,
+ callingUid, "getTraceFileDescriptor") != Process.INVALID_UID) {
+ synchronized (mLock) {
+ final ApplicationExitInfo info = getExitInfoLocked(packageName, uid, pid);
+ if (info == null) {
+ return null;
+ }
+ final File traceFile = info.getTraceFile();
+ if (traceFile == null) {
+ return null;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // The fd will be closed after being written into Parcel
+ return ParcelFileDescriptor.open(traceFile,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ return null;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+ return null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 16a7c6b4998e..0f9d61fe5f0e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -71,7 +71,6 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.ProcessInfo;
import android.content.res.Resources;
import android.graphics.Point;
import android.net.LocalSocket;
@@ -99,7 +98,6 @@ import android.provider.DeviceConfig;
import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.EventLog;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -138,10 +136,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* Activity manager code dealing with processes.
@@ -512,13 +508,6 @@ public final class ProcessList {
*/
private final int[] mZygoteSigChldMessage = new int[3];
- interface LmkdKillListener {
- /**
- * Called when there is a process kill by lmkd.
- */
- void onLmkdKillOccurred(int pid, int uid);
- }
-
final class IsolatedUidRange {
@VisibleForTesting
public final int mFirstUid;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index e7f66bb484e0..eec75195c2ca 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1621,9 +1621,11 @@ class ProcessRecord implements WindowProcessListener {
// For background ANRs, don't pass the ProcessCpuTracker to
// avoid spending 1/2 second collecting stats to rank lastPids.
StringWriter tracesFileException = new StringWriter();
+ // To hold the start and end offset to the ANR trace file respectively.
+ final long[] offsets = new long[2];
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
(isSilentAnr()) ? null : processCpuTracker, (isSilentAnr()) ? null : lastPids,
- nativePids, tracesFileException);
+ nativePids, tracesFileException, offsets);
if (isMonitorCpuUsage()) {
mService.updateCpuStatsNow();
@@ -1641,6 +1643,10 @@ class ProcessRecord implements WindowProcessListener {
if (tracesFile == null) {
// There is no trace file, so dump (only) the alleged culprit's threads to the log
Process.sendSignal(pid, Process.SIGNAL_QUIT);
+ } else if (offsets[1] > 0) {
+ // We've dumped into the trace file successfully
+ mService.mProcessList.mAppExitInfoTracker.scheduleLogAnrTrace(
+ pid, uid, getPackageList(), tracesFile, offsets[0], offsets[1]);
}
FrameworkStatsLog.write(FrameworkStatsLog.ANR_OCCURRED, uid, processName,
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 44f571a8c7a7..8519b00237aa 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -28,7 +28,6 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
-import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
@@ -45,7 +44,6 @@ import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Arrays;
/**
* System server internal API for gating and reporting compatibility changes.
@@ -58,9 +56,6 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private final ChangeReporter mChangeReporter;
private final CompatConfig mCompatConfig;
- private static int sMinTargetSdk = Build.VERSION_CODES.P;
- private static int sMaxTargetSdk = Build.VERSION_CODES.Q;
-
public PlatformCompat(Context context) {
mContext = context;
mChangeReporter = new ChangeReporter(
@@ -225,12 +220,6 @@ public class PlatformCompat extends IPlatformCompat.Stub {
return mCompatConfig.dumpChanges();
}
- @Override
- public CompatibilityChangeInfo[] listUIChanges() {
- return Arrays.stream(listAllChanges()).filter(
- x -> isShownInUI(x)).toArray(CompatibilityChangeInfo[]::new);
- }
-
/**
* Check whether the change is known to the compat config.
*
@@ -350,17 +339,4 @@ public class PlatformCompat extends IPlatformCompat.Stub {
checkCompatChangeReadPermission();
checkCompatChangeLogPermission();
}
-
- private boolean isShownInUI(CompatibilityChangeInfo change) {
- if (change.getLoggingOnly()) {
- return false;
- }
- if (change.getEnableAfterTargetSdk() != 0) {
- if (change.getEnableAfterTargetSdk() < sMinTargetSdk
- || change.getEnableAfterTargetSdk() > sMaxTargetSdk) {
- return false;
- }
- }
- return true;
- }
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c5c136377a1a..464e48c49473 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3350,6 +3350,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* existing plans; it simply lets the debug package define new plans.
*/
void setSubscriptionPlansOwner(int subId, String packageName) {
+ mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG);
SystemProperties.set(PROP_SUB_PLAN_OWNER + "." + subId, packageName);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5b7e8bf51012..348a4cb77163 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1487,6 +1487,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return e;
}
+ private void onDataLoaderUnrecoverable() {
+ final PackageManagerService packageManagerService = mPm;
+ final String packageName = mPackageName;
+ mHandler.post(() -> {
+ if (packageManagerService.deletePackageX(packageName,
+ PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
+ PackageManager.DELETE_ALL_USERS) != PackageManager.DELETE_SUCCEEDED) {
+ Slog.e(TAG, "Failed to uninstall package with failed dataloader: " + packageName);
+ }
+ });
+ }
+
/**
* If session should be sealed, then it's sealed to prevent further modification.
* If the session can't be sealed then it's destroyed.
@@ -2564,11 +2576,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
IDataLoaderStatusListener listener = new IDataLoaderStatusListener.Stub() {
@Override
public void onStatusChanged(int dataLoaderId, int status) {
- try {
- if (status == IDataLoaderStatusListener.DATA_LOADER_DESTROYED) {
+ switch (status) {
+ case IDataLoaderStatusListener.DATA_LOADER_STOPPED:
+ case IDataLoaderStatusListener.DATA_LOADER_DESTROYED:
return;
- }
+ case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
+ onDataLoaderUnrecoverable();
+ return;
+ }
+ if (mDestroyed || mDataLoaderFinished) {
+ return;
+ }
+
+ try {
IDataLoader dataLoader = dataLoaderManager.getDataLoader(dataLoaderId);
if (dataLoader == null) {
mDataLoaderFinished = true;
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 1292f6c121b4..63048f6b95b3 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -252,8 +252,21 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
* @param permission The permission to check.
*/
void enforcePermission(String permission) {
- mContext.enforceCallingOrSelfPermission(permission,
- String.format("Caller must have the %s permission.", permission));
+ final int status = PermissionChecker.checkCallingOrSelfPermissionForPreflight(mContext,
+ permission);
+ switch (status) {
+ case PermissionChecker.PERMISSION_GRANTED:
+ return;
+ case PermissionChecker.PERMISSION_HARD_DENIED:
+ throw new SecurityException(
+ String.format("Caller must have the %s permission.", permission));
+ case PermissionChecker.PERMISSION_SOFT_DENIED:
+ throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
+ String.format("Caller must have the %s permission.", permission));
+ default:
+ throw new InternalServerError(
+ new RuntimeException("Unexpected perimission check result."));
+ }
}
@Override
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index fd275d83ba56..f1f5bfe4d27e 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1292,7 +1292,7 @@ public class StatsPullAtomService extends SystemService {
int pullSystemUptime(int atomTag, List<StatsEvent> pulledData) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
- .writeLong(SystemClock.elapsedRealtime())
+ .writeLong(SystemClock.uptimeMillis())
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 25585b3ae794..7047a9eb6e5f 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -100,6 +100,7 @@ public class TunerResourceManagerService extends SystemService {
@NonNull IResourcesReclaimListener listener, @NonNull int[] clientId)
throws RemoteException {
enforceTrmAccessPermission("registerClientProfile");
+ enforceTunerAccessPermission("registerClientProfile");
if (profile == null) {
throw new RemoteException("ResourceClientProfile can't be null");
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c7a139176f6c..14ca7cbae4b3 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -27,8 +27,10 @@ import static android.app.ActivityManager.START_RETURN_INTENT_TO_CALLER;
import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WaitResult.LAUNCH_STATE_COLD;
import static android.app.WaitResult.LAUNCH_STATE_HOT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -167,6 +169,8 @@ class ActivityStarter {
// The display to launch the activity onto, barring any strong reason to do otherwise.
private int mPreferredDisplayId;
+ // The windowing mode to apply to the root task, if possible
+ private int mPreferredWindowingMode;
private Task mInTask;
@VisibleForTesting
@@ -538,6 +542,7 @@ class ActivityStarter {
mStartFlags = starter.mStartFlags;
mSourceRecord = starter.mSourceRecord;
mPreferredDisplayId = starter.mPreferredDisplayId;
+ mPreferredWindowingMode = starter.mPreferredWindowingMode;
mInTask = starter.mInTask;
mAddingToTask = starter.mAddingToTask;
@@ -1493,8 +1498,6 @@ class ActivityStarter {
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor, restrictedBgActivity);
- final int preferredWindowingMode = mLaunchParams.mWindowingMode;
-
computeLaunchingTaskFlags();
computeSourceStack();
@@ -1564,6 +1567,14 @@ class ActivityStarter {
if (!mAvoidMoveToFront && mDoResume) {
mTargetStack.moveToFront("reuseOrNewTask");
+ if (mOptions != null) {
+ if (mPreferredWindowingMode != WINDOWING_MODE_UNDEFINED) {
+ mTargetStack.setWindowingMode(mPreferredWindowingMode);
+ }
+ if (mOptions.getTaskAlwaysOnTop()) {
+ mTargetStack.setAlwaysOnTop(true);
+ }
+ }
}
mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
@@ -1627,7 +1638,7 @@ class ActivityStarter {
// Update the recent tasks list immediately when the activity starts
mSupervisor.mRecentTasks.add(mStartActivity.getTask());
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),
- preferredWindowingMode, mPreferredDisplayId, mTargetStack);
+ mPreferredWindowingMode, mPreferredDisplayId, mTargetStack);
return START_SUCCESS;
}
@@ -1680,9 +1691,10 @@ class ActivityStarter {
mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r,
sourceRecord, mOptions, PHASE_BOUNDS, mLaunchParams);
- mPreferredDisplayId =
- mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
- : DEFAULT_DISPLAY;
+ mPreferredDisplayId = mLaunchParams.hasPreferredDisplay()
+ ? mLaunchParams.mPreferredDisplayId
+ : DEFAULT_DISPLAY;
+ mPreferredWindowingMode = mLaunchParams.mWindowingMode;
}
private int isAllowedToStart(ActivityRecord r, boolean newTask, Task targetTask) {
@@ -2006,6 +2018,7 @@ class ActivityStarter {
mStartFlags = 0;
mSourceRecord = null;
mPreferredDisplayId = INVALID_DISPLAY;
+ mPreferredWindowingMode = WINDOWING_MODE_UNDEFINED;
mInTask = null;
mAddingToTask = false;
@@ -2054,9 +2067,10 @@ class ActivityStarter {
// after we located a reusable task (which might be resided in another display).
mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r,
sourceRecord, options, PHASE_DISPLAY, mLaunchParams);
- mPreferredDisplayId =
- mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
- : DEFAULT_DISPLAY;
+ mPreferredDisplayId = mLaunchParams.hasPreferredDisplay()
+ ? mLaunchParams.mPreferredDisplayId
+ : DEFAULT_DISPLAY;
+ mPreferredWindowingMode = mLaunchParams.mWindowingMode;
mLaunchMode = r.launchMode;
@@ -2098,7 +2112,7 @@ class ActivityStarter {
}
if (mOptions != null) {
- if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
+ if (mOptions.getLaunchTaskId() != INVALID_TASK_ID && mOptions.getTaskOverlay()) {
r.setTaskOverlay(true);
if (!mOptions.canTaskOverlayResume()) {
final Task task = mRootWindowContainer.anyTaskForId(
@@ -2291,6 +2305,15 @@ class ActivityStarter {
* if not or an ActivityRecord with the task into which the new activity should be added.
*/
private Task getReusableTask() {
+ // If a target task is specified, try to reuse that one
+ if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) {
+ Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
+ if (launchTask != null) {
+ return launchTask;
+ }
+ return null;
+ }
+
// We may want to try to place the new activity in to an existing task. We always
// do this if the target activity is singleTask or singleInstance; we will also do
// this if NEW_TASK has been requested, and there is not an additional qualifier telling
@@ -2304,12 +2327,7 @@ class ActivityStarter {
// same component, then instead of launching bring that one to the front.
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
- if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
- Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
- if (launchTask != null) {
- return launchTask;
- }
- } else if (putIntoExistingTask) {
+ if (putIntoExistingTask) {
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
// There can be one and only one instance of single instance activity in the
// history, and it is always in its own unique task, so we do a special search.
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index ee36db9d94ee..a4bdfb351c1f 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
@@ -87,7 +88,8 @@ class InsetsStateController {
final InsetsSourceProvider provider = target.getControllableInsetProvider();
final @InternalInsetsType int type = provider != null
? provider.getSource().getType() : ITYPE_INVALID;
- return getInsetsForTypeAndWindowingMode(type, target.getWindowingMode());
+ return getInsetsForTypeAndWindowingMode(type, target.getWindowingMode(),
+ target.isAlwaysOnTop());
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -95,7 +97,9 @@ class InsetsStateController {
final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
final @WindowingMode int windowingMode = token != null
? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
- return getInsetsForTypeAndWindowingMode(type, windowingMode);
+ final boolean alwaysOnTop = token != null
+ ? token.isAlwaysOnTop() : false;
+ return getInsetsForTypeAndWindowingMode(type, windowingMode, alwaysOnTop);
}
private static @InternalInsetsType int getInsetsTypeForWindowType(int type) {
@@ -113,7 +117,7 @@ class InsetsStateController {
/** @see #getInsetsForDispatch */
private InsetsState getInsetsForTypeAndWindowingMode(@InternalInsetsType int type,
- @WindowingMode int windowingMode) {
+ @WindowingMode int windowingMode, boolean isAlwaysOnTop) {
InsetsState state = mState;
if (type != ITYPE_INVALID) {
@@ -147,7 +151,8 @@ class InsetsStateController {
}
}
- if (WindowConfiguration.isFloating(windowingMode)) {
+ if (WindowConfiguration.isFloating(windowingMode)
+ || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
state = new InsetsState(state);
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_NAVIGATION_BAR);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 8f09f3faae29..cc52cb445a1a 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -231,7 +231,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
}
}
- void unregisterTaskOrganizer(ITaskOrganizer organizer) {
+ @Override
+ public void unregisterTaskOrganizer(ITaskOrganizer organizer) {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
state.unlinkDeath();
if (mTaskOrganizersForWindowingMode.get(state.mWindowingMode) == state) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index eb186786db1a..83fff28290ea 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -186,6 +186,7 @@ import android.os.SystemService;
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -1167,7 +1168,9 @@ public class WindowManagerService extends IWindowManager.Stub
mAnimator = new WindowAnimator(this);
mRoot = new RootWindowContainer(this);
- mUseBLAST = true;
+ mUseBLAST = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
+ WM_USE_BLAST_ADAPTER_FLAG, false);
mWindowPlacerLocked = new WindowSurfacePlacer(this);
mTaskSnapshotController = new TaskSnapshotController(this);
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 29b2cd650565..5f0c8fe3f1f7 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -598,6 +598,9 @@ private:
// IFS callbacks.
void onPendingReads(dataloader::PendingReads pendingReads) final {
std::lock_guard lock{mOutFdLock};
+ if (mOutFd < 0) {
+ return;
+ }
CHECK(mIfs);
for (auto&& pendingRead : pendingReads) {
const android::dataloader::FileId& fileId = pendingRead.id;
@@ -611,13 +614,9 @@ private:
android::incfs::toString(fileId).c_str());
continue;
}
- if (mRequestedFiles.insert(fileIdx).second) {
- if (!sendRequest(mOutFd, PREFETCH, fileIdx, blockIdx)) {
- ALOGE("Failed to request prefetch for fileid=%s. Ignore.",
- android::incfs::toString(fileId).c_str());
- mRequestedFiles.erase(fileIdx);
- mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
- }
+ if (mRequestedFiles.insert(fileIdx).second &&
+ !sendRequest(mOutFd, PREFETCH, fileIdx, blockIdx)) {
+ mRequestedFiles.erase(fileIdx);
}
sendRequest(mOutFd, BLOCK_MISSING, fileIdx, blockIdx);
}
@@ -634,7 +633,7 @@ private:
}
if (res < 0) {
ALOGE("Failed to poll. Abort.");
- mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
+ mStatusListener->reportStatus(DATA_LOADER_UNRECOVERABLE);
break;
}
if (res == mEventFd) {
@@ -644,7 +643,7 @@ private:
}
if (!readChunk(inout, data)) {
ALOGE("Failed to read a message. Abort.");
- mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
+ mStatusListener->reportStatus(DATA_LOADER_UNRECOVERABLE);
break;
}
auto remainingData = std::span(data);
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 76eb3571b907..ed85b931c08e 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1252,14 +1252,6 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange
}
switch (newStatus) {
- case IDataLoaderStatusListener::DATA_LOADER_NO_CONNECTION: {
- // TODO(b/150411019): handle data loader connection loss
- break;
- }
- case IDataLoaderStatusListener::DATA_LOADER_CONNECTION_OK: {
- // TODO(b/150411019): handle data loader connection loss
- break;
- }
case IDataLoaderStatusListener::DATA_LOADER_CREATED: {
if (startRequested) {
incrementalService.startDataLoader(mountId);
@@ -1281,6 +1273,10 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange
case IDataLoaderStatusListener::DATA_LOADER_IMAGE_NOT_READY: {
break;
}
+ case IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE: {
+ // Nothing for now. Rely on externalListener to handle this.
+ break;
+ }
default: {
LOG(WARNING) << "Unknown data loader status: " << newStatus
<< " for mount: " << mountId;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index efe8119a62a5..23381ffd4eaa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -46,6 +46,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
import android.os.Debug;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -55,6 +56,7 @@ import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Pair;
+import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.appop.AppOpsService;
@@ -71,10 +73,17 @@ import org.junit.runners.model.Statement;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Random;
+import java.util.zip.GZIPInputStream;
/**
* Test class for {@link android.app.ApplicationExitInfo}.
@@ -119,6 +128,8 @@ public class ApplicationExitInfoTest {
setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mAppExitInfoSourceLmkd",
spy(mAppExitInfoTracker.new AppExitInfoExternalSource("lmkd",
ApplicationExitInfo.REASON_LOW_MEMORY)));
+ setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mAppTraceRetriever",
+ spy(mAppExitInfoTracker.new AppTraceRetriever()));
setFieldValue(ProcessList.class, mProcessList, "mAppExitInfoTracker", mAppExitInfoTracker);
mInjector = new TestInjector(mContext);
mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
@@ -169,6 +180,11 @@ public class ApplicationExitInfoTest {
public void testApplicationExitInfo() throws Exception {
mAppExitInfoTracker.clearProcessExitInfo(true);
mAppExitInfoTracker.mAppExitInfoLoaded = true;
+ mAppExitInfoTracker.mProcExitStoreDir = new File(mContext.getFilesDir(),
+ AppExitInfoTracker.APP_EXIT_STORE_DIR);
+ assertTrue(FileUtils.createDir(mAppExitInfoTracker.mProcExitStoreDir));
+ mAppExitInfoTracker.mProcExitInfoFile = new File(mAppExitInfoTracker.mProcExitStoreDir,
+ AppExitInfoTracker.APP_EXIT_INFO_FILE);
// Test application calls System.exit()
doNothing().when(mAppExitInfoTracker).schedulePersistProcessExitInfo(anyBoolean());
@@ -188,6 +204,10 @@ public class ApplicationExitInfoTest {
final long app1Rss3 = 45680;
final String app1ProcessName = "com.android.test.stub1:process";
final String app1PackageName = "com.android.test.stub1";
+ final byte[] app1Cookie1 = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+ (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08};
+ final byte[] app1Cookie2 = {(byte) 0x08, (byte) 0x07, (byte) 0x06, (byte) 0x05,
+ (byte) 0x04, (byte) 0x03, (byte) 0x02, (byte) 0x01};
final long now1 = System.currentTimeMillis();
ProcessRecord app = makeProcessRecord(
@@ -204,6 +224,9 @@ public class ApplicationExitInfoTest {
// Case 1: basic System.exit() test
int exitCode = 5;
+ mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid1, app1Cookie1);
+ assertTrue(ArrayUtils.equals(mAppExitInfoTracker.getProcessStateSummary(app1Uid,
+ app1Pid1), app1Cookie1, app1Cookie1.length));
doReturn(new Pair<Long, Object>(now1, Integer.valueOf(makeExitStatus(exitCode))))
.when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
.remove(anyInt(), anyInt());
@@ -235,6 +258,10 @@ public class ApplicationExitInfoTest {
IMPORTANCE_CACHED, // importance
null); // description
+ assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie1,
+ app1Cookie1.length));
+ assertEquals(info.getTraceInputStream(), null);
+
// Case 2: create another app1 process record with a different pid
sleep(1);
final long now2 = System.currentTimeMillis();
@@ -250,6 +277,12 @@ public class ApplicationExitInfoTest {
app1ProcessName, // processName
app1PackageName); // packageName
exitCode = 6;
+
+ mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid2, app1Cookie1);
+ // Override with a different cookie
+ mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid2, app1Cookie2);
+ assertTrue(ArrayUtils.equals(mAppExitInfoTracker.getProcessStateSummary(app1Uid,
+ app1Pid2), app1Cookie2, app1Cookie2.length));
doReturn(new Pair<Long, Object>(now2, Integer.valueOf(makeExitStatus(exitCode))))
.when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
.remove(anyInt(), anyInt());
@@ -280,6 +313,12 @@ public class ApplicationExitInfoTest {
IMPORTANCE_SERVICE, // importance
null); // description
+ assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie2,
+ app1Cookie2.length));
+ info = list.get(1);
+ assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie1,
+ app1Cookie1.length));
+
// Case 3: Create an instance of app1 with different user, and died because of SIGKILL
sleep(1);
final long now3 = System.currentTimeMillis();
@@ -702,9 +741,19 @@ public class ApplicationExitInfoTest {
app1PackageName); // packageName
mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUid2User2, app1UidUser2);
+
+ // Pretent it gets an ANR trace too (although the reason here should be REASON_ANR)
+ final File traceFile = new File(mContext.getFilesDir(), "anr_original.txt");
+ final int traceSize = 10240;
+ final int traceStart = 1024;
+ final int traceEnd = 8192;
+ createRandomFile(traceFile, traceSize);
+ assertEquals(traceSize, traceFile.length());
+ mAppExitInfoTracker.handleLogAnrTrace(app.pid, app.uid, app.getPackageList(),
+ traceFile, traceStart, traceEnd);
+
noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, app1Description2);
-
updateExitInfo(app);
list.clear();
mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1Pid2User2, 1, list);
@@ -729,6 +778,10 @@ public class ApplicationExitInfoTest {
IMPORTANCE_CACHED, // importance
app1Description2); // description
+ // Verify if the traceFile get copied into the records correctly.
+ verifyTraceFile(traceFile, traceStart, info.getTraceFile(), 0, traceEnd - traceStart);
+ traceFile.delete();
+ info.getTraceFile().delete();
// Case 9: User2 gets removed
sleep(1);
@@ -801,8 +854,6 @@ public class ApplicationExitInfoTest {
mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, original);
assertTrue(original.size() > 0);
- mAppExitInfoTracker.mProcExitInfoFile = new File(mContext.getFilesDir(),
- AppExitInfoTracker.APP_EXIT_INFO_FILE);
mAppExitInfoTracker.persistProcessExitInfo();
assertTrue(mAppExitInfoTracker.mProcExitInfoFile.exists());
@@ -836,6 +887,37 @@ public class ApplicationExitInfoTest {
}
}
+ private static void createRandomFile(File file, int size) throws IOException {
+ try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
+ Random random = new Random();
+ byte[] buf = random.ints('a', 'z').limit(size).collect(
+ StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
+ .toString().getBytes();
+ out.write(buf);
+ }
+ }
+
+ private static void verifyTraceFile(File originFile, int originStart, File traceFile,
+ int traceStart, int length) throws IOException {
+ assertTrue(originFile.exists());
+ assertTrue(traceFile.exists());
+ assertTrue(originStart < originFile.length());
+ try (GZIPInputStream traceIn = new GZIPInputStream(new FileInputStream(traceFile));
+ BufferedInputStream originIn = new BufferedInputStream(
+ new FileInputStream(originFile))) {
+ assertEquals(traceStart, traceIn.skip(traceStart));
+ assertEquals(originStart, originIn.skip(originStart));
+ byte[] buf1 = new byte[8192];
+ byte[] buf2 = new byte[8192];
+ while (length > 0) {
+ int len = traceIn.read(buf1, 0, Math.min(buf1.length, length));
+ assertEquals(len, originIn.read(buf2, 0, len));
+ assertTrue(ArrayUtils.equals(buf1, buf2, len));
+ length -= len;
+ }
+ }
+ }
+
private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
int connectionGroup, int procState, long pss, long rss,
String processName, String packageName) {
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 72d8525671fc..53b90f2b01db 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -30,12 +30,10 @@ import static org.testng.Assert.assertThrows;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.os.Build;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.compat.AndroidBuildClassifier;
-import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.LocalServices;
import org.junit.Before;
@@ -80,47 +78,6 @@ public class PlatformCompatTest {
}
@Test
- public void testListAllChanges() {
- mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
- .addEnabledChangeWithId(1L)
- .addDisabledChangeWithIdAndName(2L, "change2")
- .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
- .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
- .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
- .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
- .addLoggingOnlyChangeWithId(7L)
- .build();
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
- assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
- new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
- new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
- new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, false, false,
- "description"),
- new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
- new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""),
- new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, false, false, ""),
- new CompatibilityChangeInfo(7L, "", -1, false, true, ""));
- }
-
- public void testListUIChanges() {
- mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
- .addEnabledChangeWithId(1L)
- .addDisabledChangeWithIdAndName(2L, "change2")
- .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
- .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
- .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
- .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
- .addLoggingOnlyChangeWithId(7L)
- .build();
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
- assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
- new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
- new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
- new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
- new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""));
- }
-
- @Test
public void testRegisterListenerToSameIdThrows() throws Exception {
// Registering a listener to change 1 is successful.
mPlatformCompat.registerListener(1, mListener1);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 05cd26dfd737..f070bff6f906 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -1208,80 +1208,6 @@ public class AppStandbyControllerTests {
STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
}
- public void testAppUpdateOnRestrictedBucketStatus() {
- // Updates shouldn't change bucket if the app timed out.
- // Way past all timeouts. App times out into RESTRICTED bucket.
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
- mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
-
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
-
- // Updates shouldn't change bucket if the app was forced by the system for a non-buggy
- // reason.
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_SYSTEM
- | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
-
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
-
- // Updates should change bucket if the app was forced by the system for a buggy reason.
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
-
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertNotEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
-
- // Updates shouldn't change bucket if the app was forced by the system for more than just
- // a buggy reason.
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE
- | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
-
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
- getStandbyBucketReason(PACKAGE_1));
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
-
- // Updates shouldn't change bucket if the app was forced by the user.
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_USER);
-
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- }
-
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index c449049164e4..2acb6476453c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -51,6 +51,7 @@ public class ActivityOptionsTest {
opts.setLaunchTaskId(Integer.MAX_VALUE);
opts.setLockTaskEnabled(true);
opts.setRotationAnimationHint(ROTATION_ANIMATION_ROTATE);
+ opts.setTaskAlwaysOnTop(true);
opts.setTaskOverlay(true, true);
opts.setSplitScreenCreateMode(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
Bundle optsBundle = opts.toBundle();
@@ -67,6 +68,7 @@ public class ActivityOptionsTest {
assertEquals(Integer.MAX_VALUE, restoredOpts.getLaunchTaskId());
assertTrue(restoredOpts.getLockTaskMode());
assertEquals(ROTATION_ANIMATION_ROTATE, restoredOpts.getRotationAnimationHint());
+ assertTrue(restoredOpts.getTaskAlwaysOnTop());
assertTrue(restoredOpts.getTaskOverlay());
assertTrue(restoredOpts.canTaskOverlayResume());
assertEquals(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index a380ece61935..bfb126f7052c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -131,6 +132,21 @@ public class InsetsStateControllerTest extends WindowTestsBase {
}
@Test
+ public void testStripForDispatch_multiwindow_alwaysOnTop() {
+ final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+ getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+ app.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ app.setAlwaysOnTop(true);
+
+ assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_STATUS_BAR));
+ assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_NAVIGATION_BAR));
+ }
+
+ @Test
public void testImeForDispatch() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index a49d3d539a7a..11568f16bb6a 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -878,14 +878,10 @@ package android.telephony {
field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
- field public static final String ACTION_SERVICE_PROVIDERS_UPDATED = "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
- field public static final int CARD_POWER_DOWN = 0; // 0x0
- field public static final int CARD_POWER_UP = 1; // 0x1
- field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -896,7 +892,6 @@ package android.telephony {
field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
field @Deprecated public static final String EXTRA_APN_TYPE = "apnType";
field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
- field public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
field public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; // 0x4
@@ -909,16 +904,12 @@ package android.telephony {
field public static final String EXTRA_PCO_VALUE = "pcoValue";
field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
- field public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
- field public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
- field public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
field public static final String EXTRA_SIM_COMBINATION_NAMES = "android.telephony.extra.SIM_COMBINATION_NAMES";
field public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; // 0x1
field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; // 0x0
field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
- field public static final String EXTRA_SPN = "android.telephony.extra.SPN";
field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fbd69da84936..d6f5bb2577ab 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1036,7 +1036,9 @@ public class TelephonyManager {
"android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
/**
- * Broadcast intent that indicates multi-SIM configuration is changed. For example, it changed
+ * Broadcast action to be received by Broadcast receivers.
+ *
+ * Indicates multi-SIM configuration is changed. For example, it changed
* from single SIM capable to dual-SIM capable (DSDS or DSDA) or triple-SIM mode.
*
* It doesn't indicate how many subscriptions are actually active, or which states SIMs are,
@@ -1279,7 +1281,6 @@ public class TelephonyManager {
* <p>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_SERVICE_PROVIDERS_UPDATED =
"android.telephony.action.SERVICE_PROVIDERS_UPDATED";
@@ -1289,7 +1290,6 @@ public class TelephonyManager {
* whether the PLMN should be shown.
* @hide
*/
- @SystemApi
public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
/**
@@ -1297,7 +1297,6 @@ public class TelephonyManager {
* the operator name of the registered network.
* @hide
*/
- @SystemApi
public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
/**
@@ -1305,7 +1304,6 @@ public class TelephonyManager {
* whether the PLMN should be shown.
* @hide
*/
- @SystemApi
public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
/**
@@ -1313,7 +1311,6 @@ public class TelephonyManager {
* the service provider name.
* @hide
*/
- @SystemApi
public static final String EXTRA_SPN = "android.telephony.extra.SPN";
/**
@@ -1321,7 +1318,6 @@ public class TelephonyManager {
* the service provider name for data service.
* @hide
*/
- @SystemApi
public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
/**
@@ -9747,14 +9743,12 @@ public class TelephonyManager {
* Powers down the SIM. SIM must be up prior.
* @hide
*/
- @SystemApi
public static final int CARD_POWER_DOWN = 0;
/**
* Powers up the SIM normally. SIM must be down prior.
* @hide
*/
- @SystemApi
public static final int CARD_POWER_UP = 1;
/**
@@ -9772,7 +9766,6 @@ public class TelephonyManager {
* is NOT persistent across boots. On reboot, SIM will power up normally.
* @hide
*/
- @SystemApi
public static final int CARD_POWER_UP_PASS_THROUGH = 2;
/**
diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp
index e44d46088c43..46d680fc4511 100644
--- a/tests/net/common/Android.bp
+++ b/tests/net/common/Android.bp
@@ -20,6 +20,7 @@ java_library {
name: "FrameworksNetCommonTests",
srcs: ["java/**/*.java", "java/**/*.kt"],
static_libs: [
+ "androidx.core_core",
"androidx.test.rules",
"junit",
"mockito-target-minus-junit4",
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 48b65e592f39..2b5720a47eb6 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -29,12 +29,19 @@ import static org.junit.Assert.fail;
import android.net.LinkProperties.ProvisioningChange;
import android.net.util.LinkPropertiesUtils.CompareResult;
+import android.os.Build;
import android.system.OsConstants;
import android.util.ArraySet;
+import androidx.core.os.BuildCompat;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,6 +57,9 @@ import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class LinkPropertiesTest {
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
private static final InetAddress ADDRV4 = address("75.208.6.1");
private static final InetAddress ADDRV6 = address("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
private static final InetAddress DNS1 = address("75.208.7.1");
@@ -76,13 +86,23 @@ public class LinkPropertiesTest {
private static final LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
private static final LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
private static final Uri CAPPORT_API_URL = Uri.parse("https://test.example.com/capportapi");
- private static final CaptivePortalData CAPPORT_DATA = new CaptivePortalData.Builder()
- .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build();
+
+ // CaptivePortalData cannot be in a constant as it does not exist on Q.
+ // The test runner also crashes when scanning for tests if it is a return type.
+ private static Object getCaptivePortalData() {
+ return new CaptivePortalData.Builder()
+ .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build();
+ }
private static InetAddress address(String addrString) {
return InetAddresses.parseNumericAddress(addrString);
}
+ private static boolean isAtLeastR() {
+ // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R)
+ return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR();
+ }
+
private void checkEmpty(final LinkProperties lp) {
assertEquals(0, lp.getAllInterfaceNames().size());
assertEquals(0, lp.getAllAddresses().size());
@@ -98,14 +118,17 @@ public class LinkPropertiesTest {
assertNull(lp.getHttpProxy());
assertNull(lp.getTcpBufferSizes());
assertNull(lp.getNat64Prefix());
- assertNull(lp.getDhcpServerAddress());
assertFalse(lp.isProvisioned());
assertFalse(lp.isIpv4Provisioned());
assertFalse(lp.isIpv6Provisioned());
assertFalse(lp.isPrivateDnsActive());
- assertFalse(lp.isWakeOnLanSupported());
- assertNull(lp.getCaptivePortalApiUrl());
- assertNull(lp.getCaptivePortalData());
+
+ if (isAtLeastR()) {
+ assertNull(lp.getDhcpServerAddress());
+ assertFalse(lp.isWakeOnLanSupported());
+ assertNull(lp.getCaptivePortalApiUrl());
+ assertNull(lp.getCaptivePortalData());
+ }
}
private LinkProperties makeTestObject() {
@@ -127,10 +150,12 @@ public class LinkPropertiesTest {
lp.setMtu(MTU);
lp.setTcpBufferSizes(TCP_BUFFER_SIZES);
lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
- lp.setDhcpServerAddress(DHCPSERVER);
- lp.setWakeOnLanSupported(true);
- lp.setCaptivePortalApiUrl(CAPPORT_API_URL);
- lp.setCaptivePortalData(CAPPORT_DATA);
+ if (isAtLeastR()) {
+ lp.setDhcpServerAddress(DHCPSERVER);
+ lp.setWakeOnLanSupported(true);
+ lp.setCaptivePortalApiUrl(CAPPORT_API_URL);
+ lp.setCaptivePortalData((CaptivePortalData) getCaptivePortalData());
+ }
return lp;
}
@@ -169,14 +194,19 @@ public class LinkPropertiesTest {
assertTrue(source.isIdenticalTcpBufferSizes(target));
assertTrue(target.isIdenticalTcpBufferSizes(source));
- assertTrue(source.isIdenticalWakeOnLan(target));
- assertTrue(target.isIdenticalWakeOnLan(source));
+ if (isAtLeastR()) {
+ assertTrue(source.isIdenticalDhcpServerAddress(target));
+ assertTrue(source.isIdenticalDhcpServerAddress(source));
- assertTrue(source.isIdenticalCaptivePortalApiUrl(target));
- assertTrue(target.isIdenticalCaptivePortalApiUrl(source));
+ assertTrue(source.isIdenticalWakeOnLan(target));
+ assertTrue(target.isIdenticalWakeOnLan(source));
- assertTrue(source.isIdenticalCaptivePortalData(target));
- assertTrue(target.isIdenticalCaptivePortalData(source));
+ assertTrue(source.isIdenticalCaptivePortalApiUrl(target));
+ assertTrue(target.isIdenticalCaptivePortalApiUrl(source));
+
+ assertTrue(source.isIdenticalCaptivePortalData(target));
+ assertTrue(target.isIdenticalCaptivePortalData(source));
+ }
// Check result of equals().
assertTrue(source.equals(target));
@@ -943,8 +973,7 @@ public class LinkPropertiesTest {
assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
}
- @Test
- public void testLinkPropertiesParcelable() throws Exception {
+ private static LinkProperties makeLinkPropertiesForParceling() {
LinkProperties source = new LinkProperties();
source.setInterfaceName(NAME);
@@ -978,17 +1007,29 @@ public class LinkPropertiesTest {
source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
- source.setWakeOnLanSupported(true);
- source.setCaptivePortalApiUrl(CAPPORT_API_URL);
- source.setCaptivePortalData(CAPPORT_DATA);
-
- source.setDhcpServerAddress((Inet4Address) GATEWAY1);
-
final LinkProperties stacked = new LinkProperties();
stacked.setInterfaceName("test-stacked");
source.addStackedLink(stacked);
- assertParcelSane(source.makeSensitiveFieldsParcelingCopy(), 18 /* fieldCount */);
+ return source;
+ }
+
+ @Test @IgnoreAfter(Build.VERSION_CODES.Q)
+ public void testLinkPropertiesParcelable_Q() throws Exception {
+ final LinkProperties source = makeLinkPropertiesForParceling();
+ assertParcelSane(source, 14 /* fieldCount */);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testLinkPropertiesParcelable() throws Exception {
+ final LinkProperties source = makeLinkPropertiesForParceling();
+
+ source.setWakeOnLanSupported(true);
+ source.setCaptivePortalApiUrl(CAPPORT_API_URL);
+ source.setCaptivePortalData((CaptivePortalData) getCaptivePortalData());
+ source.setDhcpServerAddress((Inet4Address) GATEWAY1);
+ assertParcelSane(new LinkProperties(source, true /* parcelSensitiveFields */),
+ 18 /* fieldCount */);
// Verify that without using a sensitiveFieldsParcelingCopy, sensitive fields are cleared.
final LinkProperties sanitized = new LinkProperties(source);
@@ -997,7 +1038,8 @@ public class LinkPropertiesTest {
assertEquals(sanitized, parcelingRoundTrip(source));
}
- @Test
+ // Parceling of the scope was broken until Q-QPR2
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testLinkLocalDnsServerParceling() throws Exception {
final String strAddress = "fe80::1%lo";
final LinkProperties lp = new LinkProperties();
@@ -1120,7 +1162,7 @@ public class LinkPropertiesTest {
assertFalse(lp.isPrivateDnsActive());
}
- @Test
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testDhcpServerAddress() {
final LinkProperties lp = makeTestObject();
assertEquals(DHCPSERVER, lp.getDhcpServerAddress());
@@ -1129,7 +1171,7 @@ public class LinkPropertiesTest {
assertNull(lp.getDhcpServerAddress());
}
- @Test
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testWakeOnLanSupported() {
final LinkProperties lp = makeTestObject();
assertTrue(lp.isWakeOnLanSupported());
@@ -1138,7 +1180,7 @@ public class LinkPropertiesTest {
assertFalse(lp.isWakeOnLanSupported());
}
- @Test
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testCaptivePortalApiUrl() {
final LinkProperties lp = makeTestObject();
assertEquals(CAPPORT_API_URL, lp.getCaptivePortalApiUrl());
@@ -1147,10 +1189,10 @@ public class LinkPropertiesTest {
assertNull(lp.getCaptivePortalApiUrl());
}
- @Test
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testCaptivePortalData() {
final LinkProperties lp = makeTestObject();
- assertEquals(CAPPORT_DATA, lp.getCaptivePortalData());
+ assertEquals(getCaptivePortalData(), lp.getCaptivePortalData());
lp.clear();
assertNull(lp.getCaptivePortalData());
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index efea91ab91f0..9fe1883010b7 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -48,9 +48,11 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.os.Build;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
+import androidx.core.os.BuildCompat;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
@@ -64,6 +66,13 @@ public class NetworkCapabilitiesTest {
private static final String TEST_SSID = "TEST_SSID";
private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
+ private boolean isAtLeastR() {
+ // BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R.
+ // Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after
+ // releasing Android R.
+ return BuildCompat.isAtLeastR() || Build.VERSION.SDK_INT > Build.VERSION_CODES.Q;
+ }
+
@Test
public void testMaybeMarkCapabilitiesRestricted() {
// verify EIMS is restricted
@@ -269,25 +278,36 @@ public class NetworkCapabilitiesTest {
.setUids(uids)
.addCapability(NET_CAPABILITY_EIMS)
.addCapability(NET_CAPABILITY_NOT_METERED);
- netCap.setOwnerUid(123);
+ if (isAtLeastR()) {
+ netCap.setOwnerUid(123);
+ }
assertParcelingIsLossless(netCap);
netCap.setSSID(TEST_SSID);
- assertParcelSane(netCap, 15);
+ testParcelSane(netCap);
}
@Test
public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() {
final NetworkCapabilities netCap = new NetworkCapabilities()
.addCapability(NET_CAPABILITY_INTERNET)
- .setRequestorUid(9304)
- .setRequestorPackageName("com.android.test")
.addCapability(NET_CAPABILITY_EIMS)
.addCapability(NET_CAPABILITY_NOT_METERED);
+ if (isAtLeastR()) {
+ netCap.setRequestorPackageName("com.android.test");
+ netCap.setRequestorUid(9304);
+ }
assertParcelingIsLossless(netCap);
netCap.setSSID(TEST_SSID);
- assertParcelSane(netCap, 15);
+ testParcelSane(netCap);
}
+ private void testParcelSane(NetworkCapabilities cap) {
+ if (isAtLeastR()) {
+ assertParcelSane(cap, 15);
+ } else {
+ assertParcelSane(cap, 11);
+ }
+ }
@Test
public void testOemPaid() {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c8f937512c9d..f7ad09ca36a6 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -175,6 +175,7 @@ import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.ResolverParamsParcel;
import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
import android.net.UidRange;
import android.net.Uri;
@@ -427,7 +428,6 @@ public class ConnectivityServiceTest {
public Object getSystemService(String name) {
if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager;
- if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
if (Context.USER_SERVICE.equals(name)) return mUserManager;
if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
@@ -6064,6 +6064,7 @@ public class ConnectivityServiceTest {
verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
TYPE_MOBILE);
}
+ reset(mMockNetd);
// Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
// linkproperties are cleaned up.
@@ -6115,7 +6116,6 @@ public class ConnectivityServiceTest {
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
-
// Clat iface comes up. Expect stacked link to be added.
clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
@@ -6701,17 +6701,45 @@ public class ConnectivityServiceTest {
}
}
+ private void assertRouteInfoParcelMatches(RouteInfo route, RouteInfoParcel parcel) {
+ assertEquals(route.getDestination().toString(), parcel.destination);
+ assertEquals(route.getInterface(), parcel.ifName);
+ assertEquals(route.getMtu(), parcel.mtu);
+
+ switch (route.getType()) {
+ case RouteInfo.RTN_UNICAST:
+ if (route.hasGateway()) {
+ assertEquals(route.getGateway().getHostAddress(), parcel.nextHop);
+ } else {
+ assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop);
+ }
+ break;
+ case RouteInfo.RTN_UNREACHABLE:
+ assertEquals(INetd.NEXTHOP_UNREACHABLE, parcel.nextHop);
+ break;
+ case RouteInfo.RTN_THROW:
+ assertEquals(INetd.NEXTHOP_THROW, parcel.nextHop);
+ break;
+ default:
+ assertEquals(INetd.NEXTHOP_NONE, parcel.nextHop);
+ break;
+ }
+ }
+
private void assertRoutesAdded(int netId, RouteInfo... routes) throws Exception {
- InOrder inOrder = inOrder(mNetworkManagementService);
+ ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+ verify(mMockNetd, times(routes.length)).networkAddRouteParcel(eq(netId), captor.capture());
for (int i = 0; i < routes.length; i++) {
- inOrder.verify(mNetworkManagementService).addRoute(eq(netId), eq(routes[i]));
+ assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i));
}
}
private void assertRoutesRemoved(int netId, RouteInfo... routes) throws Exception {
- InOrder inOrder = inOrder(mNetworkManagementService);
+ ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+ verify(mMockNetd, times(routes.length)).networkRemoveRouteParcel(eq(netId),
+ captor.capture());
for (int i = 0; i < routes.length; i++) {
- inOrder.verify(mNetworkManagementService).removeRoute(eq(netId), eq(routes[i]));
+ assertRouteInfoParcelMatches(routes[i], captor.getAllValues().get(i));
}
}
@@ -6977,4 +7005,60 @@ public class ConnectivityServiceTest {
verify(mConnectivityDiagnosticsCallback)
.onNetworkConnectivityReported(eq(n), eq(noConnectivity));
}
+
+ @Test
+ public void testRouteAddDeleteUpdate() throws Exception {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, networkCallback);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ reset(mMockNetd);
+ mCellNetworkAgent.connect(false);
+ networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
+ final int netId = mCellNetworkAgent.getNetwork().netId;
+
+ final String iface = "rmnet_data0";
+ final InetAddress gateway = InetAddress.getByName("fe80::5678");
+ RouteInfo direct = RouteInfo.makeHostRoute(gateway, iface);
+ RouteInfo rio1 = new RouteInfo(new IpPrefix("2001:db8:1::/48"), gateway, iface);
+ RouteInfo rio2 = new RouteInfo(new IpPrefix("2001:db8:2::/48"), gateway, iface);
+ RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, gateway, iface);
+ RouteInfo defaultWithMtu = new RouteInfo(null, gateway, iface, RouteInfo.RTN_UNICAST,
+ 1280 /* mtu */);
+
+ // Send LinkProperties and check that we ask netd to add routes.
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(iface);
+ lp.addRoute(direct);
+ lp.addRoute(rio1);
+ lp.addRoute(defaultRoute);
+ mCellNetworkAgent.sendLinkProperties(lp);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent, x -> x.getRoutes().size() == 3);
+
+ assertRoutesAdded(netId, direct, rio1, defaultRoute);
+ reset(mMockNetd);
+
+ // Send updated LinkProperties and check that we ask netd to add, remove, update routes.
+ assertTrue(lp.getRoutes().contains(defaultRoute));
+ lp.removeRoute(rio1);
+ lp.addRoute(rio2);
+ lp.addRoute(defaultWithMtu);
+ // Ensure adding the same route with a different MTU replaces the previous route.
+ assertFalse(lp.getRoutes().contains(defaultRoute));
+ assertTrue(lp.getRoutes().contains(defaultWithMtu));
+
+ mCellNetworkAgent.sendLinkProperties(lp);
+ networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
+ x -> x.getRoutes().contains(rio2));
+
+ assertRoutesRemoved(netId, rio1);
+ assertRoutesAdded(netId, rio2);
+
+ ArgumentCaptor<RouteInfoParcel> captor = ArgumentCaptor.forClass(RouteInfoParcel.class);
+ verify(mMockNetd).networkUpdateRouteParcel(eq(netId), captor.capture());
+ assertRouteInfoParcelMatches(defaultWithMtu, captor.getValue());
+
+
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
}
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 70c274128266..fbafa07fbce7 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -46,6 +46,7 @@ filegroup {
// TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
// framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
// to a separate package.
+ "java/android/net/wifi/SoftApConfToXmlMigrationUtil.java",
"java/android/net/wifi/WifiNetworkScoreCache.java",
"java/android/net/wifi/WifiMigration.java",
"java/android/net/wifi/nl80211/*.java",
diff --git a/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java
new file mode 100755
index 000000000000..c5472ce34478
--- /dev/null
+++ b/wifi/java/android/net/wifi/SoftApConfToXmlMigrationUtil.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static android.os.Environment.getDataMiscDirectory;
+
+import android.annotation.Nullable;
+import android.net.MacAddress;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Utility class to convert the legacy softap.conf file format to the new XML format.
+ * Note:
+ * <li>This should be modified by the OEM if they want to migrate configuration for existing
+ * devices for new softap features supported by AOSP in Android 11.
+ * For ex: client allowlist/blocklist feature was already supported by some OEM's before Android 10
+ * while AOSP only supported it in Android 11. </li>
+ * <li>Most of this class was copied over from WifiApConfigStore class in Android 10 and
+ * SoftApStoreData class in Android 11</li>
+ * @hide
+ */
+public final class SoftApConfToXmlMigrationUtil {
+ private static final String TAG = "SoftApConfToXmlMigrationUtil";
+
+ /**
+ * Directory to read the wifi config store files from under.
+ */
+ private static final String LEGACY_WIFI_STORE_DIRECTORY_NAME = "wifi";
+ /**
+ * The legacy Softap config file which contained key/value pairs.
+ */
+ private static final String LEGACY_AP_CONFIG_FILE = "softap.conf";
+
+ /**
+ * Pre-apex wifi shared folder.
+ */
+ private static File getLegacyWifiSharedDirectory() {
+ return new File(getDataMiscDirectory(), LEGACY_WIFI_STORE_DIRECTORY_NAME);
+ }
+
+ /* @hide constants copied from WifiConfiguration */
+ /**
+ * 2GHz band.
+ */
+ private static final int WIFICONFIG_AP_BAND_2GHZ = 0;
+ /**
+ * 5GHz band.
+ */
+ private static final int WIFICONFIG_AP_BAND_5GHZ = 1;
+ /**
+ * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability,
+ * operating country code and current radio conditions.
+ */
+ private static final int WIFICONFIG_AP_BAND_ANY = -1;
+ /**
+ * Convert band from WifiConfiguration into SoftApConfiguration
+ *
+ * @param wifiConfigBand band encoded as WIFICONFIG_AP_BAND_xxxx
+ * @return band as encoded as SoftApConfiguration.BAND_xxx
+ */
+ @VisibleForTesting
+ public static int convertWifiConfigBandToSoftApConfigBand(int wifiConfigBand) {
+ switch (wifiConfigBand) {
+ case WIFICONFIG_AP_BAND_2GHZ:
+ return SoftApConfiguration.BAND_2GHZ;
+ case WIFICONFIG_AP_BAND_5GHZ:
+ return SoftApConfiguration.BAND_5GHZ;
+ case WIFICONFIG_AP_BAND_ANY:
+ return SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ;
+ default:
+ return SoftApConfiguration.BAND_2GHZ;
+ }
+ }
+
+ /**
+ * Load AP configuration from legacy persistent storage.
+ * Note: This is deprecated and only used for migrating data once on reboot.
+ */
+ private static SoftApConfiguration loadFromLegacyFile(InputStream fis) {
+ SoftApConfiguration config = null;
+ DataInputStream in = null;
+ try {
+ SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
+ in = new DataInputStream(new BufferedInputStream(fis));
+
+ int version = in.readInt();
+ if (version < 1 || version > 3) {
+ Log.e(TAG, "Bad version on hotspot configuration file");
+ return null;
+ }
+ configBuilder.setSsid(in.readUTF());
+
+ if (version >= 2) {
+ int band = in.readInt();
+ int channel = in.readInt();
+ if (channel == 0) {
+ configBuilder.setBand(
+ convertWifiConfigBandToSoftApConfigBand(band));
+ } else {
+ configBuilder.setChannel(channel,
+ convertWifiConfigBandToSoftApConfigBand(band));
+ }
+ }
+ if (version >= 3) {
+ configBuilder.setHiddenSsid(in.readBoolean());
+ }
+ int authType = in.readInt();
+ if (authType == WifiConfiguration.KeyMgmt.WPA2_PSK) {
+ configBuilder.setPassphrase(in.readUTF(),
+ SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+ }
+ config = configBuilder.build();
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading hotspot configuration ", e);
+ config = null;
+ } catch (IllegalArgumentException ie) {
+ Log.e(TAG, "Invalid hotspot configuration ", ie);
+ config = null;
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Error closing hotspot configuration during read", e);
+ }
+ }
+ }
+ // NOTE: OEM's should add their customized parsing code here.
+ return config;
+ }
+
+ // This is the version that Android 11 released with.
+ private static final int CONFIG_STORE_DATA_VERSION = 3;
+
+ private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData";
+ private static final String XML_TAG_VERSION = "Version";
+ private static final String XML_TAG_SECTION_HEADER_SOFTAP = "SoftAp";
+ private static final String XML_TAG_SSID = "SSID";
+ private static final String XML_TAG_BSSID = "Bssid";
+ private static final String XML_TAG_CHANNEL = "Channel";
+ private static final String XML_TAG_HIDDEN_SSID = "HiddenSSID";
+ private static final String XML_TAG_SECURITY_TYPE = "SecurityType";
+ private static final String XML_TAG_AP_BAND = "ApBand";
+ private static final String XML_TAG_PASSPHRASE = "Passphrase";
+ private static final String XML_TAG_MAX_NUMBER_OF_CLIENTS = "MaxNumberOfClients";
+ private static final String XML_TAG_AUTO_SHUTDOWN_ENABLED = "AutoShutdownEnabled";
+ private static final String XML_TAG_SHUTDOWN_TIMEOUT_MILLIS = "ShutdownTimeoutMillis";
+ private static final String XML_TAG_CLIENT_CONTROL_BY_USER = "ClientControlByUser";
+ private static final String XML_TAG_BLOCKED_CLIENT_LIST = "BlockedClientList";
+ private static final String XML_TAG_ALLOWED_CLIENT_LIST = "AllowedClientList";
+ public static final String XML_TAG_CLIENT_MACADDRESS = "ClientMacAddress";
+
+ private static byte[] convertConfToXml(SoftApConfiguration softApConf) {
+ try {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+
+ // Header for the XML file.
+ out.startDocument(null, true);
+ out.startTag(null, XML_TAG_DOCUMENT_HEADER);
+ XmlUtils.writeValueXml(CONFIG_STORE_DATA_VERSION, XML_TAG_VERSION, out);
+ out.startTag(null, XML_TAG_SECTION_HEADER_SOFTAP);
+
+ // SoftAp conf
+ XmlUtils.writeValueXml(softApConf.getSsid(), XML_TAG_SSID, out);
+ if (softApConf.getBssid() != null) {
+ XmlUtils.writeValueXml(softApConf.getBssid().toString(), XML_TAG_BSSID, out);
+ }
+ XmlUtils.writeValueXml(softApConf.getBand(), XML_TAG_AP_BAND, out);
+ XmlUtils.writeValueXml(softApConf.getChannel(), XML_TAG_CHANNEL, out);
+ XmlUtils.writeValueXml(softApConf.isHiddenSsid(), XML_TAG_HIDDEN_SSID, out);
+ XmlUtils.writeValueXml(softApConf.getSecurityType(), XML_TAG_SECURITY_TYPE, out);
+ if (softApConf.getSecurityType() != SoftApConfiguration.SECURITY_TYPE_OPEN) {
+ XmlUtils.writeValueXml(softApConf.getPassphrase(), XML_TAG_PASSPHRASE, out);
+ }
+ XmlUtils.writeValueXml(softApConf.getMaxNumberOfClients(),
+ XML_TAG_MAX_NUMBER_OF_CLIENTS, out);
+ XmlUtils.writeValueXml(softApConf.isClientControlByUserEnabled(),
+ XML_TAG_CLIENT_CONTROL_BY_USER, out);
+ XmlUtils.writeValueXml(softApConf.isAutoShutdownEnabled(),
+ XML_TAG_AUTO_SHUTDOWN_ENABLED, out);
+ XmlUtils.writeValueXml(softApConf.getShutdownTimeoutMillis(),
+ XML_TAG_SHUTDOWN_TIMEOUT_MILLIS, out);
+ out.startTag(null, XML_TAG_BLOCKED_CLIENT_LIST);
+ for (MacAddress mac: softApConf.getBlockedClientList()) {
+ XmlUtils.writeValueXml(mac.toString(), XML_TAG_CLIENT_MACADDRESS, out);
+ }
+ out.endTag(null, XML_TAG_BLOCKED_CLIENT_LIST);
+ out.startTag(null, XML_TAG_ALLOWED_CLIENT_LIST);
+ for (MacAddress mac: softApConf.getAllowedClientList()) {
+ XmlUtils.writeValueXml(mac.toString(), XML_TAG_CLIENT_MACADDRESS, out);
+ }
+ out.endTag(null, XML_TAG_ALLOWED_CLIENT_LIST);
+
+ // Footer for the XML file.
+ out.endTag(null, XML_TAG_SECTION_HEADER_SOFTAP);
+ out.endTag(null, XML_TAG_DOCUMENT_HEADER);
+ out.endDocument();
+
+ return outputStream.toByteArray();
+ } catch (IOException | XmlPullParserException e) {
+ Log.e(TAG, "Failed to convert softap conf to XML", e);
+ return null;
+ }
+ }
+
+ private SoftApConfToXmlMigrationUtil() { }
+
+ /**
+ * Read the legacy /data/misc/wifi/softap.conf file format and convert to the new XML
+ * format understood by WifiConfigStore.
+ * Note: Used for unit testing.
+ */
+ @VisibleForTesting
+ @Nullable
+ public static InputStream convert(InputStream fis) {
+ SoftApConfiguration softApConf = loadFromLegacyFile(fis);
+ if (softApConf == null) return null;
+
+ byte[] xmlBytes = convertConfToXml(softApConf);
+ if (xmlBytes == null) return null;
+
+ return new ByteArrayInputStream(xmlBytes);
+ }
+
+ /**
+ * Read the legacy /data/misc/wifi/softap.conf file format and convert to the new XML
+ * format understood by WifiConfigStore.
+ */
+ @Nullable
+ public static InputStream convert() {
+ File file = new File(getLegacyWifiSharedDirectory(), LEGACY_AP_CONFIG_FILE);
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(file);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ if (fis == null) return null;
+ return convert(fis);
+ }
+
+ /**
+ * Remove the legacy /data/misc/wifi/softap.conf file.
+ */
+ @Nullable
+ public static void remove() {
+ File file = new File(getLegacyWifiSharedDirectory(), LEGACY_AP_CONFIG_FILE);
+ file.delete();
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiMigration.java b/wifi/java/android/net/wifi/WifiMigration.java
index f2a1aec550b1..666d72d32ac7 100755
--- a/wifi/java/android/net/wifi/WifiMigration.java
+++ b/wifi/java/android/net/wifi/WifiMigration.java
@@ -95,7 +95,7 @@ public final class WifiMigration {
private static final SparseArray<String> STORE_ID_TO_FILE_NAME =
new SparseArray<String>() {{
put(STORE_FILE_SHARED_GENERAL, "WifiConfigStore.xml");
- put(STORE_FILE_SHARED_SOFTAP, "softap.conf");
+ put(STORE_FILE_SHARED_SOFTAP, "WifiConfigStoreSoftAp.xml");
put(STORE_FILE_USER_GENERAL, "WifiConfigStore.xml");
put(STORE_FILE_USER_NETWORK_SUGGESTIONS, "WifiConfigStoreNetworkSuggestions.xml");
}};
@@ -176,6 +176,13 @@ public final class WifiMigration {
// OEMs should do conversions necessary here before returning the stream.
return getSharedAtomicFile(storeFileId).openRead();
} catch (FileNotFoundException e) {
+ // Special handling for softap.conf.
+ // Note: OEM devices upgrading from Q -> R will only have the softap.conf file.
+ // Test devices running previous R builds however may have already migrated to the
+ // XML format. So, check for that above before falling back to check for legacy file.
+ if (storeFileId == STORE_FILE_SHARED_SOFTAP) {
+ return SoftApConfToXmlMigrationUtil.convert();
+ }
return null;
}
}
@@ -191,7 +198,18 @@ public final class WifiMigration {
if (storeFileId != STORE_FILE_SHARED_GENERAL && storeFileId != STORE_FILE_SHARED_SOFTAP) {
throw new IllegalArgumentException("Invalid shared store file id");
}
- getSharedAtomicFile(storeFileId).delete();
+ AtomicFile file = getSharedAtomicFile(storeFileId);
+ if (file.exists()) {
+ file.delete();
+ return;
+ }
+ // Special handling for softap.conf.
+ // Note: OEM devices upgrading from Q -> R will only have the softap.conf file.
+ // Test devices running previous R builds however may have already migrated to the
+ // XML format. So, check for that above before falling back to check for legacy file.
+ if (storeFileId == STORE_FILE_SHARED_SOFTAP) {
+ SoftApConfToXmlMigrationUtil.remove();
+ }
}
/**
@@ -258,7 +276,10 @@ public final class WifiMigration {
throw new IllegalArgumentException("Invalid user store file id");
}
Objects.requireNonNull(userHandle);
- getUserAtomicFile(storeFileId, userHandle.getIdentifier()).delete();
+ AtomicFile file = getUserAtomicFile(storeFileId, userHandle.getIdentifier());
+ if (file.exists()) {
+ file.delete();
+ }
}
/**
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java b/wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
new file mode 100644
index 000000000000..f49f387cbc6b
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/SoftApConfToXmlMigrationUtilTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Unit tests for {@link android.net.wifi.SoftApConfToXmlMigrationUtilTest}.
+ */
+@SmallTest
+public class SoftApConfToXmlMigrationUtilTest {
+ private static final String TEST_SSID = "SSID";
+ private static final String TEST_PASSPHRASE = "TestPassphrase";
+ private static final int TEST_CHANNEL = 0;
+ private static final boolean TEST_HIDDEN = false;
+ private static final int TEST_BAND = SoftApConfiguration.BAND_5GHZ;
+ private static final int TEST_SECURITY = SoftApConfiguration.SECURITY_TYPE_WPA2_PSK;
+
+ private static final String TEST_EXPECTED_XML_STRING =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<WifiConfigStoreData>\n"
+ + "<int name=\"Version\" value=\"3\" />\n"
+ + "<SoftAp>\n"
+ + "<string name=\"SSID\">" + TEST_SSID + "</string>\n"
+ + "<int name=\"ApBand\" value=\"" + TEST_BAND + "\" />\n"
+ + "<int name=\"Channel\" value=\"" + TEST_CHANNEL + "\" />\n"
+ + "<boolean name=\"HiddenSSID\" value=\"" + TEST_HIDDEN + "\" />\n"
+ + "<int name=\"SecurityType\" value=\"" + TEST_SECURITY + "\" />\n"
+ + "<string name=\"Passphrase\">" + TEST_PASSPHRASE + "</string>\n"
+ + "<int name=\"MaxNumberOfClients\" value=\"0\" />\n"
+ + "<boolean name=\"ClientControlByUser\" value=\"false\" />\n"
+ + "<boolean name=\"AutoShutdownEnabled\" value=\"true\" />\n"
+ + "<long name=\"ShutdownTimeoutMillis\" value=\"0\" />\n"
+ + "<BlockedClientList />\n"
+ + "<AllowedClientList />\n"
+ + "</SoftAp>\n"
+ + "</WifiConfigStoreData>\n";
+
+ private byte[] createLegacyApConfFile(WifiConfiguration config) throws Exception {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(outputStream);
+ out.writeInt(3);
+ out.writeUTF(config.SSID);
+ out.writeInt(config.apBand);
+ out.writeInt(config.apChannel);
+ out.writeBoolean(config.hiddenSSID);
+ int authType = config.getAuthType();
+ out.writeInt(authType);
+ if (authType != WifiConfiguration.KeyMgmt.NONE) {
+ out.writeUTF(config.preSharedKey);
+ }
+ out.close();
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * Generate a SoftApConfiguration based on the specified parameters.
+ */
+ private SoftApConfiguration setupApConfig(
+ String ssid, String preSharedKey, int keyManagement, int band, int channel,
+ boolean hiddenSSID) {
+ SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
+ configBuilder.setSsid(ssid);
+ configBuilder.setPassphrase(preSharedKey, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+ if (channel == 0) {
+ configBuilder.setBand(band);
+ } else {
+ configBuilder.setChannel(channel, band);
+ }
+ configBuilder.setHiddenSsid(hiddenSSID);
+ return configBuilder.build();
+ }
+
+ /**
+ * Generate a WifiConfiguration based on the specified parameters.
+ */
+ private WifiConfiguration setupWifiConfigurationApConfig(
+ String ssid, String preSharedKey, int keyManagement, int band, int channel,
+ boolean hiddenSSID) {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = ssid;
+ config.preSharedKey = preSharedKey;
+ config.allowedKeyManagement.set(keyManagement);
+ config.apBand = band;
+ config.apChannel = channel;
+ config.hiddenSSID = hiddenSSID;
+ return config;
+ }
+
+ /**
+ * Asserts that the WifiConfigurations equal to SoftApConfiguration.
+ * This only compares the elements saved
+ * for softAp used.
+ */
+ public static void assertWifiConfigurationEqualSoftApConfiguration(
+ WifiConfiguration backup, SoftApConfiguration restore) {
+ assertEquals(backup.SSID, restore.getSsid());
+ assertEquals(backup.BSSID, restore.getBssid());
+ assertEquals(SoftApConfToXmlMigrationUtil.convertWifiConfigBandToSoftApConfigBand(
+ backup.apBand),
+ restore.getBand());
+ assertEquals(backup.apChannel, restore.getChannel());
+ assertEquals(backup.preSharedKey, restore.getPassphrase());
+ if (backup.getAuthType() == WifiConfiguration.KeyMgmt.WPA2_PSK) {
+ assertEquals(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, restore.getSecurityType());
+ } else {
+ assertEquals(SoftApConfiguration.SECURITY_TYPE_OPEN, restore.getSecurityType());
+ }
+ assertEquals(backup.hiddenSSID, restore.isHiddenSsid());
+ }
+
+ /**
+ * Note: This is a copy of {@link AtomicFile#readFully()} modified to use the passed in
+ * {@link InputStream} which was returned using {@link AtomicFile#openRead()}.
+ */
+ private static byte[] readFully(InputStream stream) throws IOException {
+ try {
+ int pos = 0;
+ int avail = stream.available();
+ byte[] data = new byte[avail];
+ while (true) {
+ int amt = stream.read(data, pos, data.length - pos);
+ if (amt <= 0) {
+ return data;
+ }
+ pos += amt;
+ avail = stream.available();
+ if (avail > data.length - pos) {
+ byte[] newData = new byte[pos + avail];
+ System.arraycopy(data, 0, newData, 0, pos);
+ data = newData;
+ }
+ }
+ } finally {
+ stream.close();
+ }
+ }
+
+ /**
+ * Tests conversion from legacy .conf file to XML file format.
+ */
+ @Test
+ public void testConversion() throws Exception {
+ WifiConfiguration backupConfig = setupWifiConfigurationApConfig(
+ TEST_SSID, /* SSID */
+ TEST_PASSPHRASE, /* preshared key */
+ WifiConfiguration.KeyMgmt.WPA2_PSK, /* key management */
+ 1, /* AP band (5GHz) */
+ TEST_CHANNEL, /* AP channel */
+ TEST_HIDDEN /* Hidden SSID */);
+ SoftApConfiguration expectedConfig = setupApConfig(
+ TEST_SSID, /* SSID */
+ TEST_PASSPHRASE, /* preshared key */
+ SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, /* security type */
+ SoftApConfiguration.BAND_5GHZ, /* AP band (5GHz) */
+ TEST_CHANNEL, /* AP channel */
+ TEST_HIDDEN /* Hidden SSID */);
+
+ assertWifiConfigurationEqualSoftApConfiguration(backupConfig, expectedConfig);
+
+ byte[] confBytes = createLegacyApConfFile(backupConfig);
+ assertNotNull(confBytes);
+
+ InputStream xmlStream = SoftApConfToXmlMigrationUtil.convert(
+ new ByteArrayInputStream(confBytes));
+
+ byte[] xmlBytes = readFully(xmlStream);
+ assertNotNull(xmlBytes);
+
+ assertEquals(TEST_EXPECTED_XML_STRING, new String(xmlBytes));
+ }
+
+}