diff options
540 files changed, 10092 insertions, 3518 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING index 3409838b5611..117faa203325 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -126,6 +126,12 @@ "exclude-annotation": "org.junit.Ignore" } ] + }, + { + "name": "vts_treble_vintf_framework_test" + }, + { + "name": "vts_treble_vintf_vendor_test" } ], "postsubmit-ravenwood": [ diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt index 6d1e6d0cbd73..4352c8ae982e 100644 --- a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt +++ b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt @@ -24,10 +24,11 @@ import android.content.pm.parsing.result.ParseTypeImpl import android.content.res.TypedArray import android.perftests.utils.BenchmarkState import android.perftests.utils.PerfStatusReporter +import android.util.ArraySet import androidx.test.filters.LargeTest +import com.android.internal.pm.parsing.pkg.PackageImpl +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils import com.android.internal.util.ConcurrentUtils -import com.android.server.pm.parsing.pkg.PackageImpl -import com.android.server.pm.pkg.parsing.ParsingPackageUtils import java.io.File import java.io.FileOutputStream import java.util.concurrent.ArrayBlockingQueue @@ -214,7 +215,10 @@ public class PackageParsingPerfTest { path, manifestArray, isCoreApp, + this, ) + override fun getHiddenApiWhitelistedApps() = ArraySet<String>() + override fun getInstallConstraintsAllowlist() = ArraySet<String>() }) override fun parseImpl(file: File) = diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index 03891bbec56a..e3ba50dc635b 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -26,6 +26,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.SystemClock; import android.os.UserHandle; @@ -319,8 +320,8 @@ public final class BackgroundJobsController extends StateController { final boolean isStopped = mPackageManagerInternal.isPackageStopped(packageName, uid); mPackageStoppedState.add(uid, packageName, isStopped); return isStopped; - } catch (IllegalArgumentException e) { - Slog.d(TAG, "Couldn't determine stopped state for unknown package: " + packageName); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Couldn't determine stopped state for unknown package: " + packageName); return false; } } diff --git a/api/Android.bp b/api/Android.bp index 2b1cfcb82d04..9029d252b470 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -31,12 +31,10 @@ bootstrap_go_package { "blueprint", "soong", "soong-android", - "soong-bp2build", "soong-genrule", "soong-java", ], srcs: ["api.go"], - testSrcs: ["api_test.go"], pluginFor: ["soong_build"], } diff --git a/api/api.go b/api/api.go index 71b1e10d2f47..2668999c572e 100644 --- a/api/api.go +++ b/api/api.go @@ -22,7 +22,6 @@ import ( "github.com/google/blueprint/proptools" "android/soong/android" - "android/soong/bazel" "android/soong/genrule" "android/soong/java" ) @@ -65,7 +64,6 @@ type CombinedApisProperties struct { type CombinedApis struct { android.ModuleBase - android.BazelModuleBase properties CombinedApisProperties } @@ -115,20 +113,6 @@ type defaultsProps struct { Previous_api *string } -type Bazel_module struct { - Label *string - Bp2build_available *bool -} -type bazelProperties struct { - *Bazel_module -} - -var bp2buildNotAvailable = bazelProperties{ - &Bazel_module{ - Bp2build_available: proptools.BoolPtr(false), - }, -} - // Struct to pass parameters for the various merged [current|removed].txt file modules we create. type MergedTxtDefinition struct { // "current.txt" or "removed.txt" @@ -143,8 +127,6 @@ type MergedTxtDefinition struct { ModuleTag string // public, system, module-lib or system-server Scope string - // True if there is a bp2build definition for this module - Bp2buildDefined bool } func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { @@ -178,20 +160,7 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { }, } props.Visibility = []string{"//visibility:public"} - bazelProps := bazelProperties{ - &Bazel_module{ - Bp2build_available: proptools.BoolPtr(false), - }, - } - if txt.Bp2buildDefined { - moduleDir := ctx.ModuleDir() - if moduleDir == android.Bp2BuildTopLevel { - moduleDir = "" - } - label := fmt.Sprintf("//%s:%s", moduleDir, moduleName) - bazelProps.Label = &label - } - ctx.CreateModule(genrule.GenRuleFactory, &props, &bazelProps) + ctx.CreateModule(genrule.GenRuleFactory, &props) } func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) { @@ -221,7 +190,7 @@ func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, sys props := fgProps{} props.Name = proptools.StringPtr(i.name) props.Srcs = createSrcs(i.modules, i.tag) - ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable) + ctx.CreateModule(android.FileGroupFactory, &props) } } @@ -315,7 +284,7 @@ func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []str props.Name = proptools.StringPtr("all-modules-public-stubs-source") props.Srcs = createSrcs(modules, "{.public.stubs.source}") props.Visibility = []string{"//frameworks/base"} - ctx.CreateModule(android.FileGroupFactory, &props, &bp2buildNotAvailable) + ctx.CreateModule(android.FileGroupFactory, &props) } func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) { @@ -323,43 +292,38 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_ tagSuffix := []string{".api.txt}", ".removed-api.txt}"} distFilename := []string{"android.txt", "android-removed.txt"} - bp2BuildDefined := []bool{true, false} for i, f := range []string{"current.txt", "removed.txt"} { textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - DistFilename: distFilename[i], - BaseTxt: ":non-updatable-" + f, - Modules: bootclasspath, - ModuleTag: "{.public" + tagSuffix[i], - Scope: "public", - Bp2buildDefined: bp2BuildDefined[i], + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-" + f, + Modules: bootclasspath, + ModuleTag: "{.public" + tagSuffix[i], + Scope: "public", }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - DistFilename: distFilename[i], - BaseTxt: ":non-updatable-system-" + f, - Modules: bootclasspath, - ModuleTag: "{.system" + tagSuffix[i], - Scope: "system", - Bp2buildDefined: bp2BuildDefined[i], + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-system-" + f, + Modules: bootclasspath, + ModuleTag: "{.system" + tagSuffix[i], + Scope: "system", }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - DistFilename: distFilename[i], - BaseTxt: ":non-updatable-module-lib-" + f, - Modules: bootclasspath, - ModuleTag: "{.module-lib" + tagSuffix[i], - Scope: "module-lib", - Bp2buildDefined: bp2BuildDefined[i], + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-module-lib-" + f, + Modules: bootclasspath, + ModuleTag: "{.module-lib" + tagSuffix[i], + Scope: "module-lib", }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - DistFilename: distFilename[i], - BaseTxt: ":non-updatable-system-server-" + f, - Modules: system_server_classpath, - ModuleTag: "{.system-server" + tagSuffix[i], - Scope: "system-server", - Bp2buildDefined: bp2BuildDefined[i], + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-system-server-" + f, + Modules: system_server_classpath, + ModuleTag: "{.system-server" + tagSuffix[i], + Scope: "system-server", }) } for _, txt := range textFiles { @@ -446,55 +410,9 @@ func combinedApisModuleFactory() android.Module { module.AddProperties(&module.properties) android.InitAndroidModule(module) android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) }) - android.InitBazelModule(module) return module } -type bazelCombinedApisAttributes struct { - Scope bazel.StringAttribute - Base bazel.LabelAttribute - Deps bazel.LabelListAttribute -} - -// combined_apis bp2build converter -func (a *CombinedApis) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) { - basePrefix := "non-updatable" - scopeToSuffix := map[string]string{ - "public": "-current.txt", - "system": "-system-current.txt", - "module-lib": "-module-lib-current.txt", - "system-server": "-system-server-current.txt", - } - - for scopeName, suffix := range scopeToSuffix { - name := a.Name() + suffix - - var scope bazel.StringAttribute - scope.SetValue(scopeName) - - var base bazel.LabelAttribute - base.SetValue(android.BazelLabelForModuleDepSingle(ctx, basePrefix+suffix)) - - var deps bazel.LabelListAttribute - classpath := a.properties.Bootclasspath - if scopeName == "system-server" { - classpath = a.properties.System_server_classpath - } - deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, classpath)) - - attrs := bazelCombinedApisAttributes{ - Scope: scope, - Base: base, - Deps: deps, - } - props := bazel.BazelTargetModuleProperties{ - Rule_class: "merged_txts", - Bzl_load_location: "//build/bazel/rules/java:merged_txts.bzl", - } - ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, &attrs) - } -} - // Various utility methods below. // Creates an array of ":<m><tag>" for each m in <modules>. diff --git a/api/api_test.go b/api/api_test.go deleted file mode 100644 index 70f2162348ad..000000000000 --- a/api/api_test.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (C) 2023 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 api - -import ( - "testing" - - "android/soong/android" - "android/soong/bp2build" - "android/soong/java" -) - -func runCombinedApisTestCaseWithRegistrationCtxFunc(t *testing.T, tc bp2build.Bp2buildTestCase, registrationCtxFunc func(ctx android.RegistrationContext)) { - t.Helper() - (&tc).ModuleTypeUnderTest = "combined_apis" - (&tc).ModuleTypeUnderTestFactory = combinedApisModuleFactory - bp2build.RunBp2BuildTestCase(t, registrationCtxFunc, tc) -} - -func runCombinedApisTestCase(t *testing.T, tc bp2build.Bp2buildTestCase) { - t.Helper() - runCombinedApisTestCaseWithRegistrationCtxFunc(t, tc, func(ctx android.RegistrationContext) { - ctx.RegisterModuleType("java_defaults", java.DefaultsFactory) - ctx.RegisterModuleType("java_sdk_library", java.SdkLibraryFactory) - ctx.RegisterModuleType("filegroup", android.FileGroupFactory) - }) -} - -func TestCombinedApisGeneral(t *testing.T) { - runCombinedApisTestCase(t, bp2build.Bp2buildTestCase{ - Description: "combined_apis, general case", - Blueprint: `combined_apis { - name: "foo", - bootclasspath: ["bcp"], - system_server_classpath: ["ssc"], -} - -java_sdk_library { - name: "bcp", - srcs: ["a.java", "b.java"], - shared_library: false, -} -java_sdk_library { - name: "ssc", - srcs: ["a.java", "b.java"], - shared_library: false, -} -filegroup { - name: "non-updatable-current.txt", - srcs: ["current.txt"], -} -filegroup { - name: "non-updatable-system-current.txt", - srcs: ["system-current.txt"], -} -filegroup { - name: "non-updatable-module-lib-current.txt", - srcs: ["system-removed.txt"], -} -filegroup { - name: "non-updatable-system-server-current.txt", - srcs: ["system-lint-baseline.txt"], -} -`, - Filesystem: map[string]string{ - "a/Android.bp": ` - java_defaults { - name: "android.jar_defaults", - } - `, - "api/current.txt": "", - "api/removed.txt": "", - "api/system-current.txt": "", - "api/system-removed.txt": "", - "api/test-current.txt": "", - "api/test-removed.txt": "", - }, - StubbedBuildDefinitions: []string{"bcp", "ssc", "non-updatable-current.txt", "non-updatable-system-current.txt", "non-updatable-module-lib-current.txt", "non-updatable-system-server-current.txt"}, - ExpectedHandcraftedModules: []string{"foo-current.txt", "foo-system-current.txt", "foo-module-lib-current.txt", "foo-system-server-current.txt"}, - ExpectedBazelTargets: []string{ - bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-current.txt", bp2build.AttrNameToString{ - "scope": `"public"`, - "base": `":non-updatable-current.txt"`, - "deps": `[":bcp"]`, - }), - bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-current.txt", bp2build.AttrNameToString{ - "scope": `"system"`, - "base": `":non-updatable-system-current.txt"`, - "deps": `[":bcp"]`, - }), - bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-module-lib-current.txt", bp2build.AttrNameToString{ - "scope": `"module-lib"`, - "base": `":non-updatable-module-lib-current.txt"`, - "deps": `[":bcp"]`, - }), - bp2build.MakeBazelTargetNoRestrictions("merged_txts", "foo-system-server-current.txt", bp2build.AttrNameToString{ - "scope": `"system-server"`, - "base": `":non-updatable-system-server-current.txt"`, - "deps": `[":ssc"]`, - }), - }, - }) -} diff --git a/api/go.mod b/api/go.mod index f8bb1c01cd96..445a6f4a5ec6 100644 --- a/api/go.mod +++ b/api/go.mod @@ -6,7 +6,5 @@ require ( android/soong v0.0.0 github.com/google/blueprint v0.0.0 google.golang.org/protobuf v0.0.0 - prebuilts/bazel/common/proto/analysis_v2 v0.0.0 - prebuilts/bazel/common/proto/build v0.0.0 go.starlark.net v0.0.0 ) diff --git a/api/go.work b/api/go.work index aa2d2b1cb461..edd002e7efba 100644 --- a/api/go.work +++ b/api/go.work @@ -13,7 +13,5 @@ replace ( google.golang.org/protobuf v0.0.0 => ../../../external/golang-protobuf github.com/google/blueprint v0.0.0 => ../../../build/blueprint github.com/google/go-cmp v0.0.0 => ../../../external/go-cmp - prebuilts/bazel/common/proto/analysis_v2 v0.0.0 => ../../../prebuilts/bazel/common/proto/analysis_v2 - prebuilts/bazel/common/proto/build v0.0.0 => ../../../prebuilts/bazel/common/proto/build go.starlark.net v0.0.0 => ../../../external/starlark-go ) diff --git a/core/api/current.txt b/core/api/current.txt index 83e3fabe475b..7f261d450b42 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5285,9 +5285,10 @@ package android.app { field public static final int START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START = 21; // 0x15 field public static final int START_TIMESTAMP_RESERVED_RANGE_SYSTEM = 20; // 0x14 field public static final int START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE = 7; // 0x7 - field public static final int START_TYPE_COLD = 0; // 0x0 - field public static final int START_TYPE_HOT = 2; // 0x2 - field public static final int START_TYPE_WARM = 1; // 0x1 + field public static final int START_TYPE_COLD = 1; // 0x1 + field public static final int START_TYPE_HOT = 3; // 0x3 + field public static final int START_TYPE_UNSET = 0; // 0x0 + field public static final int START_TYPE_WARM = 2; // 0x2 } public final class AsyncNotedAppOp implements android.os.Parcelable { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 9f56933865db..e92564b5d7c2 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4091,7 +4091,7 @@ package android.content.pm { field @Deprecated public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1 field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff field public static final int MATCH_ANY_USER = 4194304; // 0x400000 - field @Deprecated public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000 + field public static final int MATCH_CLONE_PROFILE = 536870912; // 0x20000000 field @FlaggedApi("android.content.pm.fix_duplicated_flags") public static final long MATCH_CLONE_PROFILE_LONG = 17179869184L; // 0x400000000L field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000 @@ -12930,6 +12930,7 @@ package android.service.voice { field public static final int CONFIDENCE_LEVEL_LOW = 1; // 0x1 field public static final int CONFIDENCE_LEVEL_MEDIUM = 2; // 0x2 field public static final int CONFIDENCE_LEVEL_NONE = 0; // 0x0 + field @FlaggedApi("android.service.voice.flags.allow_hotword_bump_egress") public static final int CONFIDENCE_LEVEL_VERY_HIGH = 4; // 0x4 field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordRejectedResult> CREATOR; } @@ -13051,7 +13052,7 @@ package android.service.voice { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.HotwordDetector createHotwordDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.HotwordDetector.Callback); method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager(); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public final android.service.voice.VisualQueryDetector createVisualQueryDetector(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull android.service.voice.VisualQueryDetector.Callback); - method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public void setIsReceiveSandboxedTrainingDataAllowed(boolean); + method @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") @RequiresPermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) public void setShouldReceiveSandboxedTrainingData(boolean); } } @@ -13701,6 +13702,7 @@ package android.telephony { method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>); method public int describeContents(); method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(); + method @FlaggedApi("com.android.internal.telephony.flags.carrier_restriction_status") public int getCarrierRestrictionStatus(); method public int getDefaultCarrierRestriction(); method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers(); method public int getMultiSimPolicy(); @@ -16987,7 +16989,7 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getSatelliteAttachRestrictionReasonsForCarrier(int); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback) throws android.telephony.satellite.SatelliteManager.SatelliteException; method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback); @@ -16999,7 +17001,7 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>); - method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>); + method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>); method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index dec1ee52712d..cef11bb42c3f 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -1909,6 +1909,8 @@ SamShouldBeLast: android.view.accessibility.AccessibilityManager#addAccessibilit SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addAccessibilityStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addTouchExplorationStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.view.inputmethod.InputMethodInfo#dump(android.util.Printer, String): + SAM-compatible parameters (such as parameter 1, "pw", in android.view.inputmethod.InputMethodInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams): SAM-compatible parameters (such as parameter 2, "filePathCallback", in android.webkit.WebChromeClient.onShowFileChooser) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 39f2737dc880..a3cd3dc87db3 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2,6 +2,7 @@ package android { public static final class Manifest.permission { + field @FlaggedApi("com.android.server.accessibility.motion_event_observing") public static final String ACCESSIBILITY_MOTION_EVENT_OBSERVING = "android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING"; field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS"; field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING"; field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY"; @@ -99,6 +100,8 @@ package android.accessibilityservice { public class AccessibilityServiceInfo implements android.os.Parcelable { method @NonNull public android.content.ComponentName getComponentName(); + method @FlaggedApi("android.view.accessibility.motion_event_observing") public int getObservedMotionEventSources(); + method @FlaggedApi("android.view.accessibility.motion_event_observing") public void setObservedMotionEventSources(int); } } diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 8ad6ea207665..fc342fa3431a 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -20,6 +20,7 @@ import static android.accessibilityservice.util.AccessibilityUtils.getFilteredHt import static android.accessibilityservice.util.AccessibilityUtils.loadSafeAnimatedImage; import static android.content.pm.PackageManager.FEATURE_FINGERPRINT; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -53,6 +54,7 @@ import android.view.InputDevice; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.Flags; import com.android.internal.R; import com.android.internal.compat.IPlatformCompat; @@ -630,7 +632,8 @@ public class AccessibilityServiceInfo implements Parcelable { InputDevice.SOURCE_TOUCH_NAVIGATION, InputDevice.SOURCE_ROTARY_ENCODER, InputDevice.SOURCE_JOYSTICK, - InputDevice.SOURCE_SENSOR + InputDevice.SOURCE_SENSOR, + InputDevice.SOURCE_TOUCHSCREEN }) @Retention(RetentionPolicy.SOURCE) public @interface MotionEventSources {} @@ -642,6 +645,8 @@ public class AccessibilityServiceInfo implements Parcelable { @MotionEventSources private int mMotionEventSources = 0; + private int mObservedMotionEventSources = 0; + /** * Creates a new instance. */ @@ -817,6 +822,9 @@ public class AccessibilityServiceInfo implements Parcelable { mInteractiveUiTimeout = other.mInteractiveUiTimeout; flags = other.flags; mMotionEventSources = other.mMotionEventSources; + if (Flags.motionEventObserving()) { + setObservedMotionEventSources(other.mObservedMotionEventSources); + } // NOTE: Ensure that only properties that are safe to be modified by the service itself // are included here (regardless of hidden setters, etc.). } @@ -1024,16 +1032,75 @@ public class AccessibilityServiceInfo implements Parcelable { */ public void setMotionEventSources(@MotionEventSources int motionEventSources) { mMotionEventSources = motionEventSources; + mObservedMotionEventSources = 0; + } + + /** + * Sets the bit mask of {@link android.view.InputDevice} sources that the accessibility service + * wants to observe generic {@link android.view.MotionEvent}s from if it has already requested + * to listen to them using {@link #setMotionEventSources(int)}. Events from these sources will + * be sent to the rest of the input pipeline without being consumed by accessibility services. + * This service will still be able to see them. + * + * <p><strong>Note:</strong> you will need to call this function every time you call {@link + * #setMotionEventSources(int)}. Calling {@link #setMotionEventSources(int)} clears the list of + * observed motion event sources for this service. + * + * <p><strong>Note:</strong> {@link android.view.InputDevice} sources contain source class bits + * that complicate bitwise flag removal operations. To remove a specific source you should + * rebuild the entire value using bitwise OR operations on the individual source constants. + * + * <p>Including an {@link android.view.InputDevice} source that does not send {@link + * android.view.MotionEvent}s is effectively a no-op for that source, since you will not receive + * any events from that source. + * + * <p><strong>Note:</strong> Calling this function with a source that has not been listened to + * using {@link #setMotionEventSources(int)} will throw an exception. + * + * @see AccessibilityService#onMotionEvent + * @see #MotionEventSources + * @see #setMotionEventSources(int) + * @hide + */ + @FlaggedApi(Flags.FLAG_MOTION_EVENT_OBSERVING) + @TestApi + public void setObservedMotionEventSources(int observedMotionEventSources) { + // Confirm that any sources requested here have already been requested for listening. + if ((observedMotionEventSources & ~mMotionEventSources) != 0) { + String message = + String.format( + "Requested motion event sources for listening = 0x%x but requested" + + " motion event sources for observing = 0x%x.", + mMotionEventSources, observedMotionEventSources); + throw new IllegalArgumentException(message); + } + mObservedMotionEventSources = observedMotionEventSources; + } + + /** + * Returns the bit mask of {@link android.view.InputDevice} sources that the accessibility + * service wants to observe generic {@link android.view.MotionEvent}s from if it has already + * requested to listen to them using {@link #setMotionEventSources(int)}. Events from these + * sources will be sent to the rest of the input pipeline without being consumed by + * accessibility services. This service will still be able to see them. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_MOTION_EVENT_OBSERVING) + @MotionEventSources + @TestApi + public int getObservedMotionEventSources() { + return mObservedMotionEventSources; } /** * The localized summary of the accessibility service. - * <p> - * <strong>Statically set from - * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> - * </p> - * @return The localized summary if available, and {@code null} if a summary - * has not been provided. + * + * <p><strong>Statically set from {@link AccessibilityService#SERVICE_META_DATA + * meta-data}.</strong> + * + * @return The localized summary if available, and {@code null} if a summary has not been + * provided. */ public CharSequence loadSummary(PackageManager packageManager) { if (mSummaryResId == 0) { @@ -1260,6 +1327,7 @@ public class AccessibilityServiceInfo implements Parcelable { parcel.writeString(mTileServiceName); parcel.writeInt(mIntroResId); parcel.writeInt(mMotionEventSources); + parcel.writeInt(mObservedMotionEventSources); } private void initFromParcel(Parcel parcel) { @@ -1285,6 +1353,8 @@ public class AccessibilityServiceInfo implements Parcelable { mTileServiceName = parcel.readString(); mIntroResId = parcel.readInt(); mMotionEventSources = parcel.readInt(); + // use the setter here because it throws an exception for invalid values. + setObservedMotionEventSources(parcel.readInt()); } @Override diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index ffed40538702..4a9fa9e63bf9 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6884,8 +6884,8 @@ public class Activity extends ContextThemeWrapper * application package was involved. * * <p>If called while inside the handling of {@link #onNewIntent}, this function will - * return the referrer that submitted that new intent to the activity. Otherwise, it - * always returns the referrer of the original Intent.</p> + * return the referrer that submitted that new intent to the activity only after + * {@link #setIntent(Intent)} is called with the provided intent.</p> * * <p>Note that this is <em>not</em> a security feature -- you can not trust the * referrer information, applications can spoof it.</p> diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8b39ed6fb411..6ddb36a72aa9 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4077,7 +4077,7 @@ public final class ActivityThread extends ClientTransactionHandler final LoadedApk sdkApk = getPackageInfo( contextInfo.getSdkApplicationInfo(), r.packageInfo.getCompatibilityInfo(), - ActivityContextInfo.CONTEXT_FLAGS); + contextInfo.getContextFlags()); final ContextImpl activityContext = ContextImpl.createActivityContext( this, sdkApk, r.activityInfo, r.token, displayId, r.overrideConfig); diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java index c8317c8faad5..656feb0401d6 100644 --- a/core/java/android/app/ApplicationStartInfo.java +++ b/core/java/android/app/ApplicationStartInfo.java @@ -104,14 +104,17 @@ public final class ApplicationStartInfo implements Parcelable { /** Process started due to Activity started for any reason not explicitly listed. */ public static final int START_REASON_START_ACTIVITY = 11; + /** Start type not yet set. */ + public static final int START_TYPE_UNSET = 0; + /** Process started from scratch. */ - public static final int START_TYPE_COLD = 0; + public static final int START_TYPE_COLD = 1; /** Process retained minimally SavedInstanceState. */ - public static final int START_TYPE_WARM = 1; + public static final int START_TYPE_WARM = 2; /** Process brought back to foreground. */ - public static final int START_TYPE_HOT = 2; + public static final int START_TYPE_HOT = 3; /** * Default. The system always creates a new instance of the activity in the target task and @@ -277,6 +280,7 @@ public final class ApplicationStartInfo implements Parcelable { @IntDef( prefix = {"START_TYPE_"}, value = { + START_TYPE_UNSET, START_TYPE_COLD, START_TYPE_WARM, START_TYPE_HOT, @@ -769,6 +773,7 @@ public final class ApplicationStartInfo implements Parcelable { private static String startTypeToString(@StartType int startType) { return switch (startType) { + case START_TYPE_UNSET -> "UNSET"; case START_TYPE_COLD -> "COLD"; case START_TYPE_WARM -> "WARM"; case START_TYPE_HOT -> "HOT"; diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index d93544972e7a..53a21cdd78c9 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -16,6 +16,8 @@ package android.app; +import static com.android.internal.util.Preconditions.checkArgument; + import android.annotation.DrawableRes; import android.annotation.FlaggedApi; import android.annotation.IntDef; @@ -390,7 +392,7 @@ public final class AutomaticZenRule implements Parcelable { */ @FlaggedApi(Flags.FLAG_MODES_API) public void setType(@Type int type) { - mType = type; + mType = checkValidType(type); } /** @@ -451,6 +453,24 @@ public final class AutomaticZenRule implements Parcelable { mAllowManualInvocation = allowManualInvocation; } + /** @hide */ + @FlaggedApi(Flags.FLAG_MODES_API) + public void validate() { + if (Flags.modesApi()) { + checkValidType(mType); + } + } + + @FlaggedApi(Flags.FLAG_MODES_API) + @Type + private static int checkValidType(@Type int type) { + checkArgument(type >= TYPE_UNKNOWN && type <= TYPE_MANAGED, + "Rule type must be one of TYPE_UNKNOWN, TYPE_OTHER, TYPE_SCHEDULE_TIME, " + + "TYPE_SCHEDULE_CALENDAR, TYPE_BEDTIME, TYPE_DRIVING, TYPE_IMMERSIVE, " + + "TYPE_THEATER, or TYPE_MANAGED"); + return type; + } + @Override public int describeContents() { return 0; @@ -703,10 +723,10 @@ public final class AutomaticZenRule implements Parcelable { } /** - * Sets the type of the rule + * Sets the type of the rule. */ public @NonNull Builder setType(@Type int type) { - mType = type; + mType = checkValidType(type); return this; } @@ -714,7 +734,7 @@ public final class AutomaticZenRule implements Parcelable { * Sets a user visible description of when this rule will be active * (see {@link Condition#STATE_TRUE}). * - * A description should be a (localized) string like "Mon-Fri, 9pm-7am" or + * <p>A description should be a (localized) string like "Mon-Fri, 9pm-7am" or * "When connected to [Car Name]". */ public @NonNull Builder setTriggerDescription(@Nullable String description) { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a510c7704751..013bcddbb7f3 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2205,9 +2205,6 @@ public class Notification implements Parcelable private void visitUris(@NonNull Consumer<Uri> visitor) { visitIconUri(visitor, getIcon()); - if (actionIntent != null) { - actionIntent.visitUris(visitor); - } } @Override @@ -2901,21 +2898,6 @@ public class Notification implements Parcelable } } - // allPendingIntents should contain all associated intents after parcelling, but it may also - // contain intents added by the app to extras for their own purposes. We only care about - // checking the intents known and used by system_server, to avoid the confused deputy issue. - List<PendingIntent> pendingIntents = Arrays.asList(contentIntent, deleteIntent, - fullScreenIntent); - for (PendingIntent intent : pendingIntents) { - if (intent != null) { - intent.visitUris(visitor); - } - } - - if (mBubbleMetadata != null) { - mBubbleMetadata.visitUris(visitor); - } - if (extras != null) { visitIconUri(visitor, extras.getParcelable(EXTRA_LARGE_ICON_BIG, Icon.class)); visitIconUri(visitor, extras.getParcelable(EXTRA_PICTURE_ICON, Icon.class)); @@ -2987,28 +2969,15 @@ public class Notification implements Parcelable callPerson.visitUris(visitor); } visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class)); + } - // Extras for MediaStyle. - PendingIntent deviceIntent = extras.getParcelable(EXTRA_MEDIA_REMOTE_INTENT, - PendingIntent.class); - if (deviceIntent != null) { - deviceIntent.visitUris(visitor); - } - - if (extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { - WearableExtender extender = new WearableExtender(this); - extender.visitUris(visitor); - } - - if (extras.containsKey(TvExtender.EXTRA_TV_EXTENDER)) { - TvExtender extender = new TvExtender(this); - extender.visitUris(visitor); - } + if (mBubbleMetadata != null) { + visitIconUri(visitor, mBubbleMetadata.getIcon()); + } - if (extras.containsKey(CarExtender.EXTRA_CAR_EXTENDER)) { - CarExtender extender = new CarExtender(this); - extender.visitUris(visitor); - } + if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { + WearableExtender extender = new WearableExtender(this); + extender.visitUris(visitor); } } @@ -10589,16 +10558,6 @@ public class Notification implements Parcelable } } - private void visitUris(@NonNull Consumer<Uri> visitor) { - visitIconUri(visitor, getIcon()); - if (mPendingIntent != null) { - mPendingIntent.visitUris(visitor); - } - if (mDeleteIntent != null) { - mDeleteIntent.visitUris(visitor); - } - } - /** * Builder to construct a {@link BubbleMetadata} object. */ @@ -11797,9 +11756,6 @@ public class Notification implements Parcelable } private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mDisplayIntent != null) { - mDisplayIntent.visitUris(visitor); - } for (Action action : mActions) { action.visitUris(visitor); } @@ -11952,19 +11908,12 @@ public class Notification implements Parcelable /** * Returns the unread conversation conveyed by this notification. - * * @see #setUnreadConversation(UnreadConversation) */ public UnreadConversation getUnreadConversation() { return mUnreadConversation; } - private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mUnreadConversation != null) { - mUnreadConversation.visitUris(visitor); - } - } - /** * A class which holds the unread messages from a conversation. */ @@ -12116,16 +12065,7 @@ public class Notification implements Parcelable onRead, participants, b.getLong(KEY_TIMESTAMP)); } - - private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mReadPendingIntent != null) { - mReadPendingIntent.visitUris(visitor); - } - if (mReplyPendingIntent != null) { - mReplyPendingIntent.visitUris(visitor); - } - } - } + }; /** * Builder class for {@link CarExtender.UnreadConversation} objects. @@ -12448,15 +12388,6 @@ public class Notification implements Parcelable public boolean isSuppressShowOverApps() { return mSuppressShowOverApps; } - - private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mContentIntent != null) { - mContentIntent.visitUris(visitor); - } - if (mDeleteIntent != null) { - mDeleteIntent.visitUris(visitor); - } - } } /** diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 0261f0a02174..62209b0fd27d 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -44,8 +44,6 @@ import android.content.IntentSender; import android.content.pm.PackageManager.ResolveInfoFlagsBits; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -71,7 +69,6 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; -import java.util.function.Consumer; /** * A description of an Intent and target action to perform with it. Instances @@ -1463,21 +1460,6 @@ public final class PendingIntent implements Parcelable { return sb.toString(); } - /** - * See {@link Intent#visitUris(Consumer)}. - * - * @hide - */ - public void visitUris(@NonNull Consumer<Uri> visitor) { - if (android.app.Flags.visitRiskyUris()) { - Intent intent = Binder.withCleanCallingIdentity(this::getIntent); - - if (intent != null) { - intent.visitUris(visitor); - } - } - } - /** @hide */ public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 90a265937082..4a6349b1b02f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9381,7 +9381,7 @@ public class DevicePolicyManager { @Deprecated @SystemApi @RequiresPermission(MANAGE_DEVICE_ADMINS) - public boolean setActiveProfileOwner(@NonNull ComponentName admin, @Deprecated String ownerName) + public boolean setActiveProfileOwner(@NonNull ComponentName admin, String ownerName) throws IllegalArgumentException { throwIfParentInstance("setActiveProfileOwner"); if (mService != null) { diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 6e451479c5a4..4cf9fcab9092 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -16,6 +16,8 @@ package android.appwidget; +import static android.appwidget.flags.Flags.remoteAdapterConversion; + import android.annotation.BroadcastBehavior; import android.annotation.NonNull; import android.annotation.Nullable; @@ -566,11 +568,9 @@ public class AppWidgetManager { private void tryAdapterConversion( FunctionalUtils.RemoteExceptionIgnoringConsumer<RemoteViews> action, RemoteViews original, String failureMsg) { - final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() + if (remoteAdapterConversion() && (mHasPostedLegacyLists = mHasPostedLegacyLists - || (original != null && original.hasLegacyLists())); - - if (isConvertingAdapter) { + || (original != null && original.hasLegacyLists()))) { final RemoteViews viewsCopy = new RemoteViews(original); Runnable updateWidgetWithTask = () -> { try { @@ -587,13 +587,12 @@ public class AppWidgetManager { } updateWidgetWithTask.run(); - return; - } - - try { - action.acceptOrThrow(original); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); + } else { + try { + action.acceptOrThrow(original); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } } } @@ -838,22 +837,20 @@ public class AppWidgetManager { return; } - if (!RemoteViews.isAdapterConversionEnabled()) { + if (remoteAdapterConversion()) { + if (Looper.myLooper() == Looper.getMainLooper()) { + mHasPostedLegacyLists = true; + createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange( + appWidgetIds, viewId)); + } else { + notifyCollectionWidgetChange(appWidgetIds, viewId); + } + } else { try { mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } - - return; - } - - if (Looper.myLooper() == Looper.getMainLooper()) { - mHasPostedLegacyLists = true; - createUpdateExecutorIfNull().execute(() -> notifyCollectionWidgetChange(appWidgetIds, - viewId)); - } else { - notifyCollectionWidgetChange(appWidgetIds, viewId); } } diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig index 6a735a418b58..c95b864c08bb 100644 --- a/core/java/android/appwidget/flags.aconfig +++ b/core/java/android/appwidget/flags.aconfig @@ -6,3 +6,10 @@ flag { description: "Enable support for generated previews in AppWidgetManager" bug: "306546610" } + +flag { + name: "remote_adapter_conversion" + namespace: "app_widgets" + description: "Enable adapter conversion to RemoteCollectionItemsAdapter" + bug: "245950570" +}
\ No newline at end of file diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index e4a03c596254..d5b5f40a6980 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -37,6 +37,7 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.ActivityOptions; import android.app.NotificationManager; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; @@ -709,7 +710,9 @@ public final class CompanionDeviceManager { IntentSender intentSender = mService .requestNotificationAccess(component, mContext.getUserId()) .getIntentSender(); - mContext.startIntentSender(intentSender, null, 0, 0, 0); + mContext.startIntentSender(intentSender, null, 0, 0, 0, + ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (IntentSender.SendIntentException e) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 183b9b0000d2..7af0be3b3e75 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -101,7 +101,6 @@ import java.util.Locale; import java.util.Objects; import java.util.Set; import java.util.TimeZone; -import java.util.function.Consumer; /** * An intent is an abstract description of an operation to be performed. It @@ -8148,27 +8147,6 @@ public class Intent implements Parcelable, Cloneable { } } - /** - * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission - * grants will need to be issued to ensure the recipient of this object is able to render its - * contents. - * See b/281044385 for more context and examples about what happens when this isn't done - * correctly. - * - * @hide - */ - public void visitUris(@NonNull Consumer<Uri> visitor) { - if (android.app.Flags.visitRiskyUris()) { - visitor.accept(mData); - if (mSelector != null) { - mSelector.visitUris(visitor); - } - if (mOriginalIntent != null) { - mOriginalIntent.visitUris(visitor); - } - } - } - public static Intent getIntentOld(String uri) throws URISyntaxException { Intent intent = getIntentOld(uri, 0); intent.mLocalFlags |= LOCAL_FLAG_FROM_URI; diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index e9f419e9a8ce..6f7299aa8e31 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -64,6 +64,7 @@ interface ILauncherApps { PendingIntent getActivityLaunchIntent(String callingPackage, in ComponentName component, in UserHandle user); LauncherUserInfo getLauncherUserInfo(in UserHandle user); + List<String> getPreInstalledSystemPackages(in UserHandle user); void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage, String callingFeatureId, in ComponentName component, in Rect sourceBounds, in Bundle opts, in UserHandle user); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 0cd4358b2c91..ccc8f0956865 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -800,6 +800,29 @@ public class LauncherApps { } /** + * Returns the list of the system packages that are installed at user creation. + * + * <p>An empty list denotes that all system packages are installed for that user at creation. + * This behaviour is inherited from the underlining UserManager API. + * + * @param userHandle the user for which installed system packages are required. + * @return {@link List} of {@link String}, representing the package name of the installed + * package. Can be empty but not null. + * @hide + */ + @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) + public List<String> getPreInstalledSystemPackages(@NonNull UserHandle userHandle) { + if (DEBUG) { + Log.i(TAG, "getPreInstalledSystemPackages for user: " + userHandle); + } + try { + return mService.getPreInstalledSystemPackages(userHandle); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it * returns null. * diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 5dee65b62201..4f61613b9c6e 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -231,7 +231,7 @@ public class PackageInfo implements Parcelable { * or null if there were none. This is only filled in if the flag * {@link PackageManager#GET_PERMISSIONS} was set. Each value matches * the corresponding entry in {@link #requestedPermissions}, and will have - * the flags {@link #REQUESTED_PERMISSION_GRANTED} and + * the flags {@link #REQUESTED_PERMISSION_GRANTED}, {@link #REQUESTED_PERMISSION_IMPLICIT}, and * {@link #REQUESTED_PERMISSION_NEVER_FOR_LOCATION} set as appropriate. */ @Nullable diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e2243292ab8d..a5d16f2f6be1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1225,12 +1225,10 @@ public abstract class PackageManager { public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO; /** - * @deprecated Use {@link #MATCH_CLONE_PROFILE_LONG} instead. + * Use {@link #MATCH_CLONE_PROFILE_LONG} instead. * * @hide */ - @SuppressLint("UnflaggedApi") // Just adding the @Deprecated annotation - @Deprecated @SystemApi public static final int MATCH_CLONE_PROFILE = 0x20000000; @@ -6302,6 +6300,11 @@ public abstract class PackageManager { /** * Check whether a particular package has been granted a particular * permission. + * <p> + * <strong>Note: </strong>This API returns the underlying permission state + * as-is and is mostly intended for permission managing system apps. To + * perform an access check for a certain app, please use the + * {@link Context#checkPermission} APIs instead. * * @param permName The name of the permission you are checking for. * @param packageName The name of the package you are checking against. diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 6c6b33b8d716..57025c25f97b 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -49,3 +49,10 @@ flag { description: "Add support to unlock the private space using biometrics" bug: "312184187" } + +flag { + name: "support_autolock_for_private_space" + namespace: "profile_experiences" + description: "Add support to lock private space automatically after a time period" + bug: "303201022" +} diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index 8196bf505e02..20b09326e9c0 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -40,7 +40,6 @@ import android.os.ConditionVariable; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; -import android.util.FeatureFlagUtils; import android.util.IntArray; import android.util.Log; import android.util.Pair; @@ -722,6 +721,7 @@ public final class CameraExtensionCharacteristics { switch(format) { case ImageFormat.YUV_420_888: case ImageFormat.JPEG: + case ImageFormat.JPEG_R: break; default: throw new IllegalArgumentException("Unsupported format: " + format); diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 93fbe8aee8d4..7cf10d89004f 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -1216,7 +1216,7 @@ public abstract class CameraMetadata<TKey> { * <ul> * <li>Profile {@link android.hardware.camera2.params.DynamicRangeProfiles#HLG10 }</li> * <li>All mandatory stream combinations for this specific capability as per - * <a href="CameraDevice#10-bit-output-additional-guaranteed-configurations">documentation</a></li> + * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#10-bit-output-additional-guaranteed-configurations">documentation</a></li> * <li>In case the device is not able to capture some combination of supported * standard 8-bit and/or 10-bit dynamic range profiles within the same capture request, * then those constraints must be listed in diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 06397c9a1598..ded96a23e11e 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -1484,7 +1484,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>To start a CaptureSession with a target FPS range different from the * capture request template's default value, the application * is strongly recommended to call - * {@link SessionConfiguration#setSessionParameters } + * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters } * with the target fps range before creating the capture session. The aeTargetFpsRange is * typically a session parameter. Specifying it at session creation time helps avoid * session reconfiguration delays in cases like 60fps or high speed recording.</p> @@ -2161,7 +2161,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * OFF if the recording output is not stabilized, or if there are no output * Surface types that can be stabilized.</p> * <p>The application is strongly recommended to call - * {@link SessionConfiguration#setSessionParameters } + * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters } * with the desired video stabilization mode before creating the capture session. * Video stabilization mode is a session parameter on many devices. Specifying * it at session creation time helps avoid reconfiguration delay caused by difference diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index ab4406c37c8e..1d26d69a58b9 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -899,7 +899,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>To start a CaptureSession with a target FPS range different from the * capture request template's default value, the application * is strongly recommended to call - * {@link SessionConfiguration#setSessionParameters } + * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters } * with the target fps range before creating the capture session. The aeTargetFpsRange is * typically a session parameter. Specifying it at session creation time helps avoid * session reconfiguration delays in cases like 60fps or high speed recording.</p> @@ -2382,7 +2382,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * OFF if the recording output is not stabilized, or if there are no output * Surface types that can be stabilized.</p> * <p>The application is strongly recommended to call - * {@link SessionConfiguration#setSessionParameters } + * {@link android.hardware.camera2.params.SessionConfiguration#setSessionParameters } * with the desired video stabilization mode before creating the capture session. * Video stabilization mode is a session parameter on many devices. Specifying * it at session creation time helps avoid reconfiguration delay caused by difference diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 16ffaef03121..10a8022df0ea 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1657,6 +1657,7 @@ public abstract class BatteryStats { */ public abstract CpuScalingPolicies getCpuScalingPolicies(); + @android.ravenwood.annotation.RavenwoodKeepWholeClass public final static class HistoryTag { public static final int HISTORY_TAG_POOL_OVERFLOW = -1; @@ -1713,6 +1714,7 @@ public abstract class BatteryStats { * Optional detailed information that can go into a history step. This is typically * generated each time the battery level changes. */ + @android.ravenwood.annotation.RavenwoodKeepWholeClass public final static class HistoryStepDetails { // Time (in 1/100 second) spent in user space and the kernel since the last step. public int userTime; @@ -1797,6 +1799,7 @@ public abstract class BatteryStats { /** * An extension to the history item describing a proc state change for a UID. */ + @android.ravenwood.annotation.RavenwoodKeepWholeClass public static final class ProcessStateChange { public int uid; public @BatteryConsumer.ProcessState int processState; @@ -1850,6 +1853,7 @@ public abstract class BatteryStats { /** * Battery history record. */ + @android.ravenwood.annotation.RavenwoodKeepWholeClass public static final class HistoryItem { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public HistoryItem next; diff --git a/core/java/android/os/ConditionVariable.java b/core/java/android/os/ConditionVariable.java index a13eaa6f19bf..b5ed53bf0e5e 100644 --- a/core/java/android/os/ConditionVariable.java +++ b/core/java/android/os/ConditionVariable.java @@ -29,6 +29,7 @@ package android.os; * This class uses itself as the object to wait on, so if you wait() * or notify() on a ConditionVariable, the results are undefined. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class ConditionVariable { private volatile boolean mCondition; diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index f2930fe45295..8e860c35388d 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -465,9 +465,7 @@ public final class Parcel { private static native byte[] nativeMarshall(long nativePtr); private static native void nativeUnmarshall( long nativePtr, byte[] data, int offset, int length); - @RavenwoodThrow private static native int nativeCompareData(long thisNativePtr, long otherNativePtr); - @RavenwoodThrow private static native boolean nativeCompareDataInRange( long ptrA, int offsetA, long ptrB, int offsetB, int length); private static native void nativeAppendFrom( diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index 145981c92283..83d237d6e53b 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -78,6 +78,13 @@ flag { } flag { + name: "adpf_use_fmq_channel" + namespace: "game" + description: "Guards use of the FMQ channel for ADPF" + bug: "315894228" +} + +flag { name: "battery_service_support_current_adb_command" namespace: "backstage_power" description: "Whether or not BatteryService supports adb commands for Current values." diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig index 69d86a6604ad..437668c9a7de 100644 --- a/core/java/android/os/vibrator/flags.aconfig +++ b/core/java/android/os/vibrator/flags.aconfig @@ -44,3 +44,10 @@ flag { description: "Enables the independent keyboard vibration settings feature" bug: "289107579" } + +flag { + namespace: "haptics" + name: "adaptive_haptics_enabled" + description: "Enables the adaptive haptics feature" + bug: "305961689" +} diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java index 531626b1e85f..6e771f8f0ffe 100644 --- a/core/java/android/service/notification/Condition.java +++ b/core/java/android/service/notification/Condition.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2014, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +16,8 @@ package android.service.notification; +import static com.android.internal.util.Preconditions.checkArgument; + import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -117,7 +119,7 @@ public final class Condition implements Parcelable { /** The source of, or reason for, the state change represented by this Condition. **/ @FlaggedApi(Flags.FLAG_MODES_API) - public final @Source int source; + public final @Source int source; // default = SOURCE_UNKNOWN /** * The maximum string length for any string contained in this condition. @@ -179,7 +181,7 @@ public final class Condition implements Parcelable { this.line2 = getTrimmedString(line2); this.icon = icon; this.state = state; - this.source = source; + this.source = checkValidSource(source); this.flags = flags; } @@ -197,10 +199,26 @@ public final class Condition implements Parcelable { source.readInt()); } + /** @hide */ + public void validate() { + if (Flags.modesApi()) { + checkValidSource(source); + } + } + private static boolean isValidState(int state) { return state >= STATE_FALSE && state <= STATE_ERROR; } + private static int checkValidSource(@Source int source) { + if (Flags.modesApi()) { + checkArgument(source >= SOURCE_UNKNOWN && source <= SOURCE_CONTEXT, + "Condition source must be one of SOURCE_UNKNOWN, SOURCE_USER_ACTION, " + + "SOURCE_SCHEDULE, or SOURCE_CONTEXT"); + } + return source; + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(id, 0); diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index a5b087c05dfa..fcdc5fe71e4e 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -1980,6 +1980,7 @@ public class ZenModeConfig implements Parcelable { @Nullable public ZenDeviceEffects zenDeviceEffects; public boolean modified; // rule has been modified from initial creation public String pkg; + @AutomaticZenRule.Type public int type = AutomaticZenRule.TYPE_UNKNOWN; public String triggerDescription; public String iconResName; diff --git a/core/java/android/service/persistentdata/OWNERS b/core/java/android/service/persistentdata/OWNERS new file mode 100644 index 000000000000..6dfb888dedad --- /dev/null +++ b/core/java/android/service/persistentdata/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/pdb/OWNERS diff --git a/core/java/android/service/voice/HotwordRejectedResult.java b/core/java/android/service/voice/HotwordRejectedResult.java index 26c1ca428c3b..eb1ac67719ed 100644 --- a/core/java/android/service/voice/HotwordRejectedResult.java +++ b/core/java/android/service/voice/HotwordRejectedResult.java @@ -16,9 +16,11 @@ package android.service.voice; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.SystemApi; import android.os.Parcelable; +import android.service.voice.flags.Flags; import com.android.internal.util.DataClass; @@ -53,12 +55,17 @@ public final class HotwordRejectedResult implements Parcelable { /** High confidence in hotword detector result. */ public static final int CONFIDENCE_LEVEL_HIGH = 3; + /** Very high confidence in hotword detector result. **/ + @FlaggedApi(Flags.FLAG_ALLOW_HOTWORD_BUMP_EGRESS) + public static final int CONFIDENCE_LEVEL_VERY_HIGH = 4; + /** @hide */ @IntDef(prefix = {"CONFIDENCE_LEVEL_"}, value = { CONFIDENCE_LEVEL_NONE, CONFIDENCE_LEVEL_LOW, CONFIDENCE_LEVEL_MEDIUM, - CONFIDENCE_LEVEL_HIGH + CONFIDENCE_LEVEL_HIGH, + CONFIDENCE_LEVEL_VERY_HIGH }) @Retention(RetentionPolicy.SOURCE) @interface HotwordConfidenceLevelValue { @@ -91,9 +98,10 @@ public final class HotwordRejectedResult implements Parcelable { CONFIDENCE_LEVEL_NONE, CONFIDENCE_LEVEL_LOW, CONFIDENCE_LEVEL_MEDIUM, - CONFIDENCE_LEVEL_HIGH + CONFIDENCE_LEVEL_HIGH, + CONFIDENCE_LEVEL_VERY_HIGH }) - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member public @interface ConfidenceLevel {} @@ -109,6 +117,8 @@ public final class HotwordRejectedResult implements Parcelable { return "CONFIDENCE_LEVEL_MEDIUM"; case CONFIDENCE_LEVEL_HIGH: return "CONFIDENCE_LEVEL_HIGH"; + case CONFIDENCE_LEVEL_VERY_HIGH: + return "CONFIDENCE_LEVEL_VERY_HIGH"; default: return Integer.toHexString(value); } } @@ -259,10 +269,10 @@ public final class HotwordRejectedResult implements Parcelable { } @DataClass.Generated( - time = 1621961370106L, + time = 1701990933632L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/voice/HotwordRejectedResult.java", - inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_HIGH\nprivate final @android.service.voice.HotwordRejectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate static int defaultConfidenceLevel()\nclass HotwordRejectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)") + inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final @android.annotation.FlaggedApi int CONFIDENCE_LEVEL_VERY_HIGH\nprivate final @android.service.voice.HotwordRejectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate static int defaultConfidenceLevel()\nclass HotwordRejectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index fba09233e4f4..75ab48a43da6 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -1042,13 +1042,13 @@ public class VoiceInteractionService extends Service { @SystemApi @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS) @RequiresPermission(Manifest.permission.MANAGE_HOTWORD_DETECTION) - public void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed) { - Log.i(TAG, "setIsReceiveSandboxedTrainingDataAllowed to " + allowed); + public void setShouldReceiveSandboxedTrainingData(boolean allowed) { + Log.i(TAG, "setShouldReceiveSandboxedTrainingData to " + allowed); if (mSystemService == null) { throw new IllegalStateException("Not available until onReady() is called"); } try { - mSystemService.setIsReceiveSandboxedTrainingDataAllowed(allowed); + mSystemService.setShouldReceiveSandboxedTrainingData(allowed); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/service/voice/flags/flags.aconfig b/core/java/android/service/voice/flags/flags.aconfig index c414ef8a6826..b596666bf607 100644 --- a/core/java/android/service/voice/flags/flags.aconfig +++ b/core/java/android/service/voice/flags/flags.aconfig @@ -6,3 +6,10 @@ flag { description: "This flag allows the hotword detection service to egress training data to the default assistant." bug: "296074924" } + +flag { + name: "allow_hotword_bump_egress" + namespace: "machine_learning" + description: "This flag allows hotword detection service to egress reason code for hotword bump." + bug: "290951024" +} diff --git a/core/java/android/view/ISurfaceControlViewHostParent.aidl b/core/java/android/view/ISurfaceControlViewHostParent.aidl index f42e00148587..559c20ee4825 100644 --- a/core/java/android/view/ISurfaceControlViewHostParent.aidl +++ b/core/java/android/view/ISurfaceControlViewHostParent.aidl @@ -16,6 +16,7 @@ package android.view; +import android.view.KeyEvent; import android.view.WindowManager; /** @@ -24,4 +25,6 @@ import android.view.WindowManager; */ oneway interface ISurfaceControlViewHostParent { void updateParams(in WindowManager.LayoutParams[] childAttrs); + // To forward the back key event from embedded to host app. + void forwardBackKeyToParent(in KeyEvent keyEvent); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index dbacca5def51..9bf43a390d70 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -162,6 +162,8 @@ interface IWindowSession { * @param flags See {@code View#startDragAndDrop} * @param surface Surface containing drag shadow image * @param touchSource See {@code InputDevice#getSource()} + * @param touchDeviceId device ID of last touch event + * @param pointerId pointer ID of last touch event * @param touchX X coordinate of last touch point * @param touchY Y coordinate of last touch point * @param thumbCenterX X coordinate for the position within the shadow image that should be @@ -171,9 +173,9 @@ interface IWindowSession { * @param data Data transferred by drag and drop * @return Token of drag operation which will be passed to cancelDragAndDrop. */ - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) IBinder performDrag(IWindow window, int flags, in SurfaceControl surface, int touchSource, - float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data); + int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX, + float thumbCenterY, in ClipData data); /** * Drops the content of the current drag operation for accessibility diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 405653123f79..4840f003da3e 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -447,6 +447,7 @@ public class SurfaceControlViewHost { addWindowToken(attrs); view.setLayoutParams(attrs); mViewRoot.setView(view, attrs, null); + mViewRoot.setBackKeyCallbackForWindowlessWindow(mWm::forwardBackKeyToParent); } /** diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index a44a95a1677f..108de281a411 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -37,6 +37,7 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; import android.graphics.RenderNode; +import android.hardware.input.InputManager; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -159,6 +160,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private static final boolean DEBUG = false; private static final boolean DEBUG_POSITION = false; + private static final long FORWARD_BACK_KEY_TOLERANCE_MS = 100; + @UnsupportedAppUsage( maxTargetSdk = Build.VERSION_CODES.TIRAMISU, publicAlternatives = "Track {@link SurfaceHolder#addCallback} instead") @@ -326,6 +329,41 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall }); } } + + @Override + public void forwardBackKeyToParent(@NonNull KeyEvent keyEvent) { + runOnUiThread(() -> { + if (!isAttachedToWindow() || keyEvent.getKeyCode() != KeyEvent.KEYCODE_BACK) { + return; + } + final ViewRootImpl vri = getViewRootImpl(); + if (vri == null) { + return; + } + final InputManager inputManager = mContext.getSystemService(InputManager.class); + if (inputManager == null) { + return; + } + // Check that the event was created recently. + final long timeDiff = SystemClock.uptimeMillis() - keyEvent.getEventTime(); + if (timeDiff > FORWARD_BACK_KEY_TOLERANCE_MS) { + Log.e(TAG, "Ignore the input event that exceed the tolerance time, " + + "exceed " + timeDiff + "ms"); + return; + } + if (inputManager.verifyInputEvent(keyEvent) == null) { + Log.e(TAG, "Received invalid input event"); + return; + } + try { + vri.processingBackKey(true); + vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */, + true /* processImmediately */); + } finally { + vri.processingBackKey(false); + } + }); + } }; private final boolean mRtDrivenClipping = Flags.clipSurfaceviews(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 75f8eba01fa2..bb5ee0359b6b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -28340,6 +28340,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, IBinder token = mAttachInfo.mSession.performDrag( mAttachInfo.mWindow, flags, null, mAttachInfo.mViewRootImpl.getLastTouchSource(), + mAttachInfo.mViewRootImpl.getLastTouchDeviceId(), + mAttachInfo.mViewRootImpl.getLastTouchPointerId(), 0f, 0f, 0f, 0f, data); if (ViewDebug.DEBUG_DRAG) { Log.d(VIEW_LOG_TAG, "startDragAndDrop via a11y action returned " + token); @@ -28414,7 +28416,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl, - root.getLastTouchSource(), lastTouchPoint.x, lastTouchPoint.y, + root.getLastTouchSource(), root.getLastTouchDeviceId(), + root.getLastTouchPointerId(), lastTouchPoint.x, lastTouchPoint.y, shadowTouchPoint.x, shadowTouchPoint.y, data); if (ViewDebug.DEBUG_DRAG) { Log.d(VIEW_LOG_TAG, "performDrag returned " + token); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5cbb42e0e346..e83488e2689e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -76,13 +76,8 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_E import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; -import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -96,6 +91,7 @@ import static android.view.accessibility.Flags.reduceWindowContentChangedEventTh import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER; import static android.view.flags.Flags.toolkitSetFrameRateReadOnly; +import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision; import static com.android.input.flags.Flags.enablePointerChoreographer; @@ -255,7 +251,7 @@ import java.util.OptionalInt; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; -import java.util.function.Consumer; +import java.util.function.Predicate; /** * The top of a view hierarchy, implementing the needed protocol between View @@ -375,6 +371,8 @@ public final class ViewRootImpl implements ViewParent, */ private static final int KEEP_CLEAR_AREA_REPORT_RATE_MILLIS = 100; + private static final long NANOS_PER_SEC = 1000000000; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); @@ -620,6 +618,13 @@ public final class ViewRootImpl implements ViewParent, boolean mUpcomingWindowFocus; @GuardedBy("this") boolean mUpcomingInTouchMode; + // While set, allow this VRI to handle back key without drop it. + private boolean mProcessingBackKey; + /** + * Compatibility {@link OnBackInvokedCallback} for windowless window, to forward the back + * key event host app. + */ + private Predicate<KeyEvent> mWindowlessBackKeyCallback; public boolean mTraversalScheduled; int mTraversalBarrier; @@ -808,6 +813,8 @@ public final class ViewRootImpl implements ViewParent, final PointF mDragPoint = new PointF(); final PointF mLastTouchPoint = new PointF(); int mLastTouchSource; + int mLastTouchDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; + int mLastTouchPointerId; /** Tracks last {@link MotionEvent#getToolType(int)} with {@link MotionEvent#ACTION_UP}. **/ private int mLastClickToolType; @@ -822,6 +829,8 @@ public final class ViewRootImpl implements ViewParent, private boolean mInsetsAnimationRunning; + private long mPreviousFrameDrawnTime = -1; + /** * The resolved pointer icon type requested by this window. * A null value indicates the resolved pointer icon has not yet been calculated. @@ -1059,11 +1068,14 @@ public final class ViewRootImpl implements ViewParent, private boolean mChildBoundingInsetsChanged = false; private String mTag = TAG; + private String mFpsTraceName; private static boolean sToolkitSetFrameRateReadOnlyFlagValue; + private static boolean sToolkitMetricsForFrameRateDecisionFlagValue; static { sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly(); + sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision(); } // The latest input event from the gesture that was used to resolve the pointer icon. @@ -1307,6 +1319,7 @@ public final class ViewRootImpl implements ViewParent, attrs = mWindowAttributes; setTag(); + mFpsTraceName = "FPS of " + getTitle(); if (DEBUG_KEEP_SCREEN_ON && (mClientWindowLayoutFlags & WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0 @@ -3194,7 +3207,11 @@ public final class ViewRootImpl implements ViewParent, host.dispatchAttachedToWindow(mAttachInfo, 0); mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true); dispatchApplyInsets(host); - if (!mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) { + if (!mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled() + // Don't register compat OnBackInvokedCallback for windowless window. + // The onBackInvoked event by default should forward to host app, so the + // host app can decide the behavior. + && mWindowlessBackKeyCallback == null) { // For apps requesting legacy back behavior, we add a compat callback that // dispatches {@link KeyEvent#KEYCODE_BACK} to their root views. // This way from system point of view, these apps are providing custom @@ -4726,6 +4743,31 @@ public final class ViewRootImpl implements ViewParent, } } + /** + * Called from draw() to collect metrics for frame rate decision. + */ + private void collectFrameRateDecisionMetrics() { + if (!Trace.isEnabled()) { + if (mPreviousFrameDrawnTime > 0) mPreviousFrameDrawnTime = -1; + return; + } + + if (mPreviousFrameDrawnTime < 0) { + mPreviousFrameDrawnTime = mChoreographer.getExpectedPresentationTimeNanos(); + return; + } + + long expectedDrawnTime = mChoreographer.getExpectedPresentationTimeNanos(); + long timeDiff = expectedDrawnTime - mPreviousFrameDrawnTime; + if (timeDiff <= 0) { + return; + } + + long fps = NANOS_PER_SEC / timeDiff; + Trace.setCounter(mFpsTraceName, fps); + mPreviousFrameDrawnTime = expectedDrawnTime; + } + private void reportDrawFinished(@Nullable Transaction t, int seqId) { if (DEBUG_BLAST) { Log.d(mTag, "reportDrawFinished"); @@ -5044,6 +5086,9 @@ public final class ViewRootImpl implements ViewParent, if (DEBUG_FPS) { trackFPS(); } + if (sToolkitMetricsForFrameRateDecisionFlagValue) { + collectFrameRateDecisionMetrics(); + } if (!sFirstDrawComplete) { synchronized (sFirstDrawHandlers) { @@ -6660,7 +6705,8 @@ public final class ViewRootImpl implements ViewParent, // Find a reason for dropping or canceling the event. final String reason; - if (!mAttachInfo.mHasWindowFocus + // The embedded window is focused, allow this VRI to handle back key. + if (!mAttachInfo.mHasWindowFocus && !(mProcessingBackKey && isBack(q.mEvent)) && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) && !isAutofillUiShowing()) { // This is a non-pointer event and the window doesn't currently have input focus @@ -6883,10 +6929,20 @@ public final class ViewRootImpl implements ViewParent, // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the // view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}. - if (isBack(keyEvent) - && mContext != null - && mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) { - return doOnBackKeyEvent(keyEvent); + if (isBack(keyEvent)) { + if (mWindowlessBackKeyCallback != null) { + if (mWindowlessBackKeyCallback.test(keyEvent)) { + return keyEvent.getAction() == KeyEvent.ACTION_UP + && !keyEvent.isCanceled() + ? FINISH_HANDLED : FINISH_NOT_HANDLED; + } else { + // Unable to forward the back key to host, forward to next stage. + return FORWARD; + } + } else if (mContext != null + && mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) { + return doOnBackKeyEvent(keyEvent); + } } if (mInputQueue != null) { @@ -7109,6 +7165,8 @@ public final class ViewRootImpl implements ViewParent, mLastTouchPoint.x = event.getRawX(); mLastTouchPoint.y = event.getRawY(); mLastTouchSource = event.getSource(); + mLastTouchDeviceId = event.getDeviceId(); + mLastTouchPointerId = event.getPointerId(0); // Register last ACTION_UP. This will be propagated to IME. if (event.getActionMasked() == MotionEvent.ACTION_UP) { @@ -8520,6 +8578,14 @@ public final class ViewRootImpl implements ViewParent, return mLastTouchSource; } + public int getLastTouchDeviceId() { + return mLastTouchDeviceId; + } + + public int getLastTouchPointerId() { + return mLastTouchPointerId; + } + /** * Used by InputMethodManager. * @hide @@ -10529,6 +10595,11 @@ public final class ViewRootImpl implements ViewParent, mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget(); } + // Make this VRI able to process back key without drop it. + void processingBackKey(boolean processing) { + mProcessingBackKey = processing; + } + /** * Collect and include any ScrollCaptureCallback instances registered with the window. * @@ -11753,13 +11824,18 @@ public final class ViewRootImpl implements ViewParent, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, InputDevice.SOURCE_KEYBOARD); - enqueueInputEvent(ev); + enqueueInputEvent(ev, null /* receiver */, 0 /* flags */, true /* processImmediately */); } private void registerCompatOnBackInvokedCallback() { mCompatOnBackInvokedCallback = () -> { - sendBackKeyEvent(KeyEvent.ACTION_DOWN); - sendBackKeyEvent(KeyEvent.ACTION_UP); + try { + processingBackKey(true); + sendBackKeyEvent(KeyEvent.ACTION_DOWN); + sendBackKeyEvent(KeyEvent.ACTION_UP); + } finally { + processingBackKey(false); + } }; if (mOnBackInvokedDispatcher.hasImeOnBackInvokedDispatcher()) { Log.d(TAG, "Skip registering CompatOnBackInvokedCallback on IME dispatcher"); @@ -12097,11 +12173,9 @@ public final class ViewRootImpl implements ViewParent, boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN || motionEventAction == MotionEvent.ACTION_MOVE || motionEventAction == MotionEvent.ACTION_UP; - boolean desiredType = windowType == TYPE_BASE_APPLICATION || windowType == TYPE_APPLICATION - || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION - || windowType == TYPE_NOTIFICATION_SHADE || windowType == TYPE_STATUS_BAR; + boolean undesiredType = windowType == TYPE_INPUT_METHOD; // use toolkitSetFrameRate flag to gate the change - return desiredAction && desiredType && sToolkitSetFrameRateReadOnlyFlagValue; + return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue; } /** @@ -12196,4 +12270,13 @@ public final class ViewRootImpl implements ViewParent, } return false; } + + /** + * Set the default back key callback for windowless window, to forward the back key event + * to host app. + * MUST NOT call this method for normal window. + */ + void setBackKeyCallbackForWindowlessWindow(@NonNull Predicate<KeyEvent> callback) { + mWindowlessBackKeyCallback = callback; + } } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index d817e6f51f55..d6ac56239aed 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.content.res.Configuration; @@ -488,8 +489,9 @@ public class WindowlessWindowManager implements IWindowSession { @Override public android.os.IBinder performDrag(android.view.IWindow window, int flags, - android.view.SurfaceControl surface, int touchSource, float touchX, float touchY, - float thumbCenterX, float thumbCenterY, android.content.ClipData data) { + android.view.SurfaceControl surface, int touchSource, int touchDeviceId, + int touchPointerId, float touchX, float touchY, float thumbCenterX, float thumbCenterY, + android.content.ClipData data) { return null; } @@ -703,4 +705,17 @@ public class WindowlessWindowManager implements IWindowSession { } } } + + boolean forwardBackKeyToParent(@NonNull KeyEvent keyEvent) { + if (mParentInterface == null) { + return false; + } + try { + mParentInterface.forwardBackKeyToParent(keyEvent); + } catch (RemoteException e) { + Log.e(TAG, "Failed to forward back key To Parent: ", e); + return false; + } + return true; + } } diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index e057660961f6..0cc19fb70fbc 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -59,6 +59,13 @@ flag { } flag { + name: "motion_event_observing" + namespace: "accessibility" + description: "Allows accessibility services to intercept but not consume motion events from specified sources." + bug: "297595990" +} + +flag { namespace: "accessibility" name: "granular_scrolling" description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen" diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig index a467afe5d06a..0aa516e08697 100644 --- a/core/java/android/view/flags/refresh_rate_flags.aconfig +++ b/core/java/android/view/flags/refresh_rate_flags.aconfig @@ -42,4 +42,12 @@ flag { namespace: "core_graphics" description: "Enable the `setFrameRate` callback" bug: "299946220" +} + +flag { + name: "toolkit_metrics_for_frame_rate_decision" + namespace: "toolkit" + description: "Feature flag for toolkit metrics collecting for frame rate decision" + bug: "301343249" + is_fixed_read_only: true }
\ No newline at end of file diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index a31a610ab523..8ad10af7250a 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -16,6 +16,7 @@ package android.widget; +import static android.appwidget.flags.Flags.remoteAdapterConversion; import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR; import android.annotation.AttrRes; @@ -36,7 +37,6 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityOptions; import android.app.ActivityThread; -import android.app.AppGlobals; import android.app.Application; import android.app.LoadedApk; import android.app.PendingIntent; @@ -108,7 +108,6 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.CompoundButton.OnCheckedChangeListener; import com.android.internal.R; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.util.Preconditions; import com.android.internal.widget.IRemoteViewsFactory; @@ -1015,11 +1014,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_PENDING_INTENT_TEMPLATE_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mPendingIntentTemplate.visitUris(visitor); - } } /** @@ -1434,7 +1428,9 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - mIntent.visitUris(visitor); + // TODO(b/281044385): Maybe visit intent URIs. This may require adding a dedicated + // visitUris method in the Intent class, since it can contain other intents. Otherwise, + // the basic thing to do here would be just visitor.accept(intent.getData()). } } @@ -1514,7 +1510,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - mResponse.visitUris(visitor); + // TODO(b/281044385): Maybe visit intent URIs in the RemoteResponse. } } @@ -1563,11 +1559,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mPendingIntent.visitUris(visitor); - } } /** @@ -1641,7 +1632,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - mResponse.visitUris(visitor); + // TODO(b/281044385): Maybe visit intent URIs in the RemoteResponse. } } @@ -2202,10 +2193,6 @@ public class RemoteViews implements Parcelable, Filter { final Icon icon = (Icon) getParameterValue(null); if (icon != null) visitIconUri(icon, visitor); break; - case INTENT: - final Intent intent = (Intent) getParameterValue(null); - if (intent != null) intent.visitUris(visitor); - break; // TODO(b/281044385): Should we do anything about type BUNDLE? } } @@ -4962,21 +4949,11 @@ public class RemoteViews implements Parcelable, Filter { */ @Deprecated public void setRemoteAdapter(@IdRes int viewId, Intent intent) { - if (isAdapterConversionEnabled()) { + if (remoteAdapterConversion()) { addAction(new SetRemoteCollectionItemListAdapterAction(viewId, intent)); - return; + } else { + addAction(new SetRemoteViewsAdapterIntent(viewId, intent)); } - addAction(new SetRemoteViewsAdapterIntent(viewId, intent)); - } - - /** - * @hide - * @return True if the remote adapter conversion is enabled - */ - public static boolean isAdapterConversionEnabled() { - return AppGlobals.getIntCoreSetting( - SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION, - SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT ? 1 : 0) != 0; } /** @@ -6995,20 +6972,6 @@ public class RemoteViews implements Parcelable, Filter { mElementNames = parcel.createStringArrayList(); } - /** - * See {@link RemoteViews#visitUris(Consumer)}. - * - * @hide - */ - public void visitUris(@NonNull Consumer<Uri> visitor) { - if (mPendingIntent != null) { - mPendingIntent.visitUris(visitor); - } - if (mFillIntent != null) { - mFillIntent.visitUris(visitor); - } - } - private void handleViewInteraction( View v, InteractionHandler handler) { diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 7f65c52d01b8..07beb114898d 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -16,7 +16,7 @@ flag { flag { name: "defer_display_updates" - namespace: "window_manager" + namespace: "windowing_frontend" description: "Feature flag for deferring DisplayManager updates to WindowManager if Shell transition is running" bug: "259220649" is_fixed_read_only: true diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index 82ee8fc47571..e92c6a6c4b34 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -397,5 +397,5 @@ interface IVoiceInteractionManagerService { * sandboxed detection (from trusted process). */ @EnforcePermission("MANAGE_HOTWORD_DETECTION") - void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed); + void setShouldReceiveSandboxedTrainingData(boolean allowed); } diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index e494346bae5c..bd806bfb3e24 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -519,24 +519,6 @@ public final class SystemUiDeviceConfigFlags { public static final String TASK_MANAGER_SHOW_FOOTER_DOT = "task_manager_show_footer_dot"; /** - * (boolean) Whether to enable the adapter conversion in RemoteViews - */ - public static final String REMOTEVIEWS_ADAPTER_CONVERSION = - "CursorControlFeature__remoteviews_adapter_conversion"; - - /** - * The key name used in app core settings for {@link #REMOTEVIEWS_ADAPTER_CONVERSION} - */ - public static final String KEY_REMOTEVIEWS_ADAPTER_CONVERSION = - "systemui__remoteviews_adapter_conversion"; - - /** - * Default value for whether the adapter conversion is enabled or not. This is set for - * RemoteViews and should not be a common practice. - */ - public static final boolean REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT = false; - - /** * (boolean) Whether the task manager should show a stop button if the app is allowlisted * by the user. */ diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java index f460233f0edd..96740c59ec06 100644 --- a/core/java/com/android/internal/jank/Cuj.java +++ b/core/java/com/android/internal/jank/Cuj.java @@ -109,6 +109,7 @@ public class Cuj { * eg: Exit the app using back gesture. */ public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK = 78; + // 79 is reserved. public static final int CUJ_IME_INSETS_SHOW_ANIMATION = 80; public static final int CUJ_IME_INSETS_HIDE_ANIMATION = 81; @@ -119,10 +120,11 @@ public class Cuj { public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84; public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85; public static final int CUJ_PREDICTIVE_BACK_HOME = 86; + public static final int CUJ_LAUNCHER_SEARCH_QSB_OPEN = 87; // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE. @VisibleForTesting - static final int LAST_CUJ = CUJ_PREDICTIVE_BACK_HOME; + static final int LAST_CUJ = CUJ_LAUNCHER_SEARCH_QSB_OPEN; /** @hide */ @IntDef({ @@ -204,6 +206,7 @@ public class Cuj { CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY, CUJ_PREDICTIVE_BACK_CROSS_TASK, CUJ_PREDICTIVE_BACK_HOME, + CUJ_LAUNCHER_SEARCH_QSB_OPEN, }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -295,6 +298,8 @@ public class Cuj { CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] = + FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN; } private Cuj() { @@ -467,6 +472,8 @@ public class Cuj { return "PREDICTIVE_BACK_CROSS_TASK"; case CUJ_PREDICTIVE_BACK_HOME: return "PREDICTIVE_BACK_HOME"; + case CUJ_LAUNCHER_SEARCH_QSB_OPEN: + return "LAUNCHER_SEARCH_QSB_OPEN"; } return "UNKNOWN"; } diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index 7d78f299c625..0be98040af73 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -72,6 +72,7 @@ import java.util.concurrent.locks.ReentrantLock; * All interfaces in BatteryStatsHistory should only be called by BatteryStatsImpl and protected by * locks on BatteryStatsImpl object. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class BatteryStatsHistory { private static final boolean DEBUG = false; private static final String TAG = "BatteryStatsHistory"; @@ -259,6 +260,7 @@ public class BatteryStatsHistory { * until the first change occurs. */ @VisibleForTesting + @android.ravenwood.annotation.RavenwoodKeepWholeClass public static class TraceDelegate { // Note: certain tests currently run as platform_app which is not allowed // to set debug system properties. To ensure that system properties are set @@ -391,10 +393,18 @@ public class BatteryStatsHistory { public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize, HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, MonotonicClock monotonicClock) { + this(maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator, clock, monotonicClock, + new TraceDelegate()); + } + + @VisibleForTesting + public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize, + HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, + MonotonicClock monotonicClock, TraceDelegate traceDelegate) { mMaxHistoryFiles = maxHistoryFiles; mMaxHistoryBufferSize = maxHistoryBufferSize; mStepDetailsCalculator = stepDetailsCalculator; - mTracer = new TraceDelegate(); + mTracer = traceDelegate; mClock = clock; mMonotonicClock = monotonicClock; @@ -2096,6 +2106,7 @@ public class BatteryStatsHistory { * fewer bytes. It is a bit more expensive than just writing the long into the parcel, * but at scale saves a lot of storage and allows recording of longer battery history. */ + @android.ravenwood.annotation.RavenwoodKeepWholeClass public static final class VarintParceler { /** * Writes an array of longs into Parcel using the varint format, see diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java index 6bd5898b1637..2dffe15dc4be 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java @@ -28,6 +28,7 @@ import java.util.Iterator; /** * An iterator for {@link BatteryStats.HistoryItem}'s. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.HistoryItem>, AutoCloseable { private static final boolean DEBUG = false; diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java index 1a7efac82278..56263fb924ea 100644 --- a/core/java/com/android/internal/os/PowerStats.java +++ b/core/java/com/android/internal/os/PowerStats.java @@ -41,6 +41,7 @@ import java.util.Objects; * Container for power stats, acquired by various PowerStatsCollector classes. See subclasses for * details. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public final class PowerStats { private static final String TAG = "PowerStats"; @@ -67,6 +68,7 @@ public final class PowerStats { * This descriptor is used for storing PowerStats and can also be used by power models * to adjust the algorithm in accordance with the stats available on the device. */ + @android.ravenwood.annotation.RavenwoodKeepWholeClass public static class Descriptor { public static final String XML_TAG_DESCRIPTOR = "descriptor"; private static final String XML_ATTR_ID = "id"; diff --git a/core/java/com/android/internal/pm/parsing/AppInfoUtils.java b/core/java/com/android/internal/pm/parsing/AppInfoUtils.java new file mode 100644 index 000000000000..38a2fe2a77a1 --- /dev/null +++ b/core/java/com/android/internal/pm/parsing/AppInfoUtils.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.pm.parsing; + +import android.annotation.CheckResult; +import android.content.pm.ApplicationInfo; + +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.server.pm.pkg.AndroidPackage; + +public class AppInfoUtils { + + /** + * @see ApplicationInfo#flags + */ + public static int appInfoFlags(AndroidPackage pkg) { + // @formatter:off + int pkgWithoutStateFlags = flag(pkg.isExternalStorage(), ApplicationInfo.FLAG_EXTERNAL_STORAGE) + | flag(pkg.isHardwareAccelerated(), ApplicationInfo.FLAG_HARDWARE_ACCELERATED) + | flag(pkg.isBackupAllowed(), ApplicationInfo.FLAG_ALLOW_BACKUP) + | flag(pkg.isKillAfterRestoreAllowed(), ApplicationInfo.FLAG_KILL_AFTER_RESTORE) + | flag(pkg.isRestoreAnyVersion(), ApplicationInfo.FLAG_RESTORE_ANY_VERSION) + | flag(pkg.isFullBackupOnly(), ApplicationInfo.FLAG_FULL_BACKUP_ONLY) + | flag(pkg.isPersistent(), ApplicationInfo.FLAG_PERSISTENT) + | flag(pkg.isDebuggable(), ApplicationInfo.FLAG_DEBUGGABLE) + | flag(pkg.isVmSafeMode(), ApplicationInfo.FLAG_VM_SAFE_MODE) + | flag(pkg.isDeclaredHavingCode(), ApplicationInfo.FLAG_HAS_CODE) + | flag(pkg.isTaskReparentingAllowed(), ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) + | flag(pkg.isClearUserDataAllowed(), ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) + | flag(pkg.isLargeHeap(), ApplicationInfo.FLAG_LARGE_HEAP) + | flag(pkg.isCleartextTrafficAllowed(), ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) + | flag(pkg.isRtlSupported(), ApplicationInfo.FLAG_SUPPORTS_RTL) + | flag(pkg.isTestOnly(), ApplicationInfo.FLAG_TEST_ONLY) + | flag(pkg.isMultiArch(), ApplicationInfo.FLAG_MULTIARCH) + | flag(pkg.isExtractNativeLibrariesRequested(), ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) + | flag(pkg.isGame(), ApplicationInfo.FLAG_IS_GAME) + | flag(pkg.isSmallScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) + | flag(pkg.isNormalScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) + | flag(pkg.isLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) + | flag(pkg.isExtraLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) + | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) + | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) + | flag(AndroidPackageLegacyUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM) + | flag(pkg.isFactoryTest(), ApplicationInfo.FLAG_FACTORY_TEST); + + return pkgWithoutStateFlags; + // @formatter:on + } + + /** @see ApplicationInfo#privateFlags */ + public static int appInfoPrivateFlags(AndroidPackage pkg) { + // @formatter:off + int pkgWithoutStateFlags = flag(pkg.isStaticSharedLibrary(), ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY) + | flag(pkg.isResourceOverlay(), ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY) + | flag(pkg.isIsolatedSplitLoading(), ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) + | flag(pkg.isHasDomainUrls(), ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) + | flag(pkg.isProfileableByShell(), ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) + | flag(pkg.isBackupInForeground(), ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND) + | flag(pkg.isUseEmbeddedDex(), ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX) + | flag(pkg.isDefaultToDeviceProtectedStorage(), ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) + | flag(pkg.isDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE) + | flag(pkg.isPartiallyDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) + | flag(pkg.isClearUserDataOnFailedRestoreAllowed(), ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE) + | flag(pkg.isAllowAudioPlaybackCapture(), ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) + | flag(pkg.isRequestLegacyExternalStorage(), ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) + | flag(pkg.isNonSdkApiRequested(), ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API) + | flag(pkg.isUserDataFragile(), ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA) + | flag(pkg.isSaveStateDisallowed(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) + | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) + | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING) + | flag(AndroidPackageLegacyUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) + | flag(AndroidPackageLegacyUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) + | flag(AndroidPackageLegacyUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM) + | flag(AndroidPackageLegacyUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR) + | flag(AndroidPackageLegacyUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT) + | flag(AndroidPackageLegacyUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM) + | flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY); + + Boolean resizeableActivity = pkg.getResizeableActivity(); + if (resizeableActivity != null) { + if (resizeableActivity) { + pkgWithoutStateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE; + } else { + pkgWithoutStateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE; + } + } + + return pkgWithoutStateFlags; + // @formatter:on + } + + + /** @see ApplicationInfo#privateFlagsExt */ + public static int appInfoPrivateFlagsExt(AndroidPackage pkg, + boolean isAllowlistedForHiddenApis) { + // @formatter:off + int pkgWithoutStateFlags = flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE) + | flag(pkg.hasRequestForegroundServiceExemption(), ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION) + | flag(pkg.isAttributionsUserVisible(), ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE) + | flag(pkg.isOnBackInvokedCallbackEnabled(), ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK) + | flag(isAllowlistedForHiddenApis, ApplicationInfo.PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS); + return pkgWithoutStateFlags; + // @formatter:on + } + + @CheckResult + private static int flag(boolean hasFlag, int flag) { + return hasFlag ? flag : 0; + } +} diff --git a/core/java/com/android/internal/pm/parsing/PackageParserException.java b/core/java/com/android/internal/pm/parsing/PackageParserException.java new file mode 100644 index 000000000000..4250bbd9baf6 --- /dev/null +++ b/core/java/com/android/internal/pm/parsing/PackageParserException.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.pm.parsing; + +public class PackageParserException extends Exception { + public final int error; + + public PackageParserException(int error, String detailMessage) { + super(detailMessage); + this.error = error; + } + + public PackageParserException(int error, String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + this.error = error; + } +}
\ No newline at end of file diff --git a/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageLegacyUtils.java b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageLegacyUtils.java new file mode 100644 index 000000000000..e65f1c960b6a --- /dev/null +++ b/core/java/com/android/internal/pm/parsing/pkg/AndroidPackageLegacyUtils.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.pm.parsing.pkg; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; + +import com.android.internal.pm.pkg.parsing.ParsingPackageHidden; +import com.android.server.pm.pkg.AndroidPackage; + +/** @hide */ +public class AndroidPackageLegacyUtils { + + private AndroidPackageLegacyUtils() { + } + + /** + * Returns the primary ABI as parsed from the package. Used only during parsing and derivation. + * Otherwise prefer {@link PackageState#getPrimaryCpuAbi()}. + */ + public static String getRawPrimaryCpuAbi(AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).getPrimaryCpuAbi(); + } + + /** + * Returns the secondary ABI as parsed from the package. Used only during parsing and + * derivation. Otherwise prefer {@link PackageState#getSecondaryCpuAbi()}. + */ + public static String getRawSecondaryCpuAbi(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).getSecondaryCpuAbi(); + } + + @Deprecated + @NonNull + public static ApplicationInfo generateAppInfoWithoutState(AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).toAppInfoWithoutState(); + } + + /** + * Replacement of unnecessary legacy getRealPackage. Only returns a value if the package was + * actually renamed. + */ + @Nullable + public static String getRealPackageOrNull(@NonNull AndroidPackage pkg, boolean isSystem) { + if (pkg.getOriginalPackages().isEmpty() || !isSystem) { + return null; + } + + return pkg.getManifestPackageName(); + } + + public static void fillVersionCodes(@NonNull AndroidPackage pkg, @NonNull PackageInfo info) { + info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode(); + info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor(); + } + + /** + * @deprecated Use {@link PackageState#isSystem} + */ + @Deprecated + public static boolean isSystem(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isSystem(); + } + + /** + * @deprecated Use {@link PackageState#isSystemExt} + */ + @Deprecated + public static boolean isSystemExt(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isSystemExt(); + } + + /** + * @deprecated Use {@link PackageState#isPrivileged} + */ + @Deprecated + public static boolean isPrivileged(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isPrivileged(); + } + + /** + * @deprecated Use {@link PackageState#isOem} + */ + @Deprecated + public static boolean isOem(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isOem(); + } + + /** + * @deprecated Use {@link PackageState#isVendor} + */ + @Deprecated + public static boolean isVendor(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isVendor(); + } + + /** + * @deprecated Use {@link PackageState#isProduct} + */ + @Deprecated + public static boolean isProduct(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isProduct(); + } + + /** + * @deprecated Use {@link PackageState#isOdm} + */ + @Deprecated + public static boolean isOdm(@NonNull AndroidPackage pkg) { + return ((AndroidPackageHidden) pkg).isOdm(); + } +} diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java index da58d47edbfe..f7e1f7293ac6 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java +++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.parsing.pkg; +package com.android.internal.pm.parsing.pkg; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; @@ -50,47 +50,44 @@ import android.util.SparseIntArray; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.parsing.pkg.AndroidPackageHidden; -import com.android.internal.pm.parsing.pkg.AndroidPackageInternal; -import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.parsing.AppInfoUtils; import com.android.internal.pm.pkg.AndroidPackageSplitImpl; +import com.android.internal.pm.pkg.SEInfoUtil; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedActivity; +import com.android.internal.pm.pkg.component.ParsedActivityImpl; import com.android.internal.pm.pkg.component.ParsedApexSystemService; +import com.android.internal.pm.pkg.component.ParsedApexSystemServiceImpl; import com.android.internal.pm.pkg.component.ParsedAttribution; +import com.android.internal.pm.pkg.component.ParsedAttributionImpl; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedInstrumentation; +import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.component.ParsedPermission; import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl; +import com.android.internal.pm.pkg.component.ParsedPermissionImpl; import com.android.internal.pm.pkg.component.ParsedProcess; +import com.android.internal.pm.pkg.component.ParsedProcessImpl; import com.android.internal.pm.pkg.component.ParsedProvider; +import com.android.internal.pm.pkg.component.ParsedProviderImpl; import com.android.internal.pm.pkg.component.ParsedService; +import com.android.internal.pm.pkg.component.ParsedServiceImpl; import com.android.internal.pm.pkg.component.ParsedUsesPermission; +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.internal.pm.pkg.parsing.ParsingPackageHidden; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; -import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.AndroidPackageSplit; -import com.android.server.pm.pkg.SELinuxUtil; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.component.ParsedActivityImpl; -import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl; -import com.android.server.pm.pkg.component.ParsedAttributionImpl; -import com.android.server.pm.pkg.component.ParsedInstrumentationImpl; -import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl; -import com.android.server.pm.pkg.component.ParsedPermissionImpl; -import com.android.server.pm.pkg.component.ParsedProcessImpl; -import com.android.server.pm.pkg.component.ParsedProviderImpl; -import com.android.server.pm.pkg.component.ParsedServiceImpl; -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; import libcore.util.EmptyArray; @@ -422,8 +419,10 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, @NonNull public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath, - @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp) { - return new PackageImpl(packageName, baseCodePath, codePath, manifestArray, isCoreApp); + @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp, + @Nullable ParsingPackageUtils.Callback callback) { + return new PackageImpl( + packageName, baseCodePath, codePath, manifestArray, isCoreApp, callback); } /** @@ -453,7 +452,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, @NonNull @VisibleForTesting public static ParsingPackage forTesting(String packageName, String baseCodePath) { - return new PackageImpl(packageName, baseCodePath, baseCodePath, null, false); + return new PackageImpl(packageName, baseCodePath, baseCodePath, null, false, null); } @NonNull @@ -2694,12 +2693,16 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, private String mBaseAppDataCredentialProtectedDirForSystemUser; private String mBaseAppDataDeviceProtectedDirForSystemUser; + ParsingPackageUtils.Callback mCallback; + @VisibleForTesting public PackageImpl(@NonNull String packageName, @NonNull String baseApkPath, - @NonNull String path, @Nullable TypedArray manifestArray, boolean isCoreApp) { + @NonNull String path, @Nullable TypedArray manifestArray, boolean isCoreApp, + @Nullable ParsingPackageUtils.Callback callback) { this.packageName = TextUtils.safeIntern(packageName); this.mBaseApkPath = baseApkPath; this.mPath = path; + this.mCallback = callback; if (manifestArray != null) { versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0); @@ -2750,9 +2753,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, } private void assignDerivedFields2() { - mBaseAppInfoFlags = PackageInfoUtils.appInfoFlags(this, null); - mBaseAppInfoPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(this, null); - mBaseAppInfoPrivateFlagsExt = PackageInfoUtils.appInfoPrivateFlagsExt(this, null); + mBaseAppInfoFlags = AppInfoUtils.appInfoFlags(this); + mBaseAppInfoPrivateFlags = AppInfoUtils.appInfoPrivateFlags(this); + mBaseAppInfoPrivateFlagsExt = AppInfoUtils.appInfoPrivateFlagsExt(this, + mCallback == null ? false : + mCallback.getHiddenApiWhitelistedApps().contains(this.packageName)); String baseAppDataDir = Environment.getDataDirectoryPath(getVolumeUuid()) + File.separator; String systemUserSuffix = File.separator + UserHandle.USER_SYSTEM + File.separator; mBaseAppDataCredentialProtectedDirForSystemUser = TextUtils.safeIntern( @@ -3087,7 +3092,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, appInfo.primaryCpuAbi = primaryCpuAbi; appInfo.secondaryCpuAbi = secondaryCpuAbi; appInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir; - appInfo.seInfoUser = SELinuxUtil.COMPLETE_STR; + appInfo.seInfoUser = SEInfoUtil.COMPLETE_STR; appInfo.uid = uid; return appInfo; } diff --git a/services/core/java/com/android/server/pm/permission/CompatibilityPermissionInfo.java b/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java index d9625050d28c..a670c6d5aff4 100644 --- a/services/core/java/com/android/server/pm/permission/CompatibilityPermissionInfo.java +++ b/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.permission; +package com.android.internal.pm.permission; import android.Manifest; import android.annotation.NonNull; @@ -67,7 +67,7 @@ public class CompatibilityPermissionInfo { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -97,10 +97,10 @@ public class CompatibilityPermissionInfo { } @DataClass.Generated( - time = 1627674427184L, + time = 1701338392152L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mName\nprivate final int mSdkVersion\npublic static final android.content.pm.permission.CompatibilityPermissionInfo[] COMPAT_PERMS\nclass CompatibilityPermissionInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genBuilder=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/permission/CompatibilityPermissionInfo.java", + inputSignatures = "private final @android.annotation.NonNull java.lang.String mName\nprivate final int mSdkVersion\npublic static final com.android.internal.pm.permission.CompatibilityPermissionInfo[] COMPAT_PERMS\nclass CompatibilityPermissionInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genGetters=true, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/core/java/com/android/internal/pm/pkg/SEInfoUtil.java b/core/java/com/android/internal/pm/pkg/SEInfoUtil.java new file mode 100644 index 000000000000..a6988829ca92 --- /dev/null +++ b/core/java/com/android/internal/pm/pkg/SEInfoUtil.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.pm.pkg; + +/** + * Utility methods that need to be used in application space. + * @hide + */ +public final class SEInfoUtil { + + /** Append to existing seinfo label for instant apps @hide */ + public static final String INSTANT_APP_STR = ":ephemeralapp"; + + /** Append to existing seinfo when modifications are complete @hide */ + public static final String COMPLETE_STR = ":complete"; +} diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java b/core/java/com/android/internal/pm/pkg/component/ComponentMutateUtils.java index 1964df0853fd..fd5f0f079da9 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ComponentMutateUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ComponentMutateUtils.java @@ -14,19 +14,11 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.annotation.Nullable; -import com.android.internal.pm.pkg.component.ParsedActivity; -import com.android.internal.pm.pkg.component.ParsedComponent; -import com.android.internal.pm.pkg.component.ParsedMainComponent; -import com.android.internal.pm.pkg.component.ParsedPermission; -import com.android.internal.pm.pkg.component.ParsedPermissionGroup; -import com.android.internal.pm.pkg.component.ParsedProcess; -import com.android.internal.pm.pkg.component.ParsedProvider; - /** * Contains mutation methods so that code doesn't have to cast to the Impl. Meant to eventually * be removed once all post-parsing mutation is moved to parsing. diff --git a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java b/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java index 019ca1315af8..db08005c833e 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ComponentParseUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ComponentParseUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.AttrRes; import android.annotation.NonNull; @@ -29,14 +29,9 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.text.TextUtils; -import com.android.internal.pm.pkg.component.ParsedComponent; -import com.android.internal.pm.pkg.component.ParsedIntentInfo; -import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.PackageUserState; -import com.android.server.pm.pkg.PackageUserStateUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -173,16 +168,4 @@ public class ComponentParseUtils { public static int getIcon(ParsedComponent component) { return component.getIcon(); } - - public static boolean isMatch(PackageUserState state, boolean isSystem, - boolean isPackageEnabled, ParsedMainComponent component, long flags) { - return PackageUserStateUtils.isMatch(state, isSystem, isPackageEnabled, - component.isEnabled(), component.isDirectBootAware(), component.getName(), flags); - } - - public static boolean isEnabled(PackageUserState state, boolean isPackageEnabled, - ParsedMainComponent parsedComponent, long flags) { - return PackageUserStateUtils.isEnabled(state, isPackageEnabled, parsedComponent.isEnabled(), - parsedComponent.getName(), flags); - } } diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java index dd54cfca6518..0b045919fb13 100644 --- a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java +++ b/core/java/com/android/internal/pm/pkg/component/InstallConstraintsTagParser.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.content.pm.parsing.result.ParseInput; @@ -27,7 +27,6 @@ import android.util.ArraySet; import com.android.internal.R; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.SystemConfig; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -48,9 +47,8 @@ public class InstallConstraintsTagParser { * @hide */ public static ParseResult<ParsingPackage> parseInstallConstraints( - ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) - throws XmlPullParserException, IOException { - Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist(); + ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, + Set<String> allowlist) throws XmlPullParserException, IOException { if (!allowlist.contains(pkg.getPackageName())) { return input.skip("install-constraints cannot be used by this package"); } diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java index f02790189cc0..2f977eea127d 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; @@ -22,8 +22,8 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForStringSet; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForStringSet; import android.annotation.NonNull; import android.annotation.Nullable; @@ -36,10 +36,9 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.ArraySet; -import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import java.util.Collections; import java.util.Locale; @@ -380,7 +379,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -696,10 +695,10 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse } @DataClass.Generated( - time = 1669437519576L, + time = 1701338377709L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java", - inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityImpl.java", + inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedActivityImpl> CREATOR\npublic static @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.internal.pm.pkg.component.ParsedActivity)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.internal.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java index 64985bdfd54f..c3f7dab3c46a 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static com.android.server.pm.pkg.component.ComponentParseUtils.flag; -import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET; -import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts; +import static com.android.internal.pm.pkg.component.ComponentParseUtils.flag; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts; import android.annotation.NonNull; import android.annotation.Nullable; @@ -48,11 +48,10 @@ import android.view.WindowManager; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.ArrayUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java index cfed19aa0934..27f7eee1a308 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedApexSystemService; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; @@ -60,7 +59,7 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -248,9 +247,9 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par }; @DataClass.Generated( - time = 1643723578605L, + time = 1701710844088L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java", + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceImpl.java", inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceUtils.java index d3fb29b8aa66..c69213f11f85 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedApexSystemServiceUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.R; import android.annotation.NonNull; @@ -25,8 +25,6 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.text.TextUtils; -import com.android.internal.pm.pkg.component.ParsedApexSystemService; - import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java index 62b994724346..e3bfb38802db 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.annotation.StringRes; @@ -22,7 +22,6 @@ import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedAttribution; import com.android.internal.util.DataClass; import java.util.ArrayList; @@ -60,7 +59,7 @@ public class ParsedAttributionImpl implements ParsedAttribution, Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -207,10 +206,10 @@ public class ParsedAttributionImpl implements ParsedAttribution, Parcelable { }; @DataClass.Generated( - time = 1641431950829L, + time = 1701338881658L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttributionImpl.java", - inputSignatures = "static final int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedAttribution, android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedAttributionImpl.java", + inputSignatures = "static final int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\nclass ParsedAttributionImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedAttribution, android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false, genParcelable=true)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionUtils.java index 411220ae42e8..ee5c3204ccd1 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedAttributionUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedAttributionUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,7 +26,6 @@ import android.content.res.XmlResourceParser; import android.util.ArraySet; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedAttribution; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java index 512e5c7023c7..7ee22f30ace0 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; import static java.util.Collections.emptyMap; @@ -32,12 +32,10 @@ import android.text.TextUtils; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedComponent; -import com.android.internal.pm.pkg.component.ParsedIntentInfo; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; -import com.android.server.pm.pkg.parsing.ParsingUtils; import java.util.ArrayList; import java.util.Collections; @@ -200,7 +198,7 @@ public abstract class ParsedComponentImpl implements ParsedComponent, Parcelable // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -306,10 +304,10 @@ public abstract class ParsedComponentImpl implements ParsedComponent, Parcelable } @DataClass.Generated( - time = 1641414207885L, + time = 1701445673589L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedComponentImpl.java", - inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate int icon\nprivate int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate int logo\nprivate int banner\nprivate int descriptionRes\nprivate int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<android.content.pm.parsing.component.ParsedIntentInfoImpl> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\n void addIntent(android.content.pm.parsing.component.ParsedIntentInfoImpl)\n void addProperty(android.content.pm.PackageManager.Property)\npublic android.content.pm.parsing.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @android.annotation.NonNull @java.lang.Override android.os.Bundle getMetaData()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.parsing.component.ParsedIntentInfo> getIntents()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genConstructor=false, genBuilder=false, genParcelable=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedComponentImpl.java", + inputSignatures = "private @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String name\nprivate int icon\nprivate int labelRes\nprivate @android.annotation.Nullable java.lang.CharSequence nonLocalizedLabel\nprivate int logo\nprivate int banner\nprivate int descriptionRes\nprivate int flags\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String packageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"intent\") java.util.List<com.android.internal.pm.pkg.component.ParsedIntentInfoImpl> intents\nprivate @android.annotation.Nullable android.content.ComponentName componentName\nprivate @android.annotation.Nullable android.os.Bundle metaData\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.PackageManager.Property> mProperties\npublic void addIntent(com.android.internal.pm.pkg.component.ParsedIntentInfoImpl)\npublic void addProperty(android.content.pm.PackageManager.Property)\npublic com.android.internal.pm.pkg.component.ParsedComponentImpl setName(java.lang.String)\npublic @android.annotation.CallSuper void setPackageName(java.lang.String)\npublic @java.lang.Override @android.annotation.NonNull android.content.ComponentName getComponentName()\npublic @android.annotation.NonNull @java.lang.Override android.os.Bundle getMetaData()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.internal.pm.pkg.component.ParsedIntentInfo> getIntents()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedComponentImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genConstructor=false, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedComponentUtils.java index 9322cf0e90f6..9e2548b3bce3 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedComponentUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedComponentUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET; import android.annotation.NonNull; import android.content.pm.PackageManager; @@ -32,8 +32,8 @@ import android.util.TypedValue; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; /** @hide */ class ParsedComponentUtils { diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java index 7bfad14d669a..07322e9dd912 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,7 +26,6 @@ import android.os.Parcelable; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; @@ -112,7 +111,7 @@ public class ParsedInstrumentationImpl extends ParsedComponentImpl implements // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -166,10 +165,10 @@ public class ParsedInstrumentationImpl extends ParsedComponentImpl implements } @DataClass.Generated( - time = 1641431951575L, + time = 1701445763455L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedInstrumentationImpl.java", - inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate boolean handleProfiling\nprivate boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedInstrumentationImpl> CREATOR\npublic android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic android.content.pm.parsing.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedInstrumentation, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationImpl.java", + inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetPackage\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetProcesses\nprivate boolean handleProfiling\nprivate boolean functionalTest\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedInstrumentationImpl> CREATOR\npublic com.android.internal.pm.pkg.component.ParsedInstrumentationImpl setTargetPackage(java.lang.String)\npublic com.android.internal.pm.pkg.component.ParsedInstrumentationImpl setTargetProcesses(java.lang.String)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedInstrumentationImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedInstrumentation, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationUtils.java index a7116949b911..661c8b421fb4 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedInstrumentationUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedInstrumentationUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET; import android.annotation.NonNull; import android.content.pm.parsing.result.ParseInput; @@ -26,7 +26,6 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.pm.pkg.parsing.ParsingPackage; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java index ab9404310078..adb49e9fde6d 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.annotation.Nullable; @@ -23,7 +23,6 @@ import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.util.DataClass; /** @@ -54,7 +53,7 @@ public class ParsedIntentInfoImpl implements ParsedIntentInfo, Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -170,10 +169,10 @@ public class ParsedIntentInfoImpl implements ParsedIntentInfo, Parcelable { }; @DataClass.Generated( - time = 1641431952314L, + time = 1701445800363L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedIntentInfoImpl.java", - inputSignatures = "private boolean mHasDefault\nprivate int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedIntentInfo, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoImpl.java", + inputSignatures = "private boolean mHasDefault\nprivate int mLabelRes\nprivate @android.annotation.Nullable java.lang.CharSequence mNonLocalizedLabel\nprivate int mIcon\nprivate @android.annotation.NonNull android.content.IntentFilter mIntentFilter\nclass ParsedIntentInfoImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedIntentInfo, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false, genConstructor=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java index e5e214d2292b..c6683cfc8331 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedIntentInfoUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedIntentInfoUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.ANDROID_RES_NAMESPACE; import android.annotation.NonNull; import android.content.Intent; @@ -31,10 +31,9 @@ import android.util.Slog; import android.util.TypedValue; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java index f322eef8c3a3..bb8f565d2032 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; import android.annotation.NonNull; import android.annotation.Nullable; @@ -25,7 +25,6 @@ import android.os.Parcelable; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; @@ -133,7 +132,7 @@ public class ParsedMainComponentImpl extends ParsedComponentImpl implements Pars // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -227,10 +226,10 @@ public class ParsedMainComponentImpl extends ParsedComponentImpl implements Pars } @DataClass.Generated( - time = 1641414540422L, + time = 1701447884766L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedMainComponentImpl.java", - inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate boolean directBootAware\nprivate boolean enabled\nprivate boolean exported\nprivate int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedMainComponentImpl> CREATOR\npublic android.content.pm.parsing.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic java.lang.String getClassName()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getAttributionTags()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedMainComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentImpl.java", + inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String processName\nprivate boolean directBootAware\nprivate boolean enabled\nprivate boolean exported\nprivate int order\nprivate @android.annotation.Nullable java.lang.String splitName\nprivate @android.annotation.Nullable java.lang.String[] attributionTags\npublic static final android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedMainComponentImpl> CREATOR\npublic com.android.internal.pm.pkg.component.ParsedMainComponentImpl setProcessName(java.lang.String)\npublic java.lang.String getClassName()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getAttributionTags()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedMainComponentImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedMainComponent, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java index 8268f0fdfa3e..7e56180f72ce 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedMainComponentUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,10 +31,8 @@ import android.os.Build; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedIntentInfo; -import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java index afe37bc3274c..3622019f36b9 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java @@ -14,14 +14,13 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedPermissionGroup; import com.android.internal.util.DataClass; /** @@ -75,7 +74,7 @@ public class ParsedPermissionGroupImpl extends ParsedComponentImpl implements // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -172,10 +171,10 @@ public class ParsedPermissionGroupImpl extends ParsedComponentImpl implements }; @DataClass.Generated( - time = 1642132854167L, + time = 1701445837884L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionGroupImpl.java", - inputSignatures = "private int requestDetailRes\nprivate int backgroundRequestRes\nprivate int backgroundRequestDetailRes\nprivate int requestRes\nprivate int priority\npublic java.lang.String toString()\npublic @java.lang.Override @com.android.internal.util.DataClass.Generated.Member void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.server.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionGroupImpl.java", + inputSignatures = "private int requestDetailRes\nprivate int backgroundRequestRes\nprivate int backgroundRequestDetailRes\nprivate int requestRes\nprivate int priority\npublic java.lang.String toString()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionGroupImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermissionGroup, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java index 69e33c8f281e..4dcce131168b 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.annotation.Nullable; @@ -24,8 +24,6 @@ import android.text.TextUtils; import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedPermission; -import com.android.internal.pm.pkg.component.ParsedPermissionGroup; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; @@ -148,7 +146,7 @@ public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedP // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -162,7 +160,7 @@ public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedP int requestRes, int protectionLevel, boolean tree, - @Nullable ParsedPermissionGroupImpl parsedPermissionGroup, + @Nullable ParsedPermissionGroup parsedPermissionGroup, @Nullable Set<String> knownCerts) { this.backgroundPermission = backgroundPermission; this.group = group; @@ -237,10 +235,10 @@ public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedP } @DataClass.Generated( - time = 1641414649731L, + time = 1701445829812L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java", - inputSignatures = "private static com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate int requestRes\nprivate int protectionLevel\nprivate boolean tree\nprivate @android.annotation.Nullable android.content.pm.parsing.component.ParsedPermissionGroupImpl parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedPermissionImpl> CREATOR\npublic android.content.pm.parsing.component.ParsedPermissionGroup getParsedPermissionGroup()\npublic android.content.pm.parsing.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected void setKnownCert(java.lang.String)\nprotected void setKnownCerts(java.lang.String[])\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownCerts()\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends android.content.pm.parsing.component.ParsedComponentImpl implements [android.content.pm.parsing.component.ParsedPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedPermissionImpl.java", + inputSignatures = "private static final com.android.internal.util.Parcelling.BuiltIn.ForStringSet sForStringSet\nprivate @android.annotation.Nullable java.lang.String backgroundPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String group\nprivate int requestRes\nprivate int protectionLevel\nprivate boolean tree\nprivate @android.annotation.Nullable com.android.internal.pm.pkg.component.ParsedPermissionGroup parsedPermissionGroup\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> knownCerts\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedPermissionImpl> CREATOR\npublic com.android.internal.pm.pkg.component.ParsedPermissionGroup getParsedPermissionGroup()\npublic com.android.internal.pm.pkg.component.ParsedPermissionImpl setGroup(java.lang.String)\nprotected void setKnownCert(java.lang.String)\nprotected void setKnownCerts(java.lang.String[])\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownCerts()\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedPermissionImpl extends com.android.internal.pm.pkg.component.ParsedComponentImpl implements [com.android.internal.pm.pkg.component.ParsedPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionUtils.java index 4b45d3742a2a..5651c1ca247f 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedPermissionUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.NOT_SET; import android.annotation.NonNull; import android.content.pm.PermissionInfo; @@ -31,10 +31,8 @@ import android.util.EventLog; import android.util.Slog; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedPermission; -import com.android.internal.pm.pkg.component.ParsedPermissionGroup; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java index 40e3670b9261..212fb867e7df 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import static java.util.Collections.emptySet; @@ -25,7 +25,6 @@ import android.os.Parcelable; import android.util.ArrayMap; import android.util.ArraySet; -import com.android.internal.pm.pkg.component.ParsedProcess; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; @@ -98,7 +97,7 @@ public class ParsedProcessImpl implements ParsedProcess, Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -304,10 +303,10 @@ public class ParsedProcessImpl implements ParsedProcess, Parcelable { }; @DataClass.Generated( - time = 1641431953775L, + time = 1701445656489L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcessImpl.java", - inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\npublic void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedProcess, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java", + inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(com.android.internal.pm.pkg.component.ParsedProcess)\npublic void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedProcess, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java index a84954950f44..3b2056e7892e 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedProcessUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.content.pm.ApplicationInfo; @@ -27,11 +27,10 @@ import android.util.ArrayMap; import android.util.ArraySet; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedProcess; import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.XmlUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java index 81a3c17e2bb4..987fd4158418 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; import android.annotation.NonNull; import android.annotation.Nullable; @@ -28,7 +28,6 @@ import android.os.PatternMatcher; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedProvider; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; @@ -302,7 +301,7 @@ public class ParsedProviderImpl extends ParsedMainComponentImpl implements Parse time = 1642560323360L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java", - inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate boolean grantUriPermissions\nprivate boolean forceUriPermissions\nprivate boolean multiProcess\nprivate int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)") + inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate boolean grantUriPermissions\nprivate boolean forceUriPermissions\nprivate boolean multiProcess\nprivate int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic com.android.internal.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic com.android.internal.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedProvider, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java index 0b28a1214072..5d82d0469d56 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedProviderUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.component.ComponentParseUtils.flag; -import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER; +import static com.android.internal.pm.pkg.component.ComponentParseUtils.flag; +import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,9 +34,8 @@ import android.os.PatternMatcher; import android.util.Slog; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedProvider; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java index ca8c45d1383c..f4662d803296 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.parsing.pkg.PackageImpl.sForInternedString; +import static com.android.internal.pm.parsing.pkg.PackageImpl.sForInternedString; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,8 +26,6 @@ import android.os.Parcelable; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedMainComponent; -import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling.BuiltIn.ForInternedString; @@ -107,7 +105,7 @@ public class ParsedServiceImpl extends ParsedMainComponentImpl implements Parsed // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -141,10 +139,10 @@ public class ParsedServiceImpl extends ParsedMainComponentImpl implements Parsed } @DataClass.Generated( - time = 1641431954479L, + time = 1701445638370L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedServiceImpl.java", - inputSignatures = "private int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedServiceImpl> CREATOR\npublic android.content.pm.parsing.component.ParsedMainComponent setPermission(java.lang.String)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedServiceImpl.java", + inputSignatures = "private int foregroundServiceType\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedServiceImpl> CREATOR\npublic com.android.internal.pm.pkg.component.ParsedMainComponent setPermission(java.lang.String)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedServiceImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java index 171ef594f6fd..a1dd19a3bc90 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; -import static com.android.server.pm.pkg.component.ComponentParseUtils.flag; +import static com.android.internal.pm.pkg.component.ComponentParseUtils.flag; import android.annotation.NonNull; import android.annotation.Nullable; @@ -32,9 +32,8 @@ import android.content.res.XmlResourceParser; import android.os.Build; import com.android.internal.R; -import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.pkg.parsing.ParsingUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java index 78377a836651..fd131dfd00c7 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedUsesPermissionImpl.java +++ b/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java @@ -14,14 +14,13 @@ * limitations under the License. */ -package com.android.server.pm.pkg.component; +package com.android.internal.pm.pkg.component; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.pm.pkg.component.ParsedUsesPermission; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; @@ -51,7 +50,7 @@ public class ParsedUsesPermissionImpl implements ParsedUsesPermission, Parcelabl // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -158,10 +157,10 @@ public class ParsedUsesPermissionImpl implements ParsedUsesPermission, Parcelabl }; @DataClass.Generated( - time = 1641431955242L, + time = 1701445626268L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermissionImpl.java", - inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedUsesPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)") + sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedUsesPermissionImpl.java", + inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.pm.pkg.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\nclass ParsedUsesPermissionImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedUsesPermission, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)") @Deprecated private void __metadata() {} diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java index aa0fb2734382..dbe4fba5dfdb 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm.pkg.parsing; +package com.android.internal.pm.pkg.parsing; import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; @@ -30,7 +30,7 @@ import static android.os.Build.VERSION_CODES.DONUT; import static android.os.Build.VERSION_CODES.O; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; -import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts; +import static com.android.internal.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts; import android.annotation.AnyRes; import android.annotation.CheckResult; @@ -57,7 +57,6 @@ import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.result.ParseInput; import android.content.pm.parsing.result.ParseInput.DeferredError; import android.content.pm.parsing.result.ParseResult; -import android.content.pm.parsing.result.ParseTypeImpl; import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; @@ -90,43 +89,40 @@ import android.util.apk.ApkSignatureVerifier; import com.android.internal.R; import com.android.internal.os.ClassLoaderFactory; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.permission.CompatibilityPermissionInfo; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; +import com.android.internal.pm.pkg.component.ComponentParseUtils; +import com.android.internal.pm.pkg.component.InstallConstraintsTagParser; import com.android.internal.pm.pkg.component.ParsedActivity; +import com.android.internal.pm.pkg.component.ParsedActivityImpl; +import com.android.internal.pm.pkg.component.ParsedActivityUtils; import com.android.internal.pm.pkg.component.ParsedApexSystemService; +import com.android.internal.pm.pkg.component.ParsedApexSystemServiceUtils; import com.android.internal.pm.pkg.component.ParsedAttribution; +import com.android.internal.pm.pkg.component.ParsedAttributionUtils; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedInstrumentation; +import com.android.internal.pm.pkg.component.ParsedInstrumentationUtils; import com.android.internal.pm.pkg.component.ParsedIntentInfo; +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl; +import com.android.internal.pm.pkg.component.ParsedIntentInfoUtils; import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.component.ParsedPermission; import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.component.ParsedPermissionUtils; import com.android.internal.pm.pkg.component.ParsedProcess; +import com.android.internal.pm.pkg.component.ParsedProcessUtils; import com.android.internal.pm.pkg.component.ParsedProvider; +import com.android.internal.pm.pkg.component.ParsedProviderUtils; import com.android.internal.pm.pkg.component.ParsedService; +import com.android.internal.pm.pkg.component.ParsedServiceUtils; import com.android.internal.pm.pkg.component.ParsedUsesPermission; -import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; +import com.android.internal.pm.split.DefaultSplitAssetLoader; +import com.android.internal.pm.split.SplitAssetDependencyLoader; +import com.android.internal.pm.split.SplitAssetLoader; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; -import com.android.server.pm.SharedUidMigration; -import com.android.server.pm.parsing.pkg.PackageImpl; -import com.android.server.pm.permission.CompatibilityPermissionInfo; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.component.ComponentParseUtils; -import com.android.server.pm.pkg.component.InstallConstraintsTagParser; -import com.android.server.pm.pkg.component.ParsedActivityImpl; -import com.android.server.pm.pkg.component.ParsedActivityUtils; -import com.android.server.pm.pkg.component.ParsedApexSystemServiceUtils; -import com.android.server.pm.pkg.component.ParsedAttributionUtils; -import com.android.server.pm.pkg.component.ParsedInstrumentationUtils; -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl; -import com.android.server.pm.pkg.component.ParsedIntentInfoUtils; -import com.android.server.pm.pkg.component.ParsedPermissionUtils; -import com.android.server.pm.pkg.component.ParsedProcessUtils; -import com.android.server.pm.pkg.component.ParsedProviderUtils; -import com.android.server.pm.pkg.component.ParsedServiceUtils; -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl; -import com.android.server.pm.split.DefaultSplitAssetLoader; -import com.android.server.pm.split.SplitAssetDependencyLoader; -import com.android.server.pm.split.SplitAssetLoader; import libcore.io.IoUtils; import libcore.util.EmptyArray; @@ -267,18 +263,6 @@ public class ParsingPackageUtils { public @interface ParseFlags {} /** - * @see #parseDefault(ParseInput, File, int, List, boolean) - */ - @NonNull - public static ParseResult<ParsedPackage> parseDefaultOneTime(File file, - @ParseFlags int parseFlags, - @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions, - boolean collectCertificates) { - ParseInput input = ParseTypeImpl.forDefaultParsing().reset(); - return parseDefault(input, file, parseFlags, splitPermissions, collectCertificates); - } - - /** * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off * request, without caching the input object and without querying the internal system state for * feature support. @@ -287,30 +271,10 @@ public class ParsingPackageUtils { public static ParseResult<ParsedPackage> parseDefault(ParseInput input, File file, @ParseFlags int parseFlags, @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions, - boolean collectCertificates) { - ParseResult<ParsedPackage> result; + boolean collectCertificates, Callback callback) { ParsingPackageUtils parser = new ParsingPackageUtils(null /*separateProcesses*/, - null /*displayMetrics*/, splitPermissions, new Callback() { - @Override - public boolean hasFeature(String feature) { - // Assume the device doesn't support anything. This will affect permission - // parsing and will force <uses-permission/> declarations to include all - // requiredNotFeature permissions and exclude all requiredFeature - // permissions. This mirrors the old behavior. - return false; - } - - @Override - public ParsingPackage startParsingPackage( - @NonNull String packageName, - @NonNull String baseApkPath, - @NonNull String path, - @NonNull TypedArray manifestArray, boolean isCoreApp) { - return PackageImpl.forParsing(packageName, baseApkPath, path, manifestArray, - isCoreApp); - } - }); + null /*displayMetrics*/, splitPermissions, callback); var parseResult = parser.parsePackage(input, file, parseFlags); if (parseResult.isError()) { return input.error(parseResult); @@ -1146,7 +1110,8 @@ public class ParsingPackageUtils { case TAG_RESTRICT_UPDATE: return parseRestrictUpdateHash(flags, input, pkg, res, parser); case TAG_INSTALL_CONSTRAINTS: - return parseInstallConstraints(input, pkg, res, parser); + return parseInstallConstraints(input, pkg, res, parser, + mCallback.getInstallConstraintsAllowlist()); case TAG_QUERIES: return parseQueries(input, pkg, res, parser); default: @@ -1172,7 +1137,7 @@ public class ParsingPackageUtils { } boolean leaving = false; - if (!SharedUidMigration.isDisabled()) { + if (PackageManager.ENABLE_SHARED_UID_MIGRATION) { int max = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa); leaving = (max != 0) && (max < Build.VERSION.RESOURCES_SDK_INT); } @@ -1858,10 +1823,11 @@ public class ParsingPackageUtils { return input.success(pkg); } - private static ParseResult<ParsingPackage> parseInstallConstraints( - ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) + private static ParseResult<ParsingPackage> parseInstallConstraints(ParseInput input, + ParsingPackage pkg, Resources res, XmlResourceParser parser, Set<String> allowlist) throws IOException, XmlPullParserException { - return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser); + return InstallConstraintsTagParser.parseInstallConstraints( + input, pkg, res, parser, allowlist); } private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg, @@ -3485,5 +3451,9 @@ public class ParsingPackageUtils { ParsingPackage startParsingPackage(@NonNull String packageName, @NonNull String baseApkPath, @NonNull String path, @NonNull TypedArray manifestArray, boolean isCoreApp); + + @NonNull Set<String> getHiddenApiWhitelistedApps(); + + @NonNull Set<String> getInstallConstraintsAllowlist(); } } diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingUtils.java index 1d159554e8a7..26822c649db7 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 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. @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.pm.pkg.parsing; +package com.android.internal.pm.pkg.parsing; -import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER; +import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,10 +31,9 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.pm.pkg.component.ParsedIntentInfo; -import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl; import com.android.internal.util.Parcelling; import com.android.internal.util.XmlUtils; -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl; import org.xmlpull.v1.XmlPullParserException; diff --git a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java b/core/java/com/android/internal/pm/split/DefaultSplitAssetLoader.java index 0bb969f488fe..50c62437b192 100644 --- a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java +++ b/core/java/com/android/internal/pm/split/DefaultSplitAssetLoader.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.pm.split; +package com.android.internal.pm.split; import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; @@ -21,9 +21,9 @@ import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils.ParseFlags; import com.android.internal.util.ArrayUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags; import libcore.io.IoUtils; diff --git a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java b/core/java/com/android/internal/pm/split/SplitAssetDependencyLoader.java index 56d92fbc95a2..c166cdcf9451 100644 --- a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java +++ b/core/java/com/android/internal/pm/split/SplitAssetDependencyLoader.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.pm.split; +package com.android.internal.pm.split; import android.annotation.NonNull; import android.content.pm.parsing.ApkLiteParseUtils; @@ -24,8 +24,8 @@ import android.content.res.AssetManager; import android.os.Build; import android.util.SparseArray; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils.ParseFlags; import libcore.io.IoUtils; diff --git a/services/core/java/com/android/server/pm/split/SplitAssetLoader.java b/core/java/com/android/internal/pm/split/SplitAssetLoader.java index 845015916e60..c7c409d826ba 100644 --- a/services/core/java/com/android/server/pm/split/SplitAssetLoader.java +++ b/core/java/com/android/internal/pm/split/SplitAssetLoader.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.server.pm.split; +package com.android.internal.pm.split; import android.content.res.ApkAssets; import android.content.res.AssetManager; diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 28fd2b488426..bf8e6135fd01 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -897,13 +897,26 @@ public class LockPatternUtils { } /** - * Returns true if {@code userHandle} is a managed profile with separate challenge. + * Returns true if {@code userHandle} is a profile with separate challenge. + * <p> + * Returns false if {@code userHandle} is a profile with unified challenge, a profile whose + * credential is not shareable with its parent, or a non-profile user. */ public boolean isSeparateProfileChallengeEnabled(int userHandle) { return isCredentialSharableWithParent(userHandle) && hasSeparateChallenge(userHandle); } /** + * Returns true if {@code userHandle} is a profile with unified challenge. + * <p> + * Returns false if {@code userHandle} is a profile with separate challenge, a profile whose + * credential is not shareable with its parent, or a non-profile user. + */ + public boolean isProfileWithUnifiedChallenge(int userHandle) { + return isCredentialSharableWithParent(userHandle) && !hasSeparateChallenge(userHandle); + } + + /** * Returns true if {@code userHandle} is a managed profile with unified challenge. */ public boolean isManagedProfileWithUnifiedChallenge(int userHandle) { diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java index c88763ce6c97..18d5f6db6ac9 100644 --- a/core/java/com/android/internal/widget/LockscreenCredential.java +++ b/core/java/com/android/internal/widget/LockscreenCredential.java @@ -134,12 +134,12 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { } /** - * Creates a LockscreenCredential object representing a managed password for profile with - * unified challenge. This credentiall will have type {@code CREDENTIAL_TYPE_PASSWORD} for now. - * TODO: consider add a new credential type for this. This can then supersede the - * isLockTiedToParent argument in various places in LSS. + * Creates a LockscreenCredential object representing the system-generated, system-managed + * password for a profile with unified challenge. This credential has type {@code + * CREDENTIAL_TYPE_PASSWORD} for now. TODO: consider add a new credential type for this. This + * can then supersede the isLockTiedToParent argument in various places in LSS. */ - public static LockscreenCredential createManagedPassword(@NonNull byte[] password) { + public static LockscreenCredential createUnifiedProfilePassword(@NonNull byte[] password) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, Arrays.copyOf(password, password.length), /* hasInvalidChars= */ false); } diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java index f86595f1ea7b..adb0c6959f12 100644 --- a/core/java/com/android/server/pm/pkg/AndroidPackage.java +++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java @@ -58,6 +58,7 @@ import com.android.internal.pm.pkg.component.ParsedProcess; import com.android.internal.pm.pkg.component.ParsedProvider; import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.pm.pkg.component.ParsedUsesPermission; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import java.security.PublicKey; import java.util.List; @@ -690,7 +691,7 @@ public interface AndroidPackage { /** * The names of packages to adopt ownership of permissions from, parsed under {@link - * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}. + * ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}. * * @see R.styleable#AndroidManifestOriginalPackage_name * @hide @@ -795,7 +796,7 @@ public interface AndroidPackage { /** * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link - * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_KEY_SETS}. + * ParsingPackageUtils#TAG_KEY_SETS}. * * @see R.styleable#AndroidManifestKeySet * @see R.styleable#AndroidManifestPublicKey @@ -1266,7 +1267,7 @@ public interface AndroidPackage { /** * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in {@link - * com.android.server.pm.pkg.parsing.ParsingPackageUtils#TAG_KEY_SETS}. + * ParsingPackageUtils#TAG_KEY_SETS}. * * @see R.styleable#AndroidManifestUpgradeKeySet * @hide diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp index 87ab4969040e..54c4cd50a902 100644 --- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp +++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp @@ -63,6 +63,7 @@ class NativeCommandBuffer { std::optional<std::pair<char*, char*>> readLine(FailFn fail_fn) { char* result = mBuffer + mNext; while (true) { + // We have scanned up to, but not including mNext for this line's newline. if (mNext == mEnd) { if (mEnd == MAX_COMMAND_BYTES) { return {}; @@ -89,7 +90,7 @@ class NativeCommandBuffer { } else { mNext = nl - mBuffer + 1; if (--mLinesLeft < 0) { - fail_fn("ZygoteCommandBuffer.readLine attempted to read past mEnd of command"); + fail_fn("ZygoteCommandBuffer.readLine attempted to read past end of command"); } return std::make_pair(result, nl); } @@ -125,8 +126,8 @@ class NativeCommandBuffer { mEnd += lineLen + 1; } - // Clear mBuffer, start reading new command, return the number of arguments, leaving mBuffer - // positioned at the beginning of first argument. Return 0 on EOF. + // Start reading new command, return the number of arguments, leaving mBuffer positioned at the + // beginning of first argument. Return 0 on EOF. template<class FailFn> int getCount(FailFn fail_fn) { mLinesLeft = 1; @@ -451,11 +452,14 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( (CREATE_ERROR("Write unexpectedly returned short: %d < 5", res)); } } - // Clear buffer and get count from next command. - n_buffer->clear(); for (;;) { + // Clear buffer and get count from next command. + n_buffer->clear(); // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect. int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */)); + if (poll_res < 0) { + fail_fn_z(CREATE_ERROR("Poll failed: %d: %s", errno, strerror(errno))); + } if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) { if (n_buffer->getCount(fail_fn_z) != 0) { break; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index dd93586c340b..c6a241f2fa62 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4806,6 +4806,13 @@ <permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" android:protectionLevel="signature" /> + <!-- @FlaggedApi("com.android.server.accessibility.motion_event_observing") + @hide + @TestApi + Allows an accessibility service to observe motion events without consuming them. --> + <permission android:name="android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING" + android:protectionLevel="signature" /> + <!-- @hide Allows an application to collect frame statistics --> <permission android:name="android.permission.FRAME_STATS" android:protectionLevel="signature" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 1f6ac80a133c..4596ca74bf8f 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5252,6 +5252,8 @@ <!-- Zen mode - name of default automatic calendar time-based rule that is triggered every night (when sleeping). [CHAR LIMIT=40] --> <string name="zen_mode_default_every_night_name">Sleeping</string> + <!-- Zen mode - Trigger description of the rule, indicating which app owns it. [CHAR_LIMIT=100] --> + <string name="zen_mode_implicit_trigger_description">Managed by <xliff:g id="app_name">%1$s</xliff:g></string> <!-- Zen mode - Condition summary when a rule is activated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] --> <string name="zen_mode_implicit_activated">On</string> <!-- Zen mode - Condition summary when a rule is deactivated due to a call to setInterruptionFilter(). [CHAR_LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5791ddb8addf..fd6158d02b8f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2586,6 +2586,7 @@ <java-symbol type="string" name="zen_mode_default_weekends_name" /> <java-symbol type="string" name="zen_mode_default_events_name" /> <java-symbol type="string" name="zen_mode_default_every_night_name" /> + <java-symbol type="string" name="zen_mode_implicit_trigger_description" /> <java-symbol type="string" name="zen_mode_implicit_activated" /> <java-symbol type="string" name="zen_mode_implicit_deactivated" /> <java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" /> diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java index ba2ea88e8e01..d629f6a8c57c 100644 --- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java +++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java @@ -19,15 +19,20 @@ package android.app; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; +import static org.junit.Assert.assertThrows; + import android.content.ComponentName; import android.net.Uri; import android.os.Parcel; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.google.common.base.Strings; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,6 +43,9 @@ import java.lang.reflect.Field; public class AutomaticZenRuleTest { private static final String CLASS = "android.app.AutomaticZenRule"; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Test public void testLongFields_inConstructor() { String longString = Strings.repeat("A", 65536); @@ -100,6 +108,7 @@ public class AutomaticZenRuleTest { } @Test + @EnableFlags(Flags.FLAG_MODES_API) public void testLongInputsFromParcel() { // Create a rule with long fields, set directly via reflection so that we can confirm that // a rule with too-long fields that comes in via a parcel has its fields truncated directly. @@ -152,6 +161,60 @@ public class AutomaticZenRuleTest { fromParcel.getOwner().getPackageName().length()); assertEquals(AutomaticZenRule.MAX_STRING_LENGTH, fromParcel.getOwner().getClassName().length()); - assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, rule.getTriggerDescription().length()); + assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, fromParcel.getTriggerDescription().length()); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void validate_builderWithValidType_succeeds() throws Exception { + AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")) + .setType(AutomaticZenRule.TYPE_BEDTIME) + .build(); + rule.validate(); // No exception. + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void validate_builderWithoutType_succeeds() throws Exception { + AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build(); + rule.validate(); // No exception. + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void validate_constructorWithoutType_succeeds() throws Exception { + AutomaticZenRule rule = new AutomaticZenRule("rule", new ComponentName("pkg", "cps"), + new ComponentName("pkg", "activity"), Uri.parse("condition"), null, + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + rule.validate(); // No exception. + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void validate_invalidType_throws() throws Exception { + AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build(); + + // Set the field via reflection. + Field typeField = AutomaticZenRule.class.getDeclaredField("mType"); + typeField.setAccessible(true); + typeField.set(rule, 100); + + assertThrows(IllegalArgumentException.class, rule::validate); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void setType_invalidType_throws() { + AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", Uri.parse("uri")).build(); + + assertThrows(IllegalArgumentException.class, () -> rule.setType(100)); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void setTypeBuilder_invalidType_throws() { + AutomaticZenRule.Builder builder = new AutomaticZenRule.Builder("rule", Uri.parse("uri")); + + assertThrows(IllegalArgumentException.class, () -> builder.setType(100)); } } diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java index 8c231de5598f..e7b5dff60110 100644 --- a/core/tests/coretests/src/android/os/BundleTest.java +++ b/core/tests/coretests/src/android/os/BundleTest.java @@ -197,7 +197,6 @@ public class BundleTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void kindofEquals_bothParcelled_same() { Bundle bundle1 = new Bundle(); bundle1.putString("StringKey", "S"); @@ -215,7 +214,6 @@ public class BundleTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void kindofEquals_bothParcelled_different() { Bundle bundle1 = new Bundle(); bundle1.putString("StringKey", "S"); @@ -247,7 +245,6 @@ public class BundleTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void kindofEquals_lazyValues() { Parcelable p1 = new CustomParcelable(13, "Tiramisu"); Parcelable p2 = new CustomParcelable(13, "Tiramisu"); @@ -281,7 +278,6 @@ public class BundleTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void kindofEquals_lazyValuesWithIdenticalParcels_returnsTrue() { Parcelable p1 = new CustomParcelable(13, "Tiramisu"); Parcelable p2 = new CustomParcelable(13, "Tiramisu"); diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java index 8cd6773936ef..851e61259241 100644 --- a/core/tests/coretests/src/android/os/MessageQueueTest.java +++ b/core/tests/coretests/src/android/os/MessageQueueTest.java @@ -16,6 +16,7 @@ package android.os; +import android.platform.test.annotations.IgnoreUnderRavenwood; import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.MediumTest; @@ -153,6 +154,7 @@ public class MessageQueueTest { @Test @MediumTest + @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testFieldIntegrity() throws Exception { TestHandlerThread tester = new TestFieldIntegrityHandler() { diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java index 5bbd2219e2f0..26f6d696768a 100644 --- a/core/tests/coretests/src/android/os/ParcelTest.java +++ b/core/tests/coretests/src/android/os/ParcelTest.java @@ -132,7 +132,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenSameData() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); @@ -169,7 +168,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenDifferentData() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); @@ -186,7 +184,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenLimitOutOfBounds_throws() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); @@ -213,7 +210,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenLengthZero() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); @@ -232,7 +228,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenNegativeLength_throws() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); @@ -248,7 +243,6 @@ public class ParcelTest { } @Test - @IgnoreUnderRavenwood(blockedBy = Parcel.class) public void testCompareDataInRange_whenNegativeOffset_throws() { Parcel pA = Parcel.obtain(); int iA = pA.dataPosition(); diff --git a/core/tests/coretests/src/android/service/notification/ConditionTest.java b/core/tests/coretests/src/android/service/notification/ConditionTest.java index 612562eb22dc..e94273e1ada7 100644 --- a/core/tests/coretests/src/android/service/notification/ConditionTest.java +++ b/core/tests/coretests/src/android/service/notification/ConditionTest.java @@ -21,9 +21,12 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.fail; +import static org.junit.Assert.assertThrows; + import android.app.Flags; import android.net.Uri; import android.os.Parcel; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -36,6 +39,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; @RunWith(AndroidJUnit4.class) @SmallTest @@ -194,4 +198,59 @@ public class ConditionTest { Condition fromParcel = new Condition(parcel); assertThat(fromParcel).isEqualTo(cond); } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void constructor_unspecifiedSource_succeeds() { + new Condition(Uri.parse("id"), "Summary", Condition.STATE_TRUE); + // No exception. + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void constructor_validSource_succeeds() { + new Condition(Uri.parse("id"), "Summary", Condition.STATE_TRUE, Condition.SOURCE_CONTEXT); + // No exception. + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void constructor_invalidSource_throws() { + assertThrows(IllegalArgumentException.class, + () -> new Condition(Uri.parse("uri"), "Summary", Condition.STATE_TRUE, 1000)); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void constructor_parcelWithInvalidSource_throws() { + Condition original = new Condition(Uri.parse("condition"), "Summary", Condition.STATE_TRUE, + Condition.SOURCE_SCHEDULE); + Parcel parcel = Parcel.obtain(); + original.writeToParcel(parcel, 0); + + // Tweak the parcel to contain and invalid source value. + parcel.setDataPosition(parcel.dataPosition() - 8); // going back two int fields. + parcel.writeInt(100); + parcel.setDataPosition(0); + + assertThrows(IllegalArgumentException.class, () -> new Condition(parcel)); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_API) + public void validate_invalidSource_throws() throws Exception { + Condition condition = new Condition(Uri.parse("condition"), "Summary", Condition.STATE_TRUE, + Condition.SOURCE_SCHEDULE); + + Field typeField = Condition.class.getDeclaredField("source"); + + // Reflection on reflection (ugh) to make a final field non-final + Field fieldAccessFlagsField = Field.class.getDeclaredField("accessFlags"); + fieldAccessFlagsField.setAccessible(true); + fieldAccessFlagsField.setInt(typeField, typeField.getModifiers() & ~Modifier.FINAL); + + typeField.setInt(condition, 30); + + assertThrows(IllegalArgumentException.class, condition::validate); + } } diff --git a/core/tests/coretests/src/android/util/SparseSetArrayTest.java b/core/tests/coretests/src/android/util/SparseSetArrayTest.java index 1df1090e0343..1c72185ea93c 100644 --- a/core/tests/coretests/src/android/util/SparseSetArrayTest.java +++ b/core/tests/coretests/src/android/util/SparseSetArrayTest.java @@ -37,6 +37,7 @@ public class SparseSetArrayTest { public final RavenwoodRule mRavenwood = new RavenwoodRule(); @Test + @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testAddAll() { final SparseSetArray<Integer> sparseSetArray = new SparseSetArray<>(); diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 15c90474c017..c8ea3742b3aa 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -34,6 +34,9 @@ import android.app.PendingIntent; import android.appwidget.AppWidgetHostView; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.AsyncTask; @@ -834,33 +837,6 @@ public class RemoteViewsTest { } @Test - public void visitUris_intents() { - RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); - - Uri fillIntentUri = Uri.parse("content://intent/fill"); - views.setOnCheckedChangeResponse( - R.id.layout, - RemoteViews.RemoteResponse.fromFillInIntent(new Intent("action", fillIntentUri))); - - Uri pendingIntentUri = Uri.parse("content://intent/pending"); - PendingIntent pendingIntent = getPendingIntentWithUri(pendingIntentUri); - views.setOnClickResponse( - R.id.layout, - RemoteViews.RemoteResponse.fromPendingIntent(pendingIntent)); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - views.visitUris(visitor); - verify(visitor, times(1)).accept(eq(fillIntentUri)); - verify(visitor, times(1)).accept(eq(pendingIntentUri)); - } - - private PendingIntent getPendingIntentWithUri(Uri uri) { - return PendingIntent.getActivity(mContext, 0, - new Intent("action", uri), - PendingIntent.FLAG_IMMUTABLE); - } - - @Test public void layoutInflaterFactory_nothingSet_returnsNull() { final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); assertNull(rv.getLayoutInflaterFactory()); diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java index f34b185b1c5a..c9536b9b8129 100644 --- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java +++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java @@ -57,6 +57,16 @@ public class LongArrayMultiStateCounterTest { } @Test + public void setValue() { + LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4); + + counter.setValues(0, new long[]{1, 2, 3, 4}); + counter.setValues(1, new long[]{5, 6, 7, 8}); + assertCounts(counter, 0, new long[]{1, 2, 3, 4}); + assertCounts(counter, 1, new long[]{5, 6, 7, 8}); + } + + @Test public void setEnabled() { LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4); counter.setState(0, 1000); diff --git a/core/tests/utiltests/src/android/util/TimeUtilsTest.java b/core/tests/utiltests/src/android/util/TimeUtilsTest.java index e8246c83e086..ac659e1bc593 100644 --- a/core/tests/utiltests/src/android/util/TimeUtilsTest.java +++ b/core/tests/utiltests/src/android/util/TimeUtilsTest.java @@ -18,8 +18,12 @@ package android.util; import static org.junit.Assert.assertEquals; +import android.platform.test.annotations.IgnoreUnderRavenwood; +import android.platform.test.ravenwood.RavenwoodRule; + import androidx.test.runner.AndroidJUnit4; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,6 +33,9 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class TimeUtilsTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + public static final long SECOND_IN_MILLIS = 1000; public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60; public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60; @@ -78,6 +85,7 @@ public class TimeUtilsTest { } @Test + @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testDumpTime() { assertEquals("2023-01-01 00:00:00.000", runWithPrintWriter((pw) -> { TimeUtils.dumpTime(pw, 1672556400000L); @@ -91,6 +99,7 @@ public class TimeUtilsTest { } @Test + @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testFormatForLogging() { assertEquals("unknown", TimeUtils.formatForLogging(0)); assertEquals("unknown", TimeUtils.formatForLogging(-1)); @@ -99,6 +108,7 @@ public class TimeUtilsTest { } @Test + @IgnoreUnderRavenwood(reason = "Flaky test, b/315872700") public void testLogTimeOfDay() { assertEquals("01-01 00:00:00.000", TimeUtils.logTimeOfDay(1672556400000L)); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java index 15d14e87fcf6..b315f94b5d00 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java @@ -241,7 +241,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, for (int i = 0; i < displays.length; i++) { DisplayAddress.Physical address = (DisplayAddress.Physical) displays[i].getAddress(); - if (mRearDisplayAddress == address.getPhysicalDisplayId()) { + if (address != null && mRearDisplayAddress == address.getPhysicalDisplayId()) { rearDisplayMetrics = new DisplayMetrics(); final Display rearDisplay = displays[i]; diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS index f0ed6ee5cb67..e346b51a4f19 100644 --- a/libs/WindowManager/Shell/OWNERS +++ b/libs/WindowManager/Shell/OWNERS @@ -1,4 +1,4 @@ xutan@google.com # Give submodule owners in shell resource approval -per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com +per-file res*/*/*.xml = atsjenk@google.com, hwwang@google.com, jorgegil@google.com, lbill@google.com, madym@google.com, nmusgrave@google.com, pbdr@google.com, tkachenkoi@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index b1b196d40357..fe65fdd30e48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -16,6 +16,7 @@ package com.android.wm.shell; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; @@ -168,6 +169,13 @@ public class ShellTaskOrganizer extends TaskOrganizer implements private final Object mLock = new Object(); private StartingWindowController mStartingWindow; + /** Overlay surface for home root task */ + private final SurfaceControl mHomeTaskOverlayContainer = new SurfaceControl.Builder() + .setName("home_task_overlay_container") + .setContainerLayer() + .setHidden(false) + .build(); + /** * In charge of showing compat UI. Can be {@code null} if the device doesn't support size * compat or if this isn't the main {@link ShellTaskOrganizer}. @@ -428,6 +436,14 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } } + /** + * Returns a surface which can be used to attach overlays to the home root task + */ + @NonNull + public SurfaceControl getHomeTaskOverlayContainer() { + return mHomeTaskOverlayContainer; + } + @Override public void addStartingWindow(StartingWindowInfo info) { if (mStartingWindow != null) { @@ -485,6 +501,15 @@ public class ShellTaskOrganizer extends TaskOrganizer implements if (mUnfoldAnimationController != null) { mUnfoldAnimationController.onTaskAppeared(info.getTaskInfo(), info.getLeash()); } + + if (info.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) { + ProtoLog.v(WM_SHELL_TASK_ORG, "Adding overlay to home task"); + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setLayer(mHomeTaskOverlayContainer, Integer.MAX_VALUE); + t.reparent(mHomeTaskOverlayContainer, info.getLeash()); + t.apply(); + } + notifyLocusVisibilityIfNeeded(info.getTaskInfo()); notifyCompatUI(info.getTaskInfo(), listener); mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskAdded(info.getTaskInfo())); @@ -579,6 +604,12 @@ public class ShellTaskOrganizer extends TaskOrganizer implements notifyCompatUI(taskInfo, null /* taskListener */); // Notify the recent tasks that a task has been removed mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo)); + if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.reparent(mHomeTaskOverlayContainer, null); + t.apply(); + ProtoLog.v(WM_SHELL_TASK_ORG, "Removing overlay surface"); + } if (!ENABLE_SHELL_TRANSITIONS && (appearedInfo.getLeash() != null)) { // Preemptively clean up the leash only if shell transitions are not enabled diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 0f0fbd9cc12f..f801b0d01084 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -83,6 +83,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { private int mStartPos; private GestureDetector mDoubleTapDetector; private boolean mInteractive; + private boolean mHideHandle; private boolean mSetTouchRegion = true; private int mLastDraggingPosition; private int mHandleRegionWidth; @@ -211,11 +212,8 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { } /** Sets up essential dependencies of the divider bar. */ - public void setup( - SplitLayout layout, - SplitWindowManager splitWindowManager, - SurfaceControlViewHost viewHost, - InsetsState insetsState) { + public void setup(SplitLayout layout, SplitWindowManager splitWindowManager, + SurfaceControlViewHost viewHost, InsetsState insetsState) { mSplitLayout = layout; mSplitWindowManager = splitWindowManager; mViewHost = viewHost; @@ -277,6 +275,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { R.dimen.docked_stack_divider_lift_elevation); mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener()); mInteractive = true; + mHideHandle = false; setOnTouchListener(this); mHandle.setAccessibilityDelegate(mHandleDelegate); setWillNotDraw(false); @@ -469,10 +468,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { void setInteractive(boolean interactive, boolean hideHandle, String from) { if (interactive == mInteractive) return; ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, - "Set divider bar %s from %s", interactive ? "interactive" : "non-interactive", - from); + "Set divider bar %s hide handle=%b from %s", + interactive ? "interactive" : "non-interactive", hideHandle, from); mInteractive = interactive; - if (!mInteractive && hideHandle && mMoving) { + mHideHandle = hideHandle; + if (!mInteractive && mHideHandle && mMoving) { final int position = mSplitLayout.getDividePosition(); mSplitLayout.flingDividePosition( mLastDraggingPosition, @@ -482,7 +482,15 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mMoving = false; } releaseTouching(); - mHandle.setVisibility(!mInteractive && hideHandle ? View.INVISIBLE : View.VISIBLE); + mHandle.setVisibility(!mInteractive && mHideHandle ? View.INVISIBLE : View.VISIBLE); + } + + boolean isInteractive() { + return mInteractive; + } + + boolean isHandleHidden() { + return mHideHandle; } private class DoubleTapListener extends GestureDetector.SimpleOnGestureListener { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index b699533374df..53caddb52f23 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -59,6 +59,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.animation.Interpolators; @@ -70,6 +71,7 @@ import com.android.wm.shell.common.InteractionJankMonitorUtils; import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.io.PrintWriter; import java.util.function.Consumer; @@ -420,7 +422,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public void init() { if (mInitialized) return; mInitialized = true; - mSplitWindowManager.init(this, mInsetsState); + mSplitWindowManager.init(this, mInsetsState, false /* isRestoring */); mDisplayImeController.addPositionProcessor(mImePositionProcessor); } @@ -442,14 +444,19 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } /** Releases and re-inflates {@link DividerView} on the root surface. */ - public void update(SurfaceControl.Transaction t) { + public void update(SurfaceControl.Transaction t, boolean resetImePosition) { if (!mInitialized) { init(); return; } mSplitWindowManager.release(t); - mImePositionProcessor.reset(); - mSplitWindowManager.init(this, mInsetsState); + if (resetImePosition) { + mImePositionProcessor.reset(); + } + mSplitWindowManager.init(this, mInsetsState, true /* isRestoring */); + // Update the surface positions again after recreating the divider in case nothing else + // triggers it + mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this); } @Override @@ -868,6 +875,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange pw.println(prefix + TAG + ":"); pw.println(innerPrefix + "mAllowLeftRightSplitInPortrait=" + mAllowLeftRightSplitInPortrait); pw.println(innerPrefix + "mIsLeftRightSplit=" + mIsLeftRightSplit); + pw.println(innerPrefix + "mFreezeDividerWindow=" + mFreezeDividerWindow); + pw.println(innerPrefix + "mDimNonImeSide=" + mDimNonImeSide); + pw.println(innerPrefix + "mDividerPosition=" + mDividerPosition); pw.println(innerPrefix + "bounds1=" + mBounds1.toShortString()); pw.println(innerPrefix + "dividerBounds=" + mDividerBounds.toShortString()); pw.println(innerPrefix + "bounds2=" + mBounds2.toShortString()); @@ -1151,14 +1161,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mTargetYOffset = needOffset ? getTargetYOffset() : 0; if (mTargetYOffset != mLastYOffset) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "Split IME animation starting, fromY=%d toY=%d", + mLastYOffset, mTargetYOffset); // Freeze the configuration size with offset to prevent app get a configuration // changed or relaunch. This is required to make sure client apps will calculate // insets properly after layout shifted. if (mTargetYOffset == 0) { mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this); } else { - mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset - mLastYOffset, - SplitLayout.this); + mSplitLayoutHandler.setLayoutOffsetTarget(0, mTargetYOffset, SplitLayout.this); } } @@ -1183,6 +1195,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public void onImeEndPositioning(int displayId, boolean cancel, SurfaceControl.Transaction t) { if (displayId != mDisplayId || !mHasImeFocus || cancel) return; + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "Split IME animation ending, canceled=%b", cancel); onProgress(1.0f); mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 00361d9dd9cf..8fb9bda539a0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -62,6 +62,10 @@ public final class SplitWindowManager extends WindowlessWindowManager { // Used to "pass" a transaction to WWM.remove so that view removal can be synchronized. private SurfaceControl.Transaction mSyncTransaction = null; + // For saving/restoring state + private boolean mLastDividerInteractive = true; + private boolean mLastDividerHandleHidden; + public interface ParentContainerCallbacks { void attachToParentSurface(SurfaceControl.Builder b); void onLeashReady(SurfaceControl leash); @@ -107,7 +111,7 @@ public final class SplitWindowManager extends WindowlessWindowManager { } /** Inflates {@link DividerView} on to the root surface. */ - void init(SplitLayout splitLayout, InsetsState insetsState) { + void init(SplitLayout splitLayout, InsetsState insetsState, boolean isRestoring) { if (mDividerView != null || mViewHost != null) { throw new UnsupportedOperationException( "Try to inflate divider view again without release first"); @@ -130,6 +134,10 @@ public final class SplitWindowManager extends WindowlessWindowManager { lp.accessibilityTitle = mContext.getResources().getString(R.string.accessibility_divider); mViewHost.setView(mDividerView, lp); mDividerView.setup(splitLayout, this, mViewHost, insetsState); + if (isRestoring) { + mDividerView.setInteractive(mLastDividerInteractive, mLastDividerHandleHidden, + "restore_setup"); + } } /** @@ -138,6 +146,8 @@ public final class SplitWindowManager extends WindowlessWindowManager { */ void release(@Nullable SurfaceControl.Transaction t) { if (mDividerView != null) { + mLastDividerInteractive = mDividerView.isInteractive(); + mLastDividerHandleHidden = mDividerView.isHandleHidden(); mDividerView = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java index ef763ec45994..afd3b14e8b1d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.TaskInfo; import android.content.Context; +import android.content.Intent; import android.graphics.Rect; import android.os.SystemClock; import android.view.LayoutInflater; @@ -227,9 +228,12 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract } private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) { + final Intent intent = taskInfo.baseIntent; return taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton && (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed || taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled) + && Intent.ACTION_MAIN.equals(intent.getAction()) + && intent.hasCategory(Intent.CATEGORY_LAUNCHER) && (!mUserAspectRatioButtonShownChecker.get() || isShowingButton()); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS index deb7c6db338f..1385f42bc676 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS @@ -1,3 +1,7 @@ # WM shell sub-module desktop owners atsjenk@google.com +jorgegil@google.com madym@google.com +nmusgrave@google.com +pbdr@google.com +tkachenkoi@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS index a3803ed82844..8a0eea0a9bdd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS @@ -2,3 +2,6 @@ atsjenk@google.com jorgegil@google.com madym@google.com +nmusgrave@google.com +pbdr@google.com +tkachenkoi@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index f5c01d063707..4c477373c32c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -769,7 +769,6 @@ public class PipAnimationController { getSurfaceTransactionHelper().crop(tx, leash, destBounds); } if (mContentOverlay != null) { - mContentOverlay.onAnimationEnd(tx, destBounds); clearContentOverlay(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java index a2bd47c5285e..e11e8596a7fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java @@ -67,15 +67,6 @@ public abstract class PipContentOverlay { public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx, Rect currentBounds, float fraction); - /** - * Callback when reaches the end of animation on the internal {@link #mLeash}. - * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly - * call apply on this transaction, it should be applied on the caller side. - * @param destinationBounds {@link Rect} of the final bounds. - */ - public abstract void onAnimationEnd(SurfaceControl.Transaction atomicTx, - Rect destinationBounds); - /** A {@link PipContentOverlay} uses solid color. */ public static final class PipColorOverlay extends PipContentOverlay { private static final String TAG = PipColorOverlay.class.getSimpleName(); @@ -107,11 +98,6 @@ public abstract class PipContentOverlay { atomicTx.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2); } - @Override - public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) { - // Do nothing. Color overlay should be fully opaque by now, ready for fade out. - } - private float[] getContentOverlayColor(Context context) { final TypedArray ta = context.obtainStyledAttributes(new int[] { android.R.attr.colorBackground }); @@ -164,11 +150,6 @@ public abstract class PipContentOverlay { Rect currentBounds, float fraction) { // Do nothing. Keep the snapshot till animation ends. } - - @Override - public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) { - // Do nothing. Snapshot overlay should be fully opaque by now, ready for fade out. - } } /** A {@link PipContentOverlay} shows app icon on solid color background. */ @@ -255,11 +236,6 @@ public abstract class PipContentOverlay { } @Override - public void onAnimationEnd(SurfaceControl.Transaction atomicTx, Rect destinationBounds) { - // Do nothing. Icon overlay should be fully opaque by now, ready for fade out. - } - - @Override public void detach(SurfaceControl.Transaction tx) { super.detach(tx); if (mBitmap != null && !mBitmap.isRecycled()) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 743b1ea197bc..3635165d76ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -329,15 +329,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private @Surface.Rotation int mCurrentRotation; /** - * An optional overlay used to mask content changing between an app in/out of PiP, only set if - * {@link PipTransitionState#getInSwipePipToHomeTransition()} is true, only in gesture nav. - */ - @Nullable - SurfaceControl mSwipePipToHomeOverlay; - - /** - * An optional overlay used to mask content changing between an app in/out of PiP, only set if - * {@link PipTransitionState#getInSwipePipToHomeTransition()} is false. + * An optional overlay used to mask content changing between an app in/out of PiP. */ @Nullable SurfaceControl mPipOverlay; @@ -480,7 +472,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } mPipBoundsState.setBounds(destinationBounds); - mSwipePipToHomeOverlay = overlay; + mPipOverlay = overlay; if (ENABLE_SHELL_TRANSITIONS && overlay != null) { // With Shell transition, the overlay was attached to the remote transition leash, which // will be removed when the current transition is finished, so we need to reparent it @@ -892,7 +884,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } final Rect destinationBounds = mPipBoundsState.getBounds(); - final SurfaceControl swipeToHomeOverlay = mSwipePipToHomeOverlay; + final SurfaceControl swipeToHomeOverlay = mPipOverlay; final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper .resetScale(tx, mLeash, destinationBounds) @@ -911,7 +903,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } }, tx); mPipTransitionState.setInSwipePipToHomeTransition(false); - mSwipePipToHomeOverlay = null; + mPipOverlay = null; } private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable, @@ -1126,9 +1118,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } clearWaitForFixedRotation(); - if (mSwipePipToHomeOverlay != null) { - removeContentOverlay(mSwipePipToHomeOverlay, null /* callback */); - mSwipePipToHomeOverlay = null; + if (mPipOverlay != null) { + removeContentOverlay(mPipOverlay, null /* callback */); + mPipOverlay = null; } resetShadowRadius(); mPipTransitionState.setInSwipePipToHomeTransition(false); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 0f3c16220dee..f5f15d81ea44 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -465,7 +465,7 @@ public class PipTransition extends PipTransitionController { mSurfaceTransactionHelper.crop(tx, leash, destinationBounds) .resetScale(tx, leash, destinationBounds) .round(tx, leash, true /* applyCornerRadius */); - if (mPipOrganizer.mSwipePipToHomeOverlay != null && !mInitBounds.isEmpty()) { + if (mPipOrganizer.mPipOverlay != null && !mInitBounds.isEmpty()) { // Resetting the scale for pinned task while re-adjusting its crop, // also scales the overlay. So we need to update the overlay leash too. Rect overlayBounds = new Rect(destinationBounds); @@ -476,7 +476,7 @@ public class PipTransition extends PipTransitionController { (destinationBounds.width() - overlaySize) / 2, (destinationBounds.height() - overlaySize) / 2); mSurfaceTransactionHelper.resetScale(tx, - mPipOrganizer.mSwipePipToHomeOverlay, overlayBounds); + mPipOrganizer.mPipOverlay, overlayBounds); } } mInitBounds.setEmpty(); @@ -615,9 +615,9 @@ public class PipTransition extends PipTransitionController { } } // if overlay is present remove it immediately, as exit transition came before it faded out - if (mPipOrganizer.mSwipePipToHomeOverlay != null) { - startTransaction.remove(mPipOrganizer.mSwipePipToHomeOverlay); - clearSwipePipToHomeOverlay(); + if (mPipOrganizer.mPipOverlay != null) { + startTransaction.remove(mPipOrganizer.mPipOverlay); + clearPipOverlay(); } if (pipChange == null) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, @@ -1077,7 +1077,7 @@ public class PipTransition extends PipTransitionController { if (mFixedRotationState == FIXED_ROTATION_CALLBACK && appBounds != null) { mInitBounds.set(appBounds); } - final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay; + final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mPipOverlay; if (swipePipToHomeOverlay != null) { // Launcher fade in the overlay on top of the fullscreen Task. It is possible we // reparent the PIP activity to a new PIP task (in case there are other activities @@ -1106,7 +1106,7 @@ public class PipTransition extends PipTransitionController { sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP); if (swipePipToHomeOverlay != null) { mPipOrganizer.fadeOutAndRemoveOverlay(swipePipToHomeOverlay, - this::clearSwipePipToHomeOverlay /* callback */, false /* withStartDelay */); + this::clearPipOverlay /* callback */, false /* withStartDelay */); } mPipTransitionState.setInSwipePipToHomeTransition(false); } @@ -1250,8 +1250,8 @@ public class PipTransition extends PipTransitionController { mPipMenuController.updateMenuBounds(destinationBounds); } - private void clearSwipePipToHomeOverlay() { - mPipOrganizer.mSwipePipToHomeOverlay = null; + private void clearPipOverlay() { + mPipOrganizer.mPipOverlay = null; } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index 0367ba160605..d023cea6d19d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -995,16 +995,10 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { t.show(mOpeningTasks.get(i).mTaskSurface); } for (int i = 0; i < mPausingTasks.size(); ++i) { - if (!sendUserLeaveHint && mPausingTasks.get(i).isLeaf()) { - // This means recents is not *actually* finishing, so of course we gotta - // do special stuff in WMCore to accommodate. - wct.setDoNotPip(mPausingTasks.get(i).mToken); - } - // Since we will reparent out of the leashes, pre-emptively hide the child - // surface to match the leash. Otherwise, there will be a flicker before the - // visibility gets committed in Core when using split-screen (in splitscreen, - // the leaf-tasks are not "independent" so aren't hidden by normal setup). - t.hide(mPausingTasks.get(i).mTaskSurface); + cleanUpPausingOrClosingTask(mPausingTasks.get(i), wct, t, sendUserLeaveHint); + } + for (int i = 0; i < mClosingTasks.size(); ++i) { + cleanUpPausingOrClosingTask(mClosingTasks.get(i), wct, t, sendUserLeaveHint); } if (mPipTransaction != null && sendUserLeaveHint) { SurfaceControl pipLeash = null; @@ -1053,6 +1047,20 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } } + private void cleanUpPausingOrClosingTask(TaskState task, WindowContainerTransaction wct, + SurfaceControl.Transaction finishTransaction, boolean sendUserLeaveHint) { + if (!sendUserLeaveHint && task.isLeaf()) { + // This means recents is not *actually* finishing, so of course we gotta + // do special stuff in WMCore to accommodate. + wct.setDoNotPip(task.mToken); + } + // Since we will reparent out of the leashes, pre-emptively hide the child + // surface to match the leash. Otherwise, there will be a flicker before the + // visibility gets committed in Core when using split-screen (in splitscreen, + // the leaf-tasks are not "independent" so aren't hidden by normal setup). + finishTransaction.hide(task.mTaskSurface); + } + @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 449bef5608c9..77427d999aaf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -1666,7 +1666,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void finishEnterSplitScreen(SurfaceControl.Transaction finishT) { - mSplitLayout.update(finishT); + mSplitLayout.update(finishT, true /* resetImePosition */); mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash, getMainStageBounds()); mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash, @@ -1860,9 +1860,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration) && mMainStage.isActive()) { // Clear the divider remote animating flag as the divider will be re-rendered to apply - // the new rotation config. + // the new rotation config. Don't reset the IME state since those updates are not in + // sync with task info changes. mIsDividerRemoteAnimating = false; - mSplitLayout.update(null /* t */); + mSplitLayout.update(null /* t */, false /* resetImePosition */); onLayoutSizeChanged(mSplitLayout); } } @@ -2325,7 +2326,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ public void updateSurfaces(SurfaceControl.Transaction transaction) { updateSurfaceBounds(mSplitLayout, transaction, /* applyResizingOffset */ false); - mSplitLayout.update(transaction); + mSplitLayout.update(transaction, true /* resetImePosition */); } private void onDisplayChange(int displayId, int fromRotation, int toRotation, @@ -2598,7 +2599,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final TransitionInfo.Change change = info.getChanges().get(iC); if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) { - mSplitLayout.update(startTransaction); + // Don't reset the IME state since those updates are not in sync with the + // display change transition + mSplitLayout.update(startTransaction, false /* resetImePosition */); } if (mMixedHandler.isEnteringPip(change, transitType)) { @@ -2699,7 +2702,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, startTransaction, finishTransaction, finishCallback)) { if (mSplitTransitions.isPendingResize(transition)) { // Only need to update in resize because divider exist before transition. - mSplitLayout.update(startTransaction); + mSplitLayout.update(startTransaction, true /* resetImePosition */); startTransaction.apply(); } return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index e03f82526bdb..34c015f05c68 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -330,6 +330,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { continue; } if (isHide) { + if (pending.mType == TRANSIT_TO_BACK) { + // TO_BACK is only used when setting the task view visibility immediately, + // so in that case we can also hide the surface immediately + startTransaction.hide(chg.getLeash()); + } tv.prepareHideAnimation(finishTransaction); } else { tv.prepareCloseAnimation(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl index 644a6a5114a7..7f4a8f1d476a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/IShellTransitions.aidl @@ -16,6 +16,7 @@ package com.android.wm.shell.transition; +import android.view.SurfaceControl; import android.window.RemoteTransition; import android.window.TransitionFilter; @@ -42,6 +43,13 @@ interface IShellTransitions { */ IBinder getShellApplyToken() = 3; - /** Set listener that will receive callbacks about transitions involving home activity */ + /** + * Set listener that will receive callbacks about transitions involving home activity. + */ oneway void setHomeTransitionListener(in IHomeTransitionListener listener) = 4; + + /** + * Returns a container surface for the home root task. + */ + SurfaceControl getHomeTaskOverlayContainer() = 5; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index b98762d5e104..af69b5272ad5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -64,7 +64,6 @@ import android.window.TransitionInfo; import android.window.TransitionMetrics; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; -import android.window.WindowOrganizer; import androidx.annotation.BinderThread; @@ -72,6 +71,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; +import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; @@ -172,7 +172,7 @@ public class Transitions implements RemoteCallable<Transitions>, /** Transition to animate task to desktop. */ public static final int TRANSIT_MOVE_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 15; - private final WindowOrganizer mOrganizer; + private final ShellTaskOrganizer mOrganizer; private final Context mContext; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; @@ -264,7 +264,7 @@ public class Transitions implements RemoteCallable<Transitions>, public Transitions(@NonNull Context context, @NonNull ShellInit shellInit, @NonNull ShellController shellController, - @NonNull WindowOrganizer organizer, + @NonNull ShellTaskOrganizer organizer, @NonNull TransactionPool pool, @NonNull DisplayController displayController, @NonNull ShellExecutor mainExecutor, @@ -280,7 +280,7 @@ public class Transitions implements RemoteCallable<Transitions>, @NonNull ShellInit shellInit, @Nullable ShellCommandHandler shellCommandHandler, @NonNull ShellController shellController, - @NonNull WindowOrganizer organizer, + @NonNull ShellTaskOrganizer organizer, @NonNull TransactionPool pool, @NonNull DisplayController displayController, @NonNull ShellExecutor mainExecutor, @@ -1240,6 +1240,10 @@ public class Transitions implements RemoteCallable<Transitions>, } } + private SurfaceControl getHomeTaskOverlayContainer() { + return mOrganizer.getHomeTaskOverlayContainer(); + } + /** * Interface for a callback that must be called after a TransitionHandler finishes playing an * animation. @@ -1470,6 +1474,17 @@ public class Transitions implements RemoteCallable<Transitions>, listener); }); } + + @Override + public SurfaceControl getHomeTaskOverlayContainer() { + SurfaceControl[] result = new SurfaceControl[1]; + executeRemoteCallWithTaskPermission(mTransitions, "getHomeTaskOverlayContainer", + (controller) -> { + result[0] = controller.getHomeTaskOverlayContainer(); + }, true /* blocking */); + // Return a copy as writing to parcel releases the original surface + return new SurfaceControl(result[0], "Transitions.HomeOverlay"); + } } private class SettingsObserver extends ContentObserver { diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS index deebad545c5e..d718e157afdb 100644 --- a/libs/WindowManager/Shell/tests/OWNERS +++ b/libs/WindowManager/Shell/tests/OWNERS @@ -9,3 +9,6 @@ hwwang@google.com chenghsiuchang@google.com atsjenk@google.com jorgegil@google.com +nmusgrave@google.com +pbdr@google.com +tkachenkoi@google.com diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java index 145c8f0ab8af..636c6326d213 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java @@ -69,7 +69,7 @@ public class DividerViewTest extends ShellTestCase { SplitWindowManager splitWindowManager = new SplitWindowManager("TestSplitWindowManager", mContext, configuration, mCallbacks); - splitWindowManager.init(mSplitLayout, new InsetsState()); + splitWindowManager.init(mSplitLayout, new InsetsState(), false /* isRestoring */); mDividerView = spy((DividerView) splitWindowManager.getDividerView()); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java index 2e5078d86a8b..150aa13f2d00 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java @@ -59,7 +59,7 @@ public class SplitWindowManagerTests extends ShellTestCase { @Test @UiThreadTest public void testInitRelease() { - mSplitWindowManager.init(mSplitLayout, new InsetsState()); + mSplitWindowManager.init(mSplitLayout, new InsetsState(), false /* isRestoring */); assertThat(mSplitWindowManager.getSurfaceControl()).isNotNull(); mSplitWindowManager.release(null /* t */); assertThat(mSplitWindowManager.getSurfaceControl()).isNull(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java index 065293960da7..9fe2cb11e804 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java @@ -16,6 +16,9 @@ package com.android.wm.shell.compatui; +import static android.content.Intent.ACTION_MAIN; +import static android.content.Intent.CATEGORY_LAUNCHER; +import static android.hardware.usb.UsbManager.ACTION_USB_STATE; import static android.view.WindowInsets.Type.navigationBars; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -33,6 +36,7 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.app.TaskInfo; import android.content.ComponentName; +import android.content.Intent; import android.content.res.Configuration; import android.graphics.Rect; import android.testing.AndroidTestingRunner; @@ -108,7 +112,7 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { MockitoAnnotations.initMocks(this); mExecutor = new TestShellExecutor(); mTaskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ - false, /* topActivityBoundsLetterboxed */ true); + false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER); mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo, mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(), mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0, @@ -179,7 +183,7 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { // No diff clearInvocations(mWindowManager); TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ - true, /* topActivityBoundsLetterboxed */ true); + true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER); assertTrue(mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true)); verify(mWindowManager, never()).updateSurfacePosition(); @@ -200,7 +204,24 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { clearInvocations(mWindowManager); clearInvocations(mLayout); taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ - false, /* topActivityBoundsLetterboxed */ true); + false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER); + assertFalse( + mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true)); + verify(mWindowManager).release(); + + // Recreate button + clearInvocations(mWindowManager); + taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ + true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER); + assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true)); + + verify(mWindowManager).release(); + verify(mWindowManager).createLayout(/* canShow= */ true); + + // Change has no launcher category and is not main intent, dispose the component + clearInvocations(mWindowManager); + taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ + true, /* topActivityBoundsLetterboxed */ true, ACTION_USB_STATE, ""); assertFalse( mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true)); verify(mWindowManager).release(); @@ -217,7 +238,7 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { // inflated clearInvocations(mWindowManager); TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ - false, /* topActivityBoundsLetterboxed */ true); + false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER); mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true); verify(mWindowManager, never()).inflateLayout(); @@ -225,7 +246,7 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { // Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated. clearInvocations(mWindowManager); taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ - true, /* topActivityBoundsLetterboxed */ true); + true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER); mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true); verify(mWindowManager).inflateLayout(); @@ -304,7 +325,7 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { clearInvocations(mWindowManager); spyOn(mWindowManager); TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */ - true, /* topActivityBoundsLetterboxed */ true); + true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER); // User aspect ratio settings button has not yet been shown. doReturn(false).when(mUserAspectRatioButtonShownChecker).get(); @@ -378,7 +399,7 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { } private static TaskInfo createTaskInfo(boolean eligibleForUserAspectRatioButton, - boolean topActivityBoundsLetterboxed) { + boolean topActivityBoundsLetterboxed, String action, String category) { ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); taskInfo.taskId = TASK_ID; taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton = @@ -386,6 +407,7 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase { taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed = topActivityBoundsLetterboxed; taskInfo.configuration.uiMode &= ~Configuration.UI_MODE_TYPE_DESK; taskInfo.realActivity = new ComponentName("com.mypackage.test", "TestActivity"); + taskInfo.baseIntent = new Intent(action).addCategory(category); return taskInfo; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java index 50802c3759c9..66efa02de764 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java @@ -40,12 +40,12 @@ import android.os.RemoteException; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.TransitionInfo.TransitionMode; -import android.window.WindowOrganizer; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; @@ -68,7 +68,7 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class HomeTransitionObserverTest extends ShellTestCase { - private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class); + private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class); private final TransactionPool mTransactionPool = mock(TransactionPool.class); private final Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 01c9bd0cb9f7..e22bf3de30e4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -87,7 +87,6 @@ import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; -import android.window.WindowOrganizer; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -98,6 +97,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.R; import com.android.internal.policy.TransitionAnimation; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; +import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; @@ -130,7 +130,7 @@ import java.util.function.Function; @RunWith(AndroidJUnit4.class) public class ShellTransitionTests extends ShellTestCase { - private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class); + private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class); private final TransactionPool mTransactionPool = mock(TransactionPool.class); private final Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 79a735786c38..47411701e5ab 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -79,14 +79,6 @@ cc_defaults { "external/skia/src/core", ], - product_variables: { - eng: { - lto: { - never: true, - }, - }, - }, - target: { android: { include_dirs: [ diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 8445032293dd..69718a6c4b3e 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -43,12 +43,15 @@ cc_test { }, shared_libs: [ "libandroid_runtime", + "libbase", + "libinput", "libinputservice", "libhwui", "libgui", "libutils", ], static_libs: [ + "libflagtest", "libgmock", "libgtest", ], diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index d9efd3c2fd83..adfa91e96ebb 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <com_android_input_flags.h> +#include <flag_macros.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <input/PointerController.h> @@ -28,6 +30,8 @@ namespace android { +namespace input_flags = com::android::input::flags; + enum TestCursorType { CURSOR_TYPE_DEFAULT = 0, CURSOR_TYPE_HOVER, @@ -261,7 +265,20 @@ TEST_F(PointerControllerTest, useStylusTypeForStylusHover) { mPointerController->reloadPointerResources(); } -TEST_F(PointerControllerTest, updatePointerIcon) { +TEST_F_WITH_FLAGS(PointerControllerTest, setPresentationBeforeDisplayViewportDoesNotLoadResources, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(input_flags, enable_pointer_choreographer))) { + // Setting the presentation mode before a display viewport is set will not load any resources. + mPointerController->setPresentation(PointerController::Presentation::POINTER); + ASSERT_TRUE(mPolicy->noResourcesAreLoaded()); + + // When the display is set, then the resources are loaded. + ensureDisplayViewportIsSet(); + ASSERT_TRUE(mPolicy->allResourcesAreLoaded()); +} + +TEST_F_WITH_FLAGS(PointerControllerTest, updatePointerIcon, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(input_flags, + enable_pointer_choreographer))) { ensureDisplayViewportIsSet(); mPointerController->setPresentation(PointerController::Presentation::POINTER); mPointerController->unfade(PointerController::Transition::IMMEDIATE); @@ -277,6 +294,24 @@ TEST_F(PointerControllerTest, updatePointerIcon) { mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type)); } +TEST_F_WITH_FLAGS(PointerControllerTest, updatePointerIconWithChoreographer, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(input_flags, enable_pointer_choreographer))) { + // When PointerChoreographer is enabled, the presentation mode is set before the viewport. + mPointerController->setPresentation(PointerController::Presentation::POINTER); + ensureDisplayViewportIsSet(); + mPointerController->unfade(PointerController::Transition::IMMEDIATE); + + int32_t type = CURSOR_TYPE_ADDITIONAL; + std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type); + EXPECT_CALL(*mPointerSprite, setVisible(true)); + EXPECT_CALL(*mPointerSprite, setAlpha(1.0f)); + EXPECT_CALL(*mPointerSprite, + setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(type)), + Field(&SpriteIcon::hotSpotX, hotspot.first), + Field(&SpriteIcon::hotSpotY, hotspot.second)))); + mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type)); +} + TEST_F(PointerControllerTest, setCustomPointerIcon) { ensureDisplayViewportIsSet(); mPointerController->unfade(PointerController::Transition::IMMEDIATE); diff --git a/media/OWNERS b/media/OWNERS index 4a6648e91af4..994a7b810009 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -21,7 +21,6 @@ wonsik@google.com include platform/frameworks/av:/media/janitors/media_solutions_OWNERS # SEO -sungsoo@google.com # SEA/KIR/BVE jtinker@google.com diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS index bbe5e06bb282..058c5be6af6c 100644 --- a/media/java/android/media/OWNERS +++ b/media/java/android/media/OWNERS @@ -2,7 +2,6 @@ fgoldfain@google.com elaurent@google.com lajos@google.com -sungsoo@google.com jmtrivi@google.com # go/android-fwk-media-solutions for info on areas of ownership. diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index 901ea46ba38b..a8ffd2b4dd8f 100644 --- a/media/java/android/media/tv/ITvInputManager.aidl +++ b/media/java/android/media/tv/ITvInputManager.aidl @@ -110,6 +110,10 @@ interface ITvInputManager { void pauseRecording(in IBinder sessionToken, in Bundle params, int userId); void resumeRecording(in IBinder sessionToken, in Bundle params, int userId); + // For playback control + void startPlayback(in IBinder sessionToken, int userId); + void stopPlayback(in IBinder sessionToken, int mode, int userId); + // For broadcast info void requestBroadcastInfo(in IBinder sessionToken, in BroadcastInfoRequest request, int userId); void removeBroadcastInfo(in IBinder sessionToken, int id, int userId); diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl index 5246f5c4dc1e..e37ee6e342e5 100644 --- a/media/java/android/media/tv/ITvInputSession.aidl +++ b/media/java/android/media/tv/ITvInputSession.aidl @@ -63,6 +63,9 @@ oneway interface ITvInputSession { void timeShiftSetMode(int mode); void timeShiftEnablePositionTracking(boolean enable); + void startPlayback(); + void stopPlayback(int mode); + // For the recording session void startRecording(in Uri programUri, in Bundle params); void stopRecording(); diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java index d749b91e3889..ae3ee6535c71 100644 --- a/media/java/android/media/tv/ITvInputSessionWrapper.java +++ b/media/java/android/media/tv/ITvInputSessionWrapper.java @@ -79,6 +79,8 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand private static final int DO_TIME_SHIFT_SET_MODE = 30; private static final int DO_SET_TV_MESSAGE_ENABLED = 31; private static final int DO_NOTIFY_TV_MESSAGE = 32; + private static final int DO_STOP_PLAYBACK = 33; + private static final int DO_START_PLAYBACK = 34; private final boolean mIsRecordingSession; private final HandlerCaller mCaller; @@ -286,6 +288,14 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand mTvInputSessionImpl.onTvMessageReceived((Integer) args.arg1, (Bundle) args.arg2); break; } + case DO_STOP_PLAYBACK: { + mTvInputSessionImpl.stopPlayback(msg.arg1); + break; + } + case DO_START_PLAYBACK: { + mTvInputSessionImpl.startPlayback(); + break; + } default: { Log.w(TAG, "Unhandled message code: " + msg.what); break; @@ -483,6 +493,17 @@ public class ITvInputSessionWrapper extends ITvInputSession.Stub implements Hand enabled)); } + @Override + public void stopPlayback(int mode) { + mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_STOP_PLAYBACK, mode)); + } + + @Override + public void startPlayback() { + mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_START_PLAYBACK)); + } + + private final class TvInputEventReceiver extends InputEventReceiver { TvInputEventReceiver(InputChannel inputChannel, Looper looper) { super(inputChannel, looper); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 631ab9a2693d..c685a5adb08b 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -339,6 +339,14 @@ public final class TvInputManager { */ public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = VIDEO_UNAVAILABLE_REASON_END; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * it has been stopped by stopPlayback. + * @hide + */ + public static final int VIDEO_UNAVAILABLE_REASON_STOPPED = 19; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({TIME_SHIFT_STATUS_UNKNOWN, TIME_SHIFT_STATUS_UNSUPPORTED, @@ -3302,6 +3310,30 @@ public final class TvInputManager { } } + void stopPlayback(int mode) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.stopPlayback(mToken, mode, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + void startPlayback() { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.startPlayback(mToken, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Sends TV messages to the service for testing purposes */ diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 720d9a6291de..55fa51755177 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -34,6 +34,7 @@ import android.graphics.Rect; import android.hardware.hdmi.HdmiDeviceInfo; import android.media.AudioPresentation; import android.media.PlaybackParams; +import android.media.tv.interactive.TvInteractiveAppService; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -1531,6 +1532,32 @@ public abstract class TvInputService extends Service { } /** + * Called when the application requests playback of the Audio, Video, and CC streams to be + * stopped, but the metadata should continue to be filtered. + * + * <p>The metadata that will continue to be filtered includes the PSI + * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1. + * + * <p> Note that this is different form {@link #timeShiftPause()} as should release the + * stream, making it impossible to resume from this position again. + * @param mode + * @hide + */ + public void onStopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) { + } + + /** + * Starts playback of the Audio, Video, and CC streams. + * + * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be + * used after stopping playback. This is used to restart playback from the current position + * in the live broadcast. + * @hide + */ + public void onStartPlayback() { + } + + /** * Called when the application requests to play a given recorded TV program. * * @param recordedProgramUri The URI of a recorded TV program. @@ -1993,6 +2020,20 @@ public abstract class TvInputService extends Service { } /** + * Calls {@link #onStopPlayback(int)}. + */ + void stopPlayback(int mode) { + onStopPlayback(mode); + } + + /** + * Calls {@link #onStartPlayback()}. + */ + void startPlayback() { + onStartPlayback(); + } + + /** * Calls {@link #onTimeShiftPlay(Uri)}. */ void timeShiftPlay(Uri recordedProgramUri) { diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java index 196b5c3112c0..233f96675543 100644 --- a/media/java/android/media/tv/TvView.java +++ b/media/java/android/media/tv/TvView.java @@ -37,6 +37,7 @@ import android.media.PlaybackParams; import android.media.tv.TvInputManager.Session; import android.media.tv.TvInputManager.Session.FinishedInputEventCallback; import android.media.tv.TvInputManager.SessionCallback; +import android.media.tv.interactive.TvInteractiveAppService; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -643,6 +644,35 @@ public class TvView extends ViewGroup { } } + /** + * Stops playback of the Audio, Video, and CC streams, but continue filtering the metadata. + * + * <p>The metadata that will continue to be filtered includes the PSI + * (Program specific information) and SI (Service Information), part of ISO/IEC 13818-1. + * + * <p> Note that this is different form {@link #timeShiftPause()} as this completely drops + * the stream, making it impossible to resume from this position again. + * @hide + */ + public void stopPlayback(@TvInteractiveAppService.PlaybackCommandStopMode int mode) { + if (mSession != null) { + mSession.stopPlayback(mode); + } + } + + /** + * Starts playback of the Audio, Video, and CC streams. + * + * <p> Note that this is different form {@link #timeShiftResume()} as this is intended to be + * used after stopping playback. This is used to restart playback from the current position + * in the live broadcast. + * @hide + */ + public void startPlayback() { + if (mSession != null) { + mSession.startPlayback(); + } + } /** * Sends TV messages to the session for testing purposes diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index 281eba66123b..6019aa8560e1 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -156,7 +156,7 @@ <string name="permission_storage">Photos and media</string> <!-- Notification permission will be granted of corresponding profile [CHAR LIMIT=30] --> - <string name="permission_notification">Notifications</string> + <string name="permission_notifications">Notifications</string> <!-- Apps permission will be granted of corresponding profile [CHAR LIMIT=30] --> <string name="permission_app_streaming">Apps</string> @@ -165,28 +165,31 @@ <string name="permission_nearby_device_streaming">Streaming</string> <!-- Description of phone permission of corresponding profile [CHAR LIMIT=NONE] --> - <string name="permission_phone_summary">Can make and manage phone calls</string> + <string name="permission_phone_summary">Make and manage phone calls</string> <!-- Description of Call logs permission of corresponding profile [CHAR LIMIT=NONE] --> - <string name="permission_call_logs_summary">Can read and write phone call log</string> + <string name="permission_call_logs_summary">Read and write phone call log</string> <!-- Description of SMS permission of corresponding profile [CHAR LIMIT=NONE] --> - <string name="permission_sms_summary">Can send and view SMS messages</string> + <string name="permission_sms_summary">Send and view SMS messages</string> <!-- Description of contacts permission of corresponding profile [CHAR LIMIT=NONE] --> - <string name="permission_contacts_summary">Can access your contacts</string> + <string name="permission_contacts_summary">Access your contacts</string> <!-- Description of calendar permission of corresponding profile [CHAR LIMIT=NONE] --> - <string name="permission_calendar_summary">Can access your calendar</string> + <string name="permission_calendar_summary">Access your calendar</string> <!-- Description of microphone permission of corresponding profile [CHAR LIMIT=NONE] --> - <string name="permission_microphone_summary">Can record audio</string> + <string name="permission_microphone_summary">Record audio</string> <!-- Description of nearby devices' permission of corresponding profile [CHAR LIMIT=NONE] --> - <string name="permission_nearby_devices_summary">Can find, connect to, and determine the relative position of nearby devices</string> + <string name="permission_nearby_devices_summary">Find, connect to, and determine the relative position of nearby devices</string> - <!-- Description of notification permission of corresponding profile [CHAR LIMIT=NONE] --> - <string name="permission_notification_summary">Can read all notifications, including information like contacts, messages, and photos</string> + <!-- Description of NLA (notification listener access) of corresponding profile [CHAR LIMIT=NONE] --> + <string name="permission_notification_listener_access_summary">Read all notifications, including information like contacts, messages, and photos</string> + + <!-- Description of NLA & POST_NOTIFICATION of corresponding profile [CHAR LIMIT=NONE] --> + <string name="permission_notifications_summary">\u2022 Read all notifications, including info like contacts, messages, and photos<br/>\u2022 Send notifications<br/><br/>You can manage this app\'s ability to read and send notifications anytime in Settings > Notifications.</string> <!-- Description of app streaming permission of corresponding profile [CHAR LIMIT=NONE] --> <string name="permission_app_streaming_summary">Stream your phone\u2019s apps</string> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index 1184112fe3da..66282dc209ed 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -27,13 +27,13 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState; import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT; -import static com.android.companiondevicemanager.CompanionDeviceResources.PERMISSION_TYPES; -import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILES_NAME; -import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_ICON; -import static com.android.companiondevicemanager.CompanionDeviceResources.SUMMARIES; +import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_PERMISSIONS; +import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_NAMES; +import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_ICONS; +import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_SUMMARIES; import static com.android.companiondevicemanager.CompanionDeviceResources.SUPPORTED_PROFILES; import static com.android.companiondevicemanager.CompanionDeviceResources.SUPPORTED_SELF_MANAGED_PROFILES; -import static com.android.companiondevicemanager.CompanionDeviceResources.TITLES; +import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_TITLES; import static com.android.companiondevicemanager.Utils.getApplicationLabel; import static com.android.companiondevicemanager.Utils.getHtmlFromResources; import static com.android.companiondevicemanager.Utils.getIcon; @@ -482,7 +482,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements return; } - title = getHtmlFromResources(this, TITLES.get(deviceProfile), deviceName); + title = getHtmlFromResources(this, PROFILE_TITLES.get(deviceProfile), deviceName); setupPermissionList(deviceProfile); // Summary is not needed for selfManaged dialog. @@ -525,7 +525,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements mSelectedDevice = requireNonNull(deviceFilterPairs.get(0)); - final Drawable profileIcon = getIcon(this, PROFILE_ICON.get(deviceProfile)); + final Drawable profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile)); // No need to show permission consent dialog if it is a isSkipPrompt(true) // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt. @@ -554,14 +554,14 @@ public class CompanionDeviceActivity extends FragmentActivity implements throw new RuntimeException("Unsupported profile " + deviceProfile); } - profileIcon = getIcon(this, PROFILE_ICON.get(deviceProfile)); + profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile)); if (deviceProfile == null) { title = getHtmlFromResources(this, R.string.chooser_title_non_profile, appLabel); mButtonNotAllowMultipleDevices.setText(R.string.consent_no); } else { title = getHtmlFromResources(this, - R.string.chooser_title, getString(PROFILES_NAME.get(deviceProfile))); + R.string.chooser_title, getString(PROFILE_NAMES.get(deviceProfile))); } mDeviceAdapter = new DeviceListAdapter(this, this::onDeviceClicked); @@ -626,10 +626,10 @@ public class CompanionDeviceActivity extends FragmentActivity implements private void updatePermissionUi() { final String deviceProfile = mRequest.getDeviceProfile(); - final int summaryResourceId = SUMMARIES.get(deviceProfile); + final int summaryResourceId = PROFILE_SUMMARIES.get(deviceProfile); final String remoteDeviceName = mSelectedDevice.getDisplayName(); final Spanned title = getHtmlFromResources( - this, TITLES.get(deviceProfile), mAppLabel, remoteDeviceName); + this, PROFILE_TITLES.get(deviceProfile), mAppLabel, remoteDeviceName); final Spanned summary; if (deviceProfile == null && mRequest.isSingleDevice()) { @@ -689,7 +689,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements // and when mPermissionListRecyclerView is fully populated. // Lastly, disable the Allow and Don't allow buttons. private void setupPermissionList(String deviceProfile) { - final List<Integer> permissionTypes = new ArrayList<>(PERMISSION_TYPES.get(deviceProfile)); + final List<Integer> permissionTypes = new ArrayList<>( + PROFILE_PERMISSIONS.get(deviceProfile)); mPermissionListAdapter.setPermissionType(permissionTypes); mPermissionListRecyclerView.setAdapter(mPermissionListAdapter); mPermissionListRecyclerView.setLayoutManager(mPermissionsLayoutManager); diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java index 551e9754032b..23a11d618085 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java @@ -22,28 +22,15 @@ import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER; import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES; import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING; import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH; - -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_APP_STREAMING; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_CALENDAR; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_CALL_LOGS; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_CHANGE_MEDIA_OUTPUT; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_CONTACTS; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_MICROPHONE; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_NEARBY_DEVICES; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_NEARBY_DEVICE_STREAMING; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_NOTIFICATION; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_PHONE; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_SMS; -import static com.android.companiondevicemanager.PermissionListAdapter.PERMISSION_STORAGE; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; +import android.os.Build; import android.util.ArrayMap; import android.util.ArraySet; -import com.android.media.flags.Flags; - import java.util.Arrays; import java.util.List; import java.util.Map; @@ -54,7 +41,85 @@ import java.util.Set; * for the corresponding profile. */ final class CompanionDeviceResources { - static final Map<String, Integer> TITLES; + + // Permission resources + private static final int PERMISSION_NOTIFICATION_LISTENER_ACCESS = 0; + private static final int PERMISSION_STORAGE = 1; + private static final int PERMISSION_APP_STREAMING = 2; + private static final int PERMISSION_PHONE = 3; + private static final int PERMISSION_SMS = 4; + private static final int PERMISSION_CONTACTS = 5; + private static final int PERMISSION_CALENDAR = 6; + private static final int PERMISSION_NEARBY_DEVICES = 7; + private static final int PERMISSION_NEARBY_DEVICE_STREAMING = 8; + private static final int PERMISSION_MICROPHONE = 9; + private static final int PERMISSION_CALL_LOGS = 10; + // Notification Listener Access & POST_NOTIFICATION permission + private static final int PERMISSION_NOTIFICATIONS = 11; + private static final int PERMISSION_CHANGE_MEDIA_OUTPUT = 12; + + static final Map<Integer, Integer> PERMISSION_TITLES; + static { + final Map<Integer, Integer> map = new ArrayMap<>(); + map.put(PERMISSION_NOTIFICATION_LISTENER_ACCESS, R.string.permission_notifications); + map.put(PERMISSION_STORAGE, R.string.permission_storage); + map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming); + map.put(PERMISSION_PHONE, R.string.permission_phone); + map.put(PERMISSION_SMS, R.string.permission_sms); + map.put(PERMISSION_CONTACTS, R.string.permission_contacts); + map.put(PERMISSION_CALENDAR, R.string.permission_calendar); + map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices); + map.put(PERMISSION_NEARBY_DEVICE_STREAMING, R.string.permission_nearby_device_streaming); + map.put(PERMISSION_MICROPHONE, R.string.permission_microphone); + map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs); + map.put(PERMISSION_NOTIFICATIONS, R.string.permission_notifications); + map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.string.permission_media_routing_control); + PERMISSION_TITLES = unmodifiableMap(map); + } + + static final Map<Integer, Integer> PERMISSION_SUMMARIES; + static { + final Map<Integer, Integer> map = new ArrayMap<>(); + map.put(PERMISSION_NOTIFICATION_LISTENER_ACCESS, + R.string.permission_notification_listener_access_summary); + map.put(PERMISSION_STORAGE, R.string.permission_storage_summary); + map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming_summary); + map.put(PERMISSION_PHONE, R.string.permission_phone_summary); + map.put(PERMISSION_SMS, R.string.permission_sms_summary); + map.put(PERMISSION_CONTACTS, R.string.permission_contacts_summary); + map.put(PERMISSION_CALENDAR, R.string.permission_calendar_summary); + map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices_summary); + map.put(PERMISSION_NEARBY_DEVICE_STREAMING, + R.string.permission_nearby_device_streaming_summary); + map.put(PERMISSION_MICROPHONE, R.string.permission_microphone_summary); + map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs_summary); + map.put(PERMISSION_NOTIFICATIONS, R.string.permission_notifications_summary); + map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.string.permission_media_routing_control_summary); + PERMISSION_SUMMARIES = unmodifiableMap(map); + } + + static final Map<Integer, Integer> PERMISSION_ICONS; + static { + final Map<Integer, Integer> map = new ArrayMap<>(); + map.put(PERMISSION_NOTIFICATION_LISTENER_ACCESS, R.drawable.ic_permission_notifications); + map.put(PERMISSION_STORAGE, R.drawable.ic_permission_storage); + map.put(PERMISSION_APP_STREAMING, R.drawable.ic_permission_app_streaming); + map.put(PERMISSION_PHONE, R.drawable.ic_permission_phone); + map.put(PERMISSION_SMS, R.drawable.ic_permission_sms); + map.put(PERMISSION_CONTACTS, R.drawable.ic_permission_contacts); + map.put(PERMISSION_CALENDAR, R.drawable.ic_permission_calendar); + map.put(PERMISSION_NEARBY_DEVICES, R.drawable.ic_permission_nearby_devices); + map.put(PERMISSION_NEARBY_DEVICE_STREAMING, + R.drawable.ic_permission_nearby_device_streaming); + map.put(PERMISSION_MICROPHONE, R.drawable.ic_permission_microphone); + map.put(PERMISSION_CALL_LOGS, R.drawable.ic_permission_call_logs); + map.put(PERMISSION_NOTIFICATIONS, R.drawable.ic_permission_notifications); + map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.drawable.ic_permission_media_routing_control); + PERMISSION_ICONS = unmodifiableMap(map); + } + + // Profile resources + static final Map<String, Integer> PROFILE_TITLES; static { final Map<String, Integer> map = new ArrayMap<>(); map.put(DEVICE_PROFILE_APP_STREAMING, R.string.title_app_streaming); @@ -65,71 +130,61 @@ final class CompanionDeviceResources { map.put(DEVICE_PROFILE_GLASSES, R.string.confirmation_title_glasses); map.put(null, R.string.confirmation_title); - TITLES = unmodifiableMap(map); + PROFILE_TITLES = unmodifiableMap(map); + } + + static final Map<String, Integer> PROFILE_SUMMARIES; + static { + final Map<String, Integer> map = new ArrayMap<>(); + map.put(DEVICE_PROFILE_WATCH, R.string.summary_watch); + map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses); + map.put(null, R.string.summary_generic); + + PROFILE_SUMMARIES = unmodifiableMap(map); } - static final Map<String, List<Integer>> PERMISSION_TYPES; + static final Map<String, List<Integer>> PROFILE_PERMISSIONS; static { final Map<String, List<Integer>> map = new ArrayMap<>(); map.put(DEVICE_PROFILE_APP_STREAMING, Arrays.asList(PERMISSION_APP_STREAMING)); map.put(DEVICE_PROFILE_COMPUTER, Arrays.asList( - PERMISSION_NOTIFICATION, PERMISSION_STORAGE)); + PERMISSION_NOTIFICATION_LISTENER_ACCESS, PERMISSION_STORAGE)); map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, Arrays.asList(PERMISSION_NEARBY_DEVICE_STREAMING)); - if (!Flags.enablePrivilegedRoutingForMediaRoutingControl()) { - map.put(DEVICE_PROFILE_WATCH, Arrays.asList(PERMISSION_NOTIFICATION, PERMISSION_PHONE, - PERMISSION_CALL_LOGS, PERMISSION_SMS, PERMISSION_CONTACTS, PERMISSION_CALENDAR, - PERMISSION_NEARBY_DEVICES)); - } else { - map.put(DEVICE_PROFILE_WATCH, Arrays.asList(PERMISSION_NOTIFICATION, PERMISSION_PHONE, + if (Build.VERSION.SDK_INT > UPSIDE_DOWN_CAKE) { + map.put(DEVICE_PROFILE_WATCH, Arrays.asList(PERMISSION_NOTIFICATIONS, PERMISSION_PHONE, PERMISSION_CALL_LOGS, PERMISSION_SMS, PERMISSION_CONTACTS, PERMISSION_CALENDAR, PERMISSION_NEARBY_DEVICES, PERMISSION_CHANGE_MEDIA_OUTPUT)); + } else { + map.put(DEVICE_PROFILE_WATCH, Arrays.asList(PERMISSION_NOTIFICATION_LISTENER_ACCESS, + PERMISSION_PHONE, PERMISSION_CALL_LOGS, PERMISSION_SMS, PERMISSION_CONTACTS, + PERMISSION_CALENDAR, PERMISSION_NEARBY_DEVICES)); } - map.put(DEVICE_PROFILE_GLASSES, Arrays.asList(PERMISSION_NOTIFICATION, PERMISSION_PHONE, - PERMISSION_SMS, PERMISSION_CONTACTS, PERMISSION_MICROPHONE, + map.put(DEVICE_PROFILE_GLASSES, Arrays.asList(PERMISSION_NOTIFICATION_LISTENER_ACCESS, + PERMISSION_PHONE, PERMISSION_SMS, PERMISSION_CONTACTS, PERMISSION_MICROPHONE, PERMISSION_NEARBY_DEVICES)); - PERMISSION_TYPES = unmodifiableMap(map); + PROFILE_PERMISSIONS = unmodifiableMap(map); } - static final Map<String, Integer> SUMMARIES; - static { - final Map<String, Integer> map = new ArrayMap<>(); - map.put(DEVICE_PROFILE_WATCH, R.string.summary_watch); - map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses); - map.put(null, R.string.summary_generic); - - SUMMARIES = unmodifiableMap(map); - } - - static final Map<String, Integer> PROFILES_NAME; + static final Map<String, Integer> PROFILE_NAMES; static { final Map<String, Integer> map = new ArrayMap<>(); map.put(DEVICE_PROFILE_WATCH, R.string.profile_name_watch); map.put(DEVICE_PROFILE_GLASSES, R.string.profile_name_glasses); map.put(null, R.string.profile_name_generic); - PROFILES_NAME = unmodifiableMap(map); - } - - static final Map<String, Integer> PROFILES_NAME_MULTI; - static { - final Map<String, Integer> map = new ArrayMap<>(); - map.put(DEVICE_PROFILE_GLASSES, R.string.profile_name_generic); - map.put(DEVICE_PROFILE_WATCH, R.string.profile_name_watch); - map.put(null, R.string.profile_name_generic); - - PROFILES_NAME_MULTI = unmodifiableMap(map); + PROFILE_NAMES = unmodifiableMap(map); } - static final Map<String, Integer> PROFILE_ICON; + static final Map<String, Integer> PROFILE_ICONS; static { final Map<String, Integer> map = new ArrayMap<>(); map.put(DEVICE_PROFILE_WATCH, R.drawable.ic_watch); map.put(DEVICE_PROFILE_GLASSES, R.drawable.ic_glasses); map.put(null, R.drawable.ic_device_other); - PROFILE_ICON = unmodifiableMap(map); + PROFILE_ICONS = unmodifiableMap(map); } static final Set<String> SUPPORTED_PROFILES; diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java index e21aee3cedb8..4a1f8014a2f0 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java @@ -16,14 +16,14 @@ package com.android.companiondevicemanager; +import static com.android.companiondevicemanager.CompanionDeviceResources.PERMISSION_ICONS; +import static com.android.companiondevicemanager.CompanionDeviceResources.PERMISSION_SUMMARIES; +import static com.android.companiondevicemanager.CompanionDeviceResources.PERMISSION_TITLES; import static com.android.companiondevicemanager.Utils.getHtmlFromResources; import static com.android.companiondevicemanager.Utils.getIcon; -import static java.util.Collections.unmodifiableMap; - import android.content.Context; import android.text.Spanned; -import android.util.ArrayMap; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -35,7 +35,6 @@ import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; import java.util.List; -import java.util.Map; class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.ViewHolder> { private final Context mContext; @@ -43,75 +42,6 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V // Add the expand buttons if permissions are more than PERMISSION_SIZE in the permission list. private static final int PERMISSION_SIZE = 2; - static final int PERMISSION_NOTIFICATION = 0; - static final int PERMISSION_STORAGE = 1; - static final int PERMISSION_APP_STREAMING = 2; - static final int PERMISSION_PHONE = 3; - static final int PERMISSION_SMS = 4; - static final int PERMISSION_CONTACTS = 5; - static final int PERMISSION_CALENDAR = 6; - static final int PERMISSION_NEARBY_DEVICES = 7; - static final int PERMISSION_NEARBY_DEVICE_STREAMING = 8; - static final int PERMISSION_MICROPHONE = 9; - static final int PERMISSION_CALL_LOGS = 10; - static final int PERMISSION_CHANGE_MEDIA_OUTPUT = 11; - - private static final Map<Integer, Integer> sTitleMap; - static { - final Map<Integer, Integer> map = new ArrayMap<>(); - map.put(PERMISSION_NOTIFICATION, R.string.permission_notification); - map.put(PERMISSION_STORAGE, R.string.permission_storage); - map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming); - map.put(PERMISSION_PHONE, R.string.permission_phone); - map.put(PERMISSION_SMS, R.string.permission_sms); - map.put(PERMISSION_CONTACTS, R.string.permission_contacts); - map.put(PERMISSION_CALENDAR, R.string.permission_calendar); - map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices); - map.put(PERMISSION_NEARBY_DEVICE_STREAMING, R.string.permission_nearby_device_streaming); - map.put(PERMISSION_MICROPHONE, R.string.permission_microphone); - map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs); - map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.string.permission_media_routing_control); - sTitleMap = unmodifiableMap(map); - } - - private static final Map<Integer, Integer> sSummaryMap; - static { - final Map<Integer, Integer> map = new ArrayMap<>(); - map.put(PERMISSION_NOTIFICATION, R.string.permission_notification_summary); - map.put(PERMISSION_STORAGE, R.string.permission_storage_summary); - map.put(PERMISSION_APP_STREAMING, R.string.permission_app_streaming_summary); - map.put(PERMISSION_PHONE, R.string.permission_phone_summary); - map.put(PERMISSION_SMS, R.string.permission_sms_summary); - map.put(PERMISSION_CONTACTS, R.string.permission_contacts_summary); - map.put(PERMISSION_CALENDAR, R.string.permission_calendar_summary); - map.put(PERMISSION_NEARBY_DEVICES, R.string.permission_nearby_devices_summary); - map.put(PERMISSION_NEARBY_DEVICE_STREAMING, - R.string.permission_nearby_device_streaming_summary); - map.put(PERMISSION_MICROPHONE, R.string.permission_microphone_summary); - map.put(PERMISSION_CALL_LOGS, R.string.permission_call_logs_summary); - map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.string.permission_media_routing_control_summary); - sSummaryMap = unmodifiableMap(map); - } - - private static final Map<Integer, Integer> sIconMap; - static { - final Map<Integer, Integer> map = new ArrayMap<>(); - map.put(PERMISSION_NOTIFICATION, R.drawable.ic_permission_notifications); - map.put(PERMISSION_STORAGE, R.drawable.ic_permission_storage); - map.put(PERMISSION_APP_STREAMING, R.drawable.ic_permission_app_streaming); - map.put(PERMISSION_PHONE, R.drawable.ic_permission_phone); - map.put(PERMISSION_SMS, R.drawable.ic_permission_sms); - map.put(PERMISSION_CONTACTS, R.drawable.ic_permission_contacts); - map.put(PERMISSION_CALENDAR, R.drawable.ic_permission_calendar); - map.put(PERMISSION_NEARBY_DEVICES, R.drawable.ic_permission_nearby_devices); - map.put(PERMISSION_NEARBY_DEVICE_STREAMING, - R.drawable.ic_permission_nearby_device_streaming); - map.put(PERMISSION_MICROPHONE, R.drawable.ic_permission_microphone); - map.put(PERMISSION_CALL_LOGS, R.drawable.ic_permission_call_logs); - map.put(PERMISSION_CHANGE_MEDIA_OUTPUT, R.drawable.ic_permission_media_routing_control); - sIconMap = unmodifiableMap(map); - } - PermissionListAdapter(Context context) { mContext = context; } @@ -121,7 +51,8 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V View view = LayoutInflater.from(parent.getContext()).inflate( R.layout.list_item_permission, parent, false); ViewHolder viewHolder = new ViewHolder(view); - viewHolder.mPermissionIcon.setImageDrawable(getIcon(mContext, sIconMap.get(viewType))); + viewHolder.mPermissionIcon.setImageDrawable( + getIcon(mContext, PERMISSION_ICONS.get(viewType))); if (viewHolder.mExpandButton.getTag() == null) { viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more); @@ -165,8 +96,8 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V @Override public void onBindViewHolder(ViewHolder holder, int position) { int type = getItemViewType(position); - final Spanned title = getHtmlFromResources(mContext, sTitleMap.get(type)); - final Spanned summary = getHtmlFromResources(mContext, sSummaryMap.get(type)); + final Spanned title = getHtmlFromResources(mContext, PERMISSION_TITLES.get(type)); + final Spanned summary = getHtmlFromResources(mContext, PERMISSION_SUMMARIES.get(type)); holder.mPermissionSummary.setText(summary); holder.mPermissionName.setText(title); @@ -192,6 +123,7 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V private final TextView mPermissionSummary; private final ImageView mPermissionIcon; private final ImageButton mExpandButton; + ViewHolder(View itemView) { super(itemView); mPermissionName = itemView.findViewById(R.id.permission_name); @@ -203,7 +135,7 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V private void setAccessibility(View view, int viewType, int action, int statusResourceId, int actionResourceId) { - final String permission = mContext.getString(sTitleMap.get(viewType)); + final String permission = mContext.getString(PERMISSION_TITLES.get(viewType)); if (actionResourceId != 0) { view.announceForAccessibility( diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml index e3b93ba34045..f4641b9930cb 100644 --- a/packages/PackageInstaller/res/values/strings.xml +++ b/packages/PackageInstaller/res/values/strings.xml @@ -202,11 +202,6 @@ <!-- Dialog attributes to indicate parse errors --> <string name="Parse_error_dlg_text">There was a problem parsing the package.</string> - <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=30] --> - <string name="wear_not_allowed_dlg_title">Android Wear</string> - <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=none] --> - <string name="wear_not_allowed_dlg_text">Install/Uninstall actions not supported on Wear.</string> - <!-- Message that the app to be installed is being staged [CHAR LIMIT=50] --> <string name="message_staging">Staging app…</string> diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java index e07e9425808e..2da8c8c69ff8 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java @@ -156,10 +156,9 @@ public class UninstallAlertDialogFragment extends DialogFragment implements if (customUserManager.isUserOfType(USER_TYPE_PROFILE_MANAGED) && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) { messageBuilder.append(isArchive - ? getString(R.string.archive_application_text_current_user_work_profile, - userName) : getString( - R.string.uninstall_application_text_current_user_work_profile, - userName)); + ? getString(R.string.archive_application_text_current_user_work_profile) + : getString( + R.string.uninstall_application_text_current_user_work_profile)); } else if (customUserManager.isUserOfType(USER_TYPE_PROFILE_CLONE) && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) { mIsClonedApp = true; @@ -168,11 +167,11 @@ public class UninstallAlertDialogFragment extends DialogFragment implements } else if (Flags.allowPrivateProfile() && customUserManager.isPrivateProfile() && customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) { - messageBuilder.append(isArchive ? getString( - R.string.archive_application_text_current_user_private_profile, - userName) : getString( - R.string.uninstall_application_text_current_user_private_profile, - userName)); + messageBuilder.append( + isArchive ? getString( + R.string.archive_application_text_current_user_private_profile) + : getString( + R.string.uninstall_application_text_current_user_private_profile)); } else if (isArchive) { messageBuilder.append( getString(R.string.archive_application_text_user, userName)); diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java index 96a11eeb3b78..5b39f4ee1541 100644 --- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java +++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java @@ -112,26 +112,6 @@ public class RestrictedLockUtils { } /** - * Shows restricted setting dialog. - */ - @RequiresApi(Build.VERSION_CODES.TIRAMISU) - public static void sendShowRestrictedSettingDialogIntent(Context context, - String packageName, int uid) { - final Intent intent = getShowRestrictedSettingsIntent(packageName, uid); - context.startActivity(intent); - } - - /** - * Gets restricted settings dialog intent. - */ - private static Intent getShowRestrictedSettingsIntent(String packageName, int uid) { - final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG); - intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); - intent.putExtra(Intent.EXTRA_UID, uid); - return intent; - } - - /** * Checks if current user is profile or not */ @RequiresApi(Build.VERSION_CODES.M) @@ -238,4 +218,35 @@ public class RestrictedLockUtils { + '}'; } } + + + /** + * Shows restricted setting dialog. + * + * @deprecated TODO(b/308921175): This will be deleted with the + * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new + * code. + */ + @Deprecated + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + public static void sendShowRestrictedSettingDialogIntent(Context context, + String packageName, int uid) { + final Intent intent = getShowRestrictedSettingsIntent(packageName, uid); + context.startActivity(intent); + } + + /** + * Gets restricted settings dialog intent. + * + * @deprecated TODO(b/308921175): This will be deleted with the + * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new + * code. + */ + @Deprecated + private static Intent getShowRestrictedSettingsIntent(String packageName, int uid) { + final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + intent.putExtra(Intent.EXTRA_UID, uid); + return intent; + } } diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt index d0d2dc0083a6..e099f1124bf1 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlow.kt @@ -20,13 +20,17 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.util.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn +private const val TAG = "BroadcastReceiverFlow" + /** * A [BroadcastReceiver] flow for the given [intentFilter]. */ @@ -39,4 +43,6 @@ fun Context.broadcastReceiverFlow(intentFilter: IntentFilter): Flow<Intent> = ca registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED) awaitClose { unregisterReceiver(broadcastReceiver) } +}.catch { e -> + Log.e(TAG, "Error while broadcastReceiverFlow", e) }.conflate().flowOn(Dispatchers.Default) diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt index dfaf3c66ff8d..eef5225aef42 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverFlowTest.kt @@ -31,8 +31,10 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doThrow import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.stub @RunWith(AndroidJUnit4::class) class BroadcastReceiverFlowTest { @@ -74,6 +76,18 @@ class BroadcastReceiverFlowTest { assertThat(onReceiveIsCalled).isTrue() } + @Test + fun broadcastReceiverFlow_unregisterReceiverThrowException_noCrash() = runBlocking { + context.stub { + on { unregisterReceiver(any()) } doThrow IllegalArgumentException() + } + val flow = context.broadcastReceiverFlow(INTENT_FILTER) + + flow.firstWithTimeoutOrNull() + + assertThat(registeredBroadcastReceiver).isNotNull() + } + private companion object { val INTENT_FILTER = IntentFilter() } diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index 4454b710b7e4..02374462f093 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -77,6 +77,9 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { ECM_KEYS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS); ECM_KEYS.add(Manifest.permission.BIND_DEVICE_ADMIN); } + + ECM_KEYS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS); + ECM_KEYS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); } /** diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java index db2a6ec2da68..50e3bd08026c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java @@ -96,12 +96,29 @@ public class RestrictedPreference extends TwoTargetPreference { mHelper.checkRestrictionAndSetDisabled(userRestriction, userId); } + /** + * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this + * package. Marks the preference as disabled if so. + * @param restriction The key identifying the setting + * @param packageName the package to check the restriction for + * @param uid the uid of the package + */ + public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) { + mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid); + } + @Override public void setEnabled(boolean enabled) { if (enabled && isDisabledByAdmin()) { mHelper.setDisabledByAdmin(null); return; } + + if (enabled && isDisabledByEcm()) { + mHelper.setDisabledByEcm(null); + return; + } + super.setEnabled(enabled); } @@ -111,16 +128,14 @@ public class RestrictedPreference extends TwoTargetPreference { } } - public void setDisabledByAppOps(boolean disabled) { - if (mHelper.setDisabledByAppOps(disabled)) { - notifyChanged(); - } - } - public boolean isDisabledByAdmin() { return mHelper.isDisabledByAdmin(); } + public boolean isDisabledByEcm() { + return mHelper.isDisabledByEcm(); + } + public int getUid() { return mHelper != null ? mHelper.uid : Process.INVALID_UID; } @@ -128,4 +143,16 @@ public class RestrictedPreference extends TwoTargetPreference { public String getPackageName() { return mHelper != null ? mHelper.packageName : null; } + + /** + * @deprecated TODO(b/308921175): This will be deleted with the + * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new + * code. + */ + @Deprecated + public void setDisabledByAppOps(boolean disabled) { + if (mHelper.setDisabledByAppOps(disabled)) { + notifyChanged(); + } + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java index 29ea25e13835..a479269f40fb 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -17,10 +17,12 @@ package com.android.settingslib; import static android.app.admin.DevicePolicyResources.Strings.Settings.CONTROLLED_BY_ADMIN_SUMMARY; + import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.Intent; import android.content.res.TypedArray; import android.os.Build; import android.os.UserHandle; @@ -52,7 +54,8 @@ public class RestrictedPreferenceHelper { private String mAttrUserRestriction = null; private boolean mDisabledSummary = false; - private boolean mDisabledByAppOps; + private boolean mDisabledByEcm; + private Intent mDisabledByEcmIntent = null; public RestrictedPreferenceHelper(Context context, Preference preference, AttributeSet attrs, String packageName, int uid) { @@ -101,7 +104,7 @@ public class RestrictedPreferenceHelper { * Modify PreferenceViewHolder to add padlock if restriction is disabled. */ public void onBindViewHolder(PreferenceViewHolder holder) { - if (mDisabledByAdmin || mDisabledByAppOps) { + if (mDisabledByAdmin || mDisabledByEcm) { holder.itemView.setEnabled(true); } if (mDisabledSummary) { @@ -112,7 +115,7 @@ public class RestrictedPreferenceHelper { : mContext.getString(R.string.disabled_by_admin_summary_text); if (mDisabledByAdmin) { summaryView.setText(disabledText); - } else if (mDisabledByAppOps) { + } else if (mDisabledByEcm) { summaryView.setText(R.string.disabled_by_app_ops_text); } else if (TextUtils.equals(disabledText, summaryView.getText())) { // It's previously set to disabled text, clear it. @@ -144,7 +147,12 @@ public class RestrictedPreferenceHelper { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin); return true; } - if (mDisabledByAppOps) { + if (mDisabledByEcm) { + if (android.security.Flags.extendEcmToAllSettings()) { + mContext.startActivity(mDisabledByEcmIntent); + return true; + } + RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext, packageName, uid); return true; @@ -174,6 +182,20 @@ public class RestrictedPreferenceHelper { } /** + * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this + * package. Marks the preference as disabled if so. + * @param restriction The key identifying the setting + * @param packageName the package to check the restriction for + * @param uid the uid of the package + */ + public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) { + updatePackageDetails(packageName, uid); + Intent intent = RestrictedLockUtilsInternal.checkIfRequiresEnhancedConfirmation( + mContext, restriction, uid, packageName); + setDisabledByEcm(intent); + } + + /** * @return EnforcedAdmin if we have been passed the restriction in the xml. */ public EnforcedAdmin checkRestrictionEnforced() { @@ -211,10 +233,19 @@ public class RestrictedPreferenceHelper { return changed; } - public boolean setDisabledByAppOps(boolean disabled) { + /** + * Disable the preference based on the passed in Intent + * @param disabledIntent The intent which is started when the user clicks the disabled + * preference. If it is {@code null}, then this preference will be enabled. Otherwise, it will + * be disabled. + * @return true if the disabled state was changed. + */ + public boolean setDisabledByEcm(Intent disabledIntent) { + boolean disabled = disabledIntent != null; boolean changed = false; - if (mDisabledByAppOps != disabled) { - mDisabledByAppOps = disabled; + if (mDisabledByEcm != disabled) { + mDisabledByEcmIntent = disabledIntent; + mDisabledByEcm = disabled; changed = true; updateDisabledState(); } @@ -226,8 +257,8 @@ public class RestrictedPreferenceHelper { return mDisabledByAdmin; } - public boolean isDisabledByAppOps() { - return mDisabledByAppOps; + public boolean isDisabledByEcm() { + return mDisabledByEcm; } public void updatePackageDetails(String packageName, int uid) { @@ -236,13 +267,31 @@ public class RestrictedPreferenceHelper { } private void updateDisabledState() { + boolean isEnabled = !(mDisabledByAdmin || mDisabledByEcm); if (!(mPreference instanceof RestrictedTopLevelPreference)) { - mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps)); + mPreference.setEnabled(isEnabled); } if (mPreference instanceof PrimarySwitchPreference) { - ((PrimarySwitchPreference) mPreference) - .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps)); + ((PrimarySwitchPreference) mPreference).setSwitchEnabled(isEnabled); } } + + + /** + * @deprecated TODO(b/308921175): This will be deleted with the + * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new + * code. + */ + @Deprecated + public boolean setDisabledByAppOps(boolean disabled) { + boolean changed = false; + if (mDisabledByEcm != disabled) { + mDisabledByEcm = disabled; + changed = true; + updateDisabledState(); + } + + return changed; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java index 60321eb1a9dc..3b8f66577f6e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java @@ -197,6 +197,17 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat { mHelper.checkRestrictionAndSetDisabled(userRestriction, userId); } + /** + * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this + * package. Marks the preference as disabled if so. + * @param restriction The key identifying the setting + * @param packageName the package to check the restriction for + * @param uid the uid of the package + */ + public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) { + mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid); + } + @Override public void setEnabled(boolean enabled) { boolean changed = false; @@ -204,8 +215,8 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat { mHelper.setDisabledByAdmin(null); changed = true; } - if (enabled && isDisabledByAppOps()) { - mHelper.setDisabledByAppOps(false); + if (enabled && isDisabledByEcm()) { + mHelper.setDisabledByEcm(null); changed = true; } if (!changed) { @@ -223,25 +234,50 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat { return mHelper.isDisabledByAdmin(); } + public boolean isDisabledByEcm() { + return mHelper.isDisabledByEcm(); + } + + /** + * @deprecated TODO(b/308921175): This will be deleted with the + * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new + * code. + */ + @Deprecated private void setDisabledByAppOps(boolean disabled) { if (mHelper.setDisabledByAppOps(disabled)) { notifyChanged(); } } - public boolean isDisabledByAppOps() { - return mHelper.isDisabledByAppOps(); - } - + /** + * @deprecated TODO(b/308921175): This will be deleted with the + * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new + * code. + */ + @Deprecated public int getUid() { return mHelper != null ? mHelper.uid : Process.INVALID_UID; } + /** + * @deprecated TODO(b/308921175): This will be deleted with the + * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new + * code. + */ + @Deprecated public String getPackageName() { return mHelper != null ? mHelper.packageName : null; } - /** Updates enabled state based on associated package. */ + /** + * Updates enabled state based on associated package + * + * @deprecated TODO(b/308921175): This will be deleted with the + * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new + * code. + */ + @Deprecated public void updateState( @NonNull String packageName, int uid, boolean isEnableAllowed, boolean isEnabled) { mHelper.updatePackageDetails(packageName, uid); @@ -258,7 +294,7 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat { setEnabled(false); } else if (isEnabled) { setEnabled(true); - } else if (appOpsAllowed && isDisabledByAppOps()) { + } else if (appOpsAllowed && isDisabledByEcm()) { setEnabled(true); } else if (!appOpsAllowed){ setDisabledByAppOps(true); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java index 69b61c74625e..2cb44ec39a23 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -31,7 +31,6 @@ import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothLeBroadcastSubgroup; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; -import android.bluetooth.BluetoothStatusCodes; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -42,7 +41,6 @@ import android.os.Looper; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; -import android.util.Pair; import androidx.annotation.RequiresApi; @@ -53,15 +51,17 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; /** - * LocalBluetoothLeBroadcast provides an interface between the Settings app - * and the functionality of the local {@link BluetoothLeBroadcast}. - * Use the {@link BluetoothLeBroadcast.Callback} to get the result callback. + * LocalBluetoothLeBroadcast provides an interface between the Settings app and the functionality of + * the local {@link BluetoothLeBroadcast}. Use the {@link BluetoothLeBroadcast.Callback} to get the + * result callback. */ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { private static final String TAG = "LocalBluetoothLeBroadcast"; @@ -74,11 +74,12 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { // Order of this profile in device profiles list private static final int ORDINAL = 1; private static final int UNKNOWN_VALUE_PLACEHOLDER = -1; - private static final Uri[] SETTINGS_URIS = new Uri[]{ - Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO), - Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE), - Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME), - }; + private static final Uri[] SETTINGS_URIS = + new Uri[] { + Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO), + Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE), + Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME), + }; private BluetoothLeBroadcast mServiceBroadcast; private BluetoothLeBroadcastAssistant mServiceBroadcastAssistant; @@ -95,62 +96,82 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { private Executor mExecutor; private ContentResolver mContentResolver; private ContentObserver mSettingsObserver; + // Cached broadcast callbacks being register before service is connected. + private Map<BluetoothLeBroadcast.Callback, Executor> mCachedBroadcastCallbackExecutorMap = + new ConcurrentHashMap<>(); - private final ServiceListener mServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (DEBUG) { - Log.d(TAG, "Bluetooth service connected: " + profile); - } - if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && !mIsBroadcastProfileReady) { - mServiceBroadcast = (BluetoothLeBroadcast) proxy; - mIsBroadcastProfileReady = true; - registerServiceCallBack(mExecutor, mBroadcastCallback); - List<BluetoothLeBroadcastMetadata> metadata = getAllBroadcastMetadata(); - if (!metadata.isEmpty()) { - updateBroadcastInfoFromBroadcastMetadata(metadata.get(0)); + private final ServiceListener mServiceListener = + new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (DEBUG) { + Log.d(TAG, "Bluetooth service connected: " + profile); + } + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) + && !mIsBroadcastProfileReady) { + mServiceBroadcast = (BluetoothLeBroadcast) proxy; + mIsBroadcastProfileReady = true; + registerServiceCallBack(mExecutor, mBroadcastCallback); + List<BluetoothLeBroadcastMetadata> metadata = getAllBroadcastMetadata(); + if (!metadata.isEmpty()) { + updateBroadcastInfoFromBroadcastMetadata(metadata.get(0)); + } + registerContentObserver(); + if (DEBUG) { + Log.d( + TAG, + "onServiceConnected: register " + + "mCachedBroadcastCallbackExecutorMap = " + + mCachedBroadcastCallbackExecutorMap); + } + mCachedBroadcastCallbackExecutorMap.forEach( + (callback, executor) -> + registerServiceCallBack(executor, callback)); + } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) + && !mIsBroadcastAssistantProfileReady) { + mIsBroadcastAssistantProfileReady = true; + mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy; + registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback); + } } - registerContentObserver(); - } else if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) - && !mIsBroadcastAssistantProfileReady) { - mIsBroadcastAssistantProfileReady = true; - mServiceBroadcastAssistant = (BluetoothLeBroadcastAssistant) proxy; - registerBroadcastAssistantCallback(mExecutor, mBroadcastAssistantCallback); - } - } - @Override - public void onServiceDisconnected(int profile) { - if (DEBUG) { - Log.d(TAG, "Bluetooth service disconnected"); - } - if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) && mIsBroadcastProfileReady) { - mIsBroadcastProfileReady = false; - unregisterServiceCallBack(mBroadcastCallback); - } - if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) - && mIsBroadcastAssistantProfileReady) { - mIsBroadcastAssistantProfileReady = false; - unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback); - } + @Override + public void onServiceDisconnected(int profile) { + if (DEBUG) { + Log.d(TAG, "Bluetooth service disconnected: " + profile); + } + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST) + && mIsBroadcastProfileReady) { + mIsBroadcastProfileReady = false; + unregisterServiceCallBack(mBroadcastCallback); + mCachedBroadcastCallbackExecutorMap.clear(); + } + if ((profile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) + && mIsBroadcastAssistantProfileReady) { + mIsBroadcastAssistantProfileReady = false; + unregisterBroadcastAssistantCallback(mBroadcastAssistantCallback); + } - if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) { - unregisterContentObserver(); - } - } - }; + if (!mIsBroadcastAssistantProfileReady && !mIsBroadcastProfileReady) { + unregisterContentObserver(); + } + } + }; private final BluetoothLeBroadcast.Callback mBroadcastCallback = new BluetoothLeBroadcast.Callback() { @Override public void onBroadcastStarted(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastStarted(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastStarted(), reason = " + + reason + + ", broadcastId = " + broadcastId); } setLatestBroadcastId(broadcastId); - setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true); + setAppSourceName(mNewAppSourceName, /* updateContentResolver= */ true); } @Override @@ -161,8 +182,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } @Override - public void onBroadcastMetadataChanged(int broadcastId, - @NonNull BluetoothLeBroadcastMetadata metadata) { + public void onBroadcastMetadataChanged( + int broadcastId, @NonNull BluetoothLeBroadcastMetadata metadata) { if (DEBUG) { Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = " + broadcastId); } @@ -172,8 +193,11 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { @Override public void onBroadcastStopped(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastStopped(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastStopped(), reason = " + + reason + + ", broadcastId = " + broadcastId); } @@ -191,37 +215,42 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { @Override public void onBroadcastUpdated(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastUpdated(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastUpdated(), reason = " + + reason + + ", broadcastId = " + broadcastId); } setLatestBroadcastId(broadcastId); - setAppSourceName(mNewAppSourceName, /*updateContentResolver=*/ true); + setAppSourceName(mNewAppSourceName, /* updateContentResolver= */ true); } @Override public void onBroadcastUpdateFailed(int reason, int broadcastId) { if (DEBUG) { - Log.d(TAG, - "onBroadcastUpdateFailed(), reason = " + reason + ", broadcastId = " + Log.d( + TAG, + "onBroadcastUpdateFailed(), reason = " + + reason + + ", broadcastId = " + broadcastId); } } @Override - public void onPlaybackStarted(int reason, int broadcastId) { - } + public void onPlaybackStarted(int reason, int broadcastId) {} @Override - public void onPlaybackStopped(int reason, int broadcastId) { - } + public void onPlaybackStopped(int reason, int broadcastId) {} }; private final BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = new BluetoothLeBroadcastAssistant.Callback() { @Override - public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, - int reason) {} + public void onSourceAdded( + @NonNull BluetoothDevice sink, int sourceId, int reason) {} + @Override public void onSearchStarted(int reason) {} @@ -238,38 +267,65 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {} @Override - public void onSourceAddFailed(@NonNull BluetoothDevice sink, - @NonNull BluetoothLeBroadcastMetadata source, int reason) {} + public void onSourceAddFailed( + @NonNull BluetoothDevice sink, + @NonNull BluetoothLeBroadcastMetadata source, + int reason) {} @Override - public void onSourceModified(@NonNull BluetoothDevice sink, int sourceId, - int reason) {} + public void onSourceModified( + @NonNull BluetoothDevice sink, int sourceId, int reason) {} @Override - public void onSourceModifyFailed(@NonNull BluetoothDevice sink, int sourceId, - int reason) {} + public void onSourceModifyFailed( + @NonNull BluetoothDevice sink, int sourceId, int reason) {} @Override - public void onSourceRemoved(@NonNull BluetoothDevice sink, int sourceId, - int reason) { + public void onSourceRemoved( + @NonNull BluetoothDevice sink, int sourceId, int reason) { if (DEBUG) { - Log.d(TAG, "onSourceRemoved(), sink = " + sink + ", reason = " - + reason + ", sourceId = " + sourceId); + Log.d( + TAG, + "onSourceRemoved(), sink = " + + sink + + ", reason = " + + reason + + ", sourceId = " + + sourceId); } } @Override - public void onSourceRemoveFailed(@NonNull BluetoothDevice sink, int sourceId, - int reason) { + public void onSourceRemoveFailed( + @NonNull BluetoothDevice sink, int sourceId, int reason) { if (DEBUG) { - Log.d(TAG, "onSourceRemoveFailed(), sink = " + sink + ", reason = " - + reason + ", sourceId = " + sourceId); + Log.d( + TAG, + "onSourceRemoveFailed(), sink = " + + sink + + ", reason = " + + reason + + ", sourceId = " + + sourceId); } } @Override - public void onReceiveStateChanged(@NonNull BluetoothDevice sink, int sourceId, - @NonNull BluetoothLeBroadcastReceiveState state) {} + public void onReceiveStateChanged( + @NonNull BluetoothDevice sink, + int sourceId, + @NonNull BluetoothLeBroadcastReceiveState state) { + if (DEBUG) { + Log.d( + TAG, + "onReceiveStateChanged(), sink = " + + sink + + ", sourceId = " + + sourceId + + ", state = " + + state); + } + } }; private class BroadcastSettingsObserver extends ContentObserver { @@ -296,8 +352,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { BluetoothAdapter.getDefaultAdapter() .getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST); BluetoothAdapter.getDefaultAdapter() - .getProfileProxy(context, mServiceListener, - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + .getProfileProxy( + context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); } /** @@ -312,11 +368,11 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } String programInfo = getProgramInfo(); if (DEBUG) { - Log.d(TAG, - "startBroadcast: language = " + language + " ,programInfo = " + programInfo); + Log.d(TAG, "startBroadcast: language = " + language + " ,programInfo = " + programInfo); } buildContentMetadata(language, programInfo); - mServiceBroadcast.startBroadcast(mBluetoothLeAudioContentMetadata, + mServiceBroadcast.startBroadcast( + mBluetoothLeAudioContentMetadata, (mBroadcastCode != null && mBroadcastCode.length > 0) ? mBroadcastCode : null); } @@ -325,7 +381,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } public void setProgramInfo(String programInfo) { - setProgramInfo(programInfo, /*updateContentResolver=*/ true); + setProgramInfo(programInfo, /* updateContentResolver= */ true); } private void setProgramInfo(String programInfo, boolean updateContentResolver) { @@ -344,8 +400,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "mContentResolver is null"); return; } - Settings.Secure.putString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, programInfo); + Settings.Secure.putString( + mContentResolver, + Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, + programInfo); } } @@ -354,7 +412,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } public void setBroadcastCode(byte[] broadcastCode) { - setBroadcastCode(broadcastCode, /*updateContentResolver=*/ true); + setBroadcastCode(broadcastCode, /* updateContentResolver= */ true); } private void setBroadcastCode(byte[] broadcastCode, boolean updateContentResolver) { @@ -372,7 +430,9 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "mContentResolver is null"); return; } - Settings.Secure.putString(mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE, + Settings.Secure.putString( + mContentResolver, + Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE, new String(broadcastCode, StandardCharsets.UTF_8)); } } @@ -401,8 +461,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "mContentResolver is null"); return; } - Settings.Secure.putString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, mAppSourceName); + Settings.Secure.putString( + mContentResolver, + Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, + mAppSourceName); } } @@ -427,10 +489,11 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { if (mBluetoothLeBroadcastMetadata == null) { final List<BluetoothLeBroadcastMetadata> metadataList = mServiceBroadcast.getAllBroadcastMetadata(); - mBluetoothLeBroadcastMetadata = metadataList.stream() - .filter(i -> i.getBroadcastId() == mBroadcastId) - .findFirst() - .orElse(null); + mBluetoothLeBroadcastMetadata = + metadataList.stream() + .filter(i -> i.getBroadcastId() == mBroadcastId) + .findFirst() + .orElse(null); } return mBluetoothLeBroadcastMetadata; } @@ -440,22 +503,27 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { Log.d(TAG, "updateBroadcastInfoFromContentProvider: mContentResolver is null"); return; } - String programInfo = Settings.Secure.getString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO); + String programInfo = + Settings.Secure.getString( + mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO); if (programInfo == null) { programInfo = getDefaultValueOfProgramInfo(); } - setProgramInfo(programInfo, /*updateContentResolver=*/ false); + setProgramInfo(programInfo, /* updateContentResolver= */ false); - String prefBroadcastCode = Settings.Secure.getString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE); - byte[] broadcastCode = (prefBroadcastCode == null) ? getDefaultValueOfBroadcastCode() - : prefBroadcastCode.getBytes(StandardCharsets.UTF_8); - setBroadcastCode(broadcastCode, /*updateContentResolver=*/ false); + String prefBroadcastCode = + Settings.Secure.getString( + mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE); + byte[] broadcastCode = + (prefBroadcastCode == null) + ? getDefaultValueOfBroadcastCode() + : prefBroadcastCode.getBytes(StandardCharsets.UTF_8); + setBroadcastCode(broadcastCode, /* updateContentResolver= */ false); - String appSourceName = Settings.Secure.getString(mContentResolver, - Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME); - setAppSourceName(appSourceName, /*updateContentResolver=*/ false); + String appSourceName = + Settings.Secure.getString( + mContentResolver, Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME); + setAppSourceName(appSourceName, /* updateContentResolver= */ false); } private void updateBroadcastInfoFromBroadcastMetadata( @@ -474,12 +542,12 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } BluetoothLeAudioContentMetadata contentMetadata = subgroup.get(0).getContentMetadata(); setProgramInfo(contentMetadata.getProgramInfo()); - setAppSourceName(getAppSourceName(), /*updateContentResolver=*/ true); + setAppSourceName(getAppSourceName(), /* updateContentResolver= */ true); } /** - * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system - * calls the corresponding callback {@link BluetoothLeBroadcast.Callback}. + * Stop the latest LE Broadcast. If the system stopped the LE Broadcast, then the system calls + * the corresponding callback {@link BluetoothLeBroadcast.Callback}. */ public void stopLatestBroadcast() { stopBroadcast(mBroadcastId); @@ -511,7 +579,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } String programInfo = getProgramInfo(); if (DEBUG) { - Log.d(TAG, + Log.d( + TAG, "updateBroadcast: language = " + language + " ,programInfo = " + programInfo); } mNewAppSourceName = appSourceName; @@ -519,50 +588,79 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { mServiceBroadcast.updateBroadcast(mBroadcastId, mBluetoothLeAudioContentMetadata); } - public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor, + /** + * Register Broadcast Callbacks to track its state and receivers + * + * @param executor Executor object for callback + * @param callback Callback object to be registered + */ + public void registerServiceCallBack( + @NonNull @CallbackExecutor Executor executor, @NonNull BluetoothLeBroadcast.Callback callback) { if (mServiceBroadcast == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d(TAG, "registerServiceCallBack failed, the BluetoothLeBroadcast is null."); + mCachedBroadcastCallbackExecutorMap.putIfAbsent(callback, executor); return; } - mServiceBroadcast.registerCallback(executor, callback); + try { + mServiceBroadcast.registerCallback(executor, callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "registerServiceCallBack failed. " + e.getMessage()); + } } /** - * Register Broadcast Assistant Callbacks to track it's state and receivers + * Register Broadcast Assistant Callbacks to track its state and receivers * * @param executor Executor object for callback * @param callback Callback object to be registered */ - public void registerBroadcastAssistantCallback(@NonNull @CallbackExecutor Executor executor, + private void registerBroadcastAssistantCallback( + @NonNull @CallbackExecutor Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback) { if (mServiceBroadcastAssistant == null) { - Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null."); + Log.d( + TAG, + "registerBroadcastAssistantCallback failed, " + + "the BluetoothLeBroadcastAssistant is null."); return; } mServiceBroadcastAssistant.registerCallback(executor, callback); } + /** + * Unregister previously registered Broadcast Callbacks + * + * @param callback Callback object to be unregistered + */ public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) { + mCachedBroadcastCallbackExecutorMap.remove(callback); if (mServiceBroadcast == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d(TAG, "unregisterServiceCallBack failed, the BluetoothLeBroadcast is null."); return; } - mServiceBroadcast.unregisterCallback(callback); + try { + mServiceBroadcast.unregisterCallback(callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "unregisterServiceCallBack failed. " + e.getMessage()); + } } /** - * Unregister previousely registered Broadcast Assistant Callbacks + * Unregister previously registered Broadcast Assistant Callbacks * * @param callback Callback object to be unregistered */ - public void unregisterBroadcastAssistantCallback( + private void unregisterBroadcastAssistantCallback( @NonNull BluetoothLeBroadcastAssistant.Callback callback) { if (mServiceBroadcastAssistant == null) { - Log.d(TAG, "The BluetoothLeBroadcastAssisntant is null."); + Log.d( + TAG, + "unregisterBroadcastAssistantCallback, " + + "the BluetoothLeBroadcastAssistant is null."); return; } @@ -570,8 +668,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } private void buildContentMetadata(String language, String programInfo) { - mBluetoothLeAudioContentMetadata = mBuilder.setLanguage(language).setProgramInfo( - programInfo).build(); + mBluetoothLeAudioContentMetadata = + mBuilder.setLanguage(language).setProgramInfo(programInfo).build(); } public LocalBluetoothLeBroadcastMetadata getLocalBluetoothLeBroadcastMetaData() { @@ -600,9 +698,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return true; } - /** - * Not supported since LE Audio Broadcasts do not establish a connection. - */ + /** Not supported since LE Audio Broadcasts do not establish a connection. */ public int getConnectionStatus(BluetoothDevice device) { if (mServiceBroadcast == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -611,9 +707,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return mServiceBroadcast.getConnectionState(device); } - /** - * Not supported since LE Audio Broadcasts do not establish a connection. - */ + /** Not supported since LE Audio Broadcasts do not establish a connection. */ public List<BluetoothDevice> getConnectedDevices() { if (mServiceBroadcast == null) { return new ArrayList<BluetoothDevice>(0); @@ -622,8 +716,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return mServiceBroadcast.getConnectedDevices(); } - public @NonNull - List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() { + /** Get all broadcast metadata. */ + public @NonNull List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() { if (mServiceBroadcast == null) { Log.d(TAG, "The BluetoothLeBroadcast is null."); return Collections.emptyList(); @@ -640,16 +734,14 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { return !mServiceBroadcast.getAllBroadcastMetadata().isEmpty(); } - /** - * Service does not provide method to get/set policy. - */ + /** Service does not provide method to get/set policy. */ public int getConnectionPolicy(BluetoothDevice device) { return CONNECTION_POLICY_FORBIDDEN; } /** - * Service does not provide "setEnabled" method. Please use {@link #startBroadcast}, - * {@link #stopBroadcast()} or {@link #updateBroadcast(String, String)} + * Service does not provide "setEnabled" method. Please use {@link #startBroadcast}, {@link + * #stopBroadcast()} or {@link #updateBroadcast(String, String)} */ public boolean setEnabled(BluetoothDevice device, boolean enabled) { return false; @@ -683,9 +775,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } if (mServiceBroadcast != null) { try { - BluetoothAdapter.getDefaultAdapter().closeProfileProxy( - BluetoothProfile.LE_AUDIO_BROADCAST, - mServiceBroadcast); + BluetoothAdapter.getDefaultAdapter() + .closeProfileProxy(BluetoothProfile.LE_AUDIO_BROADCAST, mServiceBroadcast); mServiceBroadcast = null; } catch (Throwable t) { Log.w(TAG, "Error cleaning up LeAudio proxy", t); @@ -694,13 +785,13 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } private String getDefaultValueOfProgramInfo() { - //set the default value; + // set the default value; int postfix = ThreadLocalRandom.current().nextInt(DEFAULT_CODE_MIN, DEFAULT_CODE_MAX); return BluetoothAdapter.getDefaultAdapter().getName() + UNDERLINE + postfix; } private byte[] getDefaultValueOfBroadcastCode() { - //set the default value; + // set the default value; return generateRandomPassword().getBytes(StandardCharsets.UTF_8); } @@ -708,14 +799,14 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { if (DEBUG) { Log.d(TAG, "resetCacheInfo:"); } - setAppSourceName("", /*updateContentResolver=*/ true); + setAppSourceName("", /* updateContentResolver= */ true); mBluetoothLeBroadcastMetadata = null; mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER; } private String generateRandomPassword() { String randomUUID = UUID.randomUUID().toString(); - //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + // first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx return randomUUID.substring(0, 8) + randomUUID.substring(9, 13); } @@ -752,5 +843,4 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { } } } - } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java index bb103b8896fd..34008ac56042 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistant.java @@ -39,13 +39,14 @@ import com.android.settingslib.R; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; - /** - * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app - * and the functionality of the local {@link BluetoothLeBroadcastAssistant}. - * Use the {@link BluetoothLeBroadcastAssistant.Callback} to get the result callback. + * LocalBluetoothLeBroadcastAssistant provides an interface between the Settings app and the + * functionality of the local {@link BluetoothLeBroadcastAssistant}. Use the {@link + * BluetoothLeBroadcastAssistant.Callback} to get the result callback. */ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile { private static final String TAG = "LocalBluetoothLeBroadcastAssistant"; @@ -62,58 +63,76 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata; private BluetoothLeBroadcastMetadata.Builder mBuilder; private boolean mIsProfileReady; + // Cached assistant callbacks being register before service is connected. + private final Map<BluetoothLeBroadcastAssistant.Callback, Executor> mCachedCallbackExecutorMap = + new ConcurrentHashMap<>(); + + private final ServiceListener mServiceListener = + new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (DEBUG) { + Log.d(TAG, "Bluetooth service connected"); + } + mService = (BluetoothLeBroadcastAssistant) proxy; + // We just bound to the service, so refresh the UI for any connected LeAudio + // devices. + List<BluetoothDevice> deviceList = mService.getConnectedDevices(); + while (!deviceList.isEmpty()) { + BluetoothDevice nextDevice = deviceList.remove(0); + CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); + // we may add a new device here, but generally this should not happen + if (device == null) { + if (DEBUG) { + Log.d( + TAG, + "LocalBluetoothLeBroadcastAssistant found new device: " + + nextDevice); + } + device = mDeviceManager.addDevice(nextDevice); + } + device.onProfileStateChanged( + LocalBluetoothLeBroadcastAssistant.this, + BluetoothProfile.STATE_CONNECTED); + device.refresh(); + } - private final ServiceListener mServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (DEBUG) { - Log.d(TAG, "Bluetooth service connected"); - } - mService = (BluetoothLeBroadcastAssistant) proxy; - // We just bound to the service, so refresh the UI for any connected LeAudio devices. - List<BluetoothDevice> deviceList = mService.getConnectedDevices(); - while (!deviceList.isEmpty()) { - BluetoothDevice nextDevice = deviceList.remove(0); - CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice); - // we may add a new device here, but generally this should not happen - if (device == null) { + mProfileManager.callServiceConnectedListeners(); + mIsProfileReady = true; if (DEBUG) { - Log.d(TAG, "LocalBluetoothLeBroadcastAssistant found new device: " - + nextDevice); + Log.d( + TAG, + "onServiceConnected, register mCachedCallbackExecutorMap = " + + mCachedCallbackExecutorMap); } - device = mDeviceManager.addDevice(nextDevice); + mCachedCallbackExecutorMap.forEach( + (callback, executor) -> registerServiceCallBack(executor, callback)); } - device.onProfileStateChanged(LocalBluetoothLeBroadcastAssistant.this, - BluetoothProfile.STATE_CONNECTED); - device.refresh(); - } - mProfileManager.callServiceConnectedListeners(); - mIsProfileReady = true; - } - - @Override - public void onServiceDisconnected(int profile) { - if (profile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) { - Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST_ASSISTANT"); - return; - } - if (DEBUG) { - Log.d(TAG, "Bluetooth service disconnected"); - } - mProfileManager.callServiceDisconnectedListeners(); - mIsProfileReady = false; - } - }; + @Override + public void onServiceDisconnected(int profile) { + if (profile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) { + Log.d(TAG, "The profile is not LE_AUDIO_BROADCAST_ASSISTANT"); + return; + } + if (DEBUG) { + Log.d(TAG, "Bluetooth service disconnected"); + } + mProfileManager.callServiceDisconnectedListeners(); + mIsProfileReady = false; + mCachedCallbackExecutorMap.clear(); + } + }; - public LocalBluetoothLeBroadcastAssistant(Context context, + public LocalBluetoothLeBroadcastAssistant( + Context context, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager) { mProfileManager = profileManager; mDeviceManager = deviceManager; - BluetoothAdapter.getDefaultAdapter(). - getProfileProxy(context, mServiceListener, - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); + BluetoothAdapter.getDefaultAdapter() + .getProfileProxy( + context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); mBuilder = new BluetoothLeBroadcastMetadata.Builder(); } @@ -123,11 +142,11 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile * @param sink Broadcast Sink to which the Broadcast Source should be added * @param metadata Broadcast Source metadata to be added to the Broadcast Sink * @param isGroupOp {@code true} if Application wants to perform this operation for all - * coordinated set members throughout this session. Otherwise, caller - * would have to add, modify, and remove individual set members. + * coordinated set members throughout this session. Otherwise, caller would have to add, + * modify, and remove individual set members. */ - public void addSource(BluetoothDevice sink, BluetoothLeBroadcastMetadata metadata, - boolean isGroupOp) { + public void addSource( + BluetoothDevice sink, BluetoothLeBroadcastMetadata metadata, boolean isGroupOp) { if (mService == null) { Log.d(TAG, "The BluetoothLeBroadcastAssistant is null"); return; @@ -140,36 +159,55 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile * the qr code string. * * @param sink Broadcast Sink to which the Broadcast Source should be added - * @param sourceAddressType hardware MAC Address of the device. See - * {@link BluetoothDevice.AddressType}. + * @param sourceAddressType hardware MAC Address of the device. See {@link + * BluetoothDevice.AddressType}. * @param presentationDelayMicros presentation delay of this Broadcast Source in microseconds. * @param sourceAdvertisingSid 1-byte long Advertising_SID of the Broadcast Source. * @param broadcastId 3-byte long Broadcast_ID of the Broadcast Source. - * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source, - * {@link BluetoothLeBroadcastMetadata#PA_SYNC_INTERVAL_UNKNOWN} if - * unknown. + * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source, {@link + * BluetoothLeBroadcastMetadata#PA_SYNC_INTERVAL_UNKNOWN} if unknown. * @param isEncrypted whether the Broadcast Source is encrypted. * @param broadcastCode Broadcast Code for this Broadcast Source, null if code is not required. * @param sourceDevice source advertiser address. * @param isGroupOp {@code true} if Application wants to perform this operation for all - * coordinated set members throughout this session. Otherwise, caller - * would have to add, modify, and remove individual set members. + * coordinated set members throughout this session. Otherwise, caller would have to add, + * modify, and remove individual set members. */ - public void addSource(@NonNull BluetoothDevice sink, int sourceAddressType, - int presentationDelayMicros, int sourceAdvertisingSid, int broadcastId, - int paSyncInterval, boolean isEncrypted, byte[] broadcastCode, - BluetoothDevice sourceDevice, boolean isGroupOp) { + public void addSource( + @NonNull BluetoothDevice sink, + int sourceAddressType, + int presentationDelayMicros, + int sourceAdvertisingSid, + int broadcastId, + int paSyncInterval, + boolean isEncrypted, + byte[] broadcastCode, + BluetoothDevice sourceDevice, + boolean isGroupOp) { if (DEBUG) { Log.d(TAG, "addSource()"); } - buildMetadata(sourceAddressType, presentationDelayMicros, sourceAdvertisingSid, broadcastId, - paSyncInterval, isEncrypted, broadcastCode, sourceDevice); + buildMetadata( + sourceAddressType, + presentationDelayMicros, + sourceAdvertisingSid, + broadcastId, + paSyncInterval, + isEncrypted, + broadcastCode, + sourceDevice); addSource(sink, mBluetoothLeBroadcastMetadata, isGroupOp); } - private void buildMetadata(int sourceAddressType, int presentationDelayMicros, - int sourceAdvertisingSid, int broadcastId, int paSyncInterval, boolean isEncrypted, - byte[] broadcastCode, BluetoothDevice sourceDevice) { + private void buildMetadata( + int sourceAddressType, + int presentationDelayMicros, + int sourceAdvertisingSid, + int broadcastId, + int paSyncInterval, + boolean isEncrypted, + byte[] broadcastCode, + BluetoothDevice sourceDevice) { mBluetoothLeBroadcastMetadata = mBuilder.setSourceDevice(sourceDevice, sourceAddressType) .setSourceAdvertisingSid(sourceAdvertisingSid) @@ -223,10 +261,10 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile /** * Stops an ongoing search for nearby Broadcast Sources. * - * On success, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopped(int)} will be - * called with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}. - * On failure, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopFailed(int)} will be - * called with reason code + * <p>On success, {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopped(int)} will be + * called with reason code {@link BluetoothStatusCodes#REASON_LOCAL_APP_REQUEST}. On failure, + * {@link BluetoothLeBroadcastAssistant.Callback#onSearchStopFailed(int)} will be called with + * reason code * * @throws IllegalStateException if callback was not registered */ @@ -245,8 +283,8 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile * Get information about all Broadcast Sources that a Broadcast Sink knows about. * * @param sink Broadcast Sink from which to get all Broadcast Sources - * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState} - * stored in the Broadcast Sink + * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState} stored + * in the Broadcast Sink * @throws NullPointerException when <var>sink</var> is null */ public @NonNull List<BluetoothLeBroadcastReceiveState> getAllSources( @@ -261,24 +299,50 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile return mService.getAllSources(sink); } - public void registerServiceCallBack(@NonNull @CallbackExecutor Executor executor, + /** + * Register Broadcast Assistant Callbacks to track its state and receivers + * + * @param executor Executor object for callback + * @param callback Callback object to be registered + */ + public void registerServiceCallBack( + @NonNull @CallbackExecutor Executor executor, @NonNull BluetoothLeBroadcastAssistant.Callback callback) { if (mService == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d( + TAG, + "registerServiceCallBack failed, the BluetoothLeBroadcastAssistant is null."); + mCachedCallbackExecutorMap.putIfAbsent(callback, executor); return; } - mService.registerCallback(executor, callback); + try { + mService.registerCallback(executor, callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "registerServiceCallBack failed. " + e.getMessage()); + } } + /** + * Unregister previously registered Broadcast Assistant Callbacks + * + * @param callback Callback object to be unregistered + */ public void unregisterServiceCallBack( @NonNull BluetoothLeBroadcastAssistant.Callback callback) { + mCachedCallbackExecutorMap.remove(callback); if (mService == null) { - Log.d(TAG, "The BluetoothLeBroadcast is null."); + Log.d( + TAG, + "unregisterServiceCallBack failed, the BluetoothLeBroadcastAssistant is null."); return; } - mService.unregisterCallback(callback); + try { + mService.unregisterCallback(callback); + } catch (IllegalArgumentException e) { + Log.w(TAG, "unregisterServiceCallBack failed. " + e.getMessage()); + } } public boolean isProfileReady() { @@ -310,9 +374,11 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile return new ArrayList<BluetoothDevice>(0); } return mService.getDevicesMatchingConnectionStates( - new int[]{BluetoothProfile.STATE_CONNECTED, - BluetoothProfile.STATE_CONNECTING, - BluetoothProfile.STATE_DISCONNECTING}); + new int[] { + BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_DISCONNECTING + }); } public boolean isEnabled(BluetoothDevice device) { @@ -373,9 +439,8 @@ public class LocalBluetoothLeBroadcastAssistant implements LocalBluetoothProfile } if (mService != null) { try { - BluetoothAdapter.getDefaultAdapter().closeProfileProxy( - BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, - mService); + BluetoothAdapter.getDefaultAdapter() + .closeProfileProxy(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, mService); mService = null; } catch (Throwable t) { Log.w(TAG, "Error cleaning up LeAudio proxy", t); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index d12d9d665a8c..bacab0f8f1e8 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -879,6 +879,9 @@ <uses-permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE" /> + <!-- Permissions required for CTS test - CtsAccessibilityServiceTestCases--> + <uses-permission android:name="android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 7061e2cb8a4e..f10ac1bf1e0a 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -204,6 +204,7 @@ android_library { "lottie", "LowLightDreamLib", "motion_tool_lib", + "notification_flags_lib", ], libs: [ "keepanno-annotations", @@ -328,6 +329,7 @@ android_library { "androidx.compose.ui_ui", "flag-junit", "platform-test-annotations", + "notification_flags_lib", ], } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index f7d9056c33dc..9c46ebdc5ac8 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -21,7 +21,6 @@ import android.app.ActivityTaskManager import android.app.PendingIntent import android.app.TaskInfo import android.graphics.Matrix -import android.graphics.Path import android.graphics.Rect import android.graphics.RectF import android.os.Build @@ -37,7 +36,6 @@ import android.view.SyncRtSurfaceTransactionApplier import android.view.View import android.view.ViewGroup import android.view.WindowManager -import android.view.animation.Interpolator import android.view.animation.PathInterpolator import androidx.annotation.AnyThread import androidx.annotation.BinderThread @@ -93,7 +91,7 @@ class ActivityLaunchAnimator( val INTERPOLATORS = LaunchAnimator.Interpolators( positionInterpolator = Interpolators.EMPHASIZED, - positionXInterpolator = createPositionXInterpolator(), + positionXInterpolator = Interpolators.EMPHASIZED_COMPLEMENT, contentBeforeFadeOutInterpolator = Interpolators.LINEAR_OUT_SLOW_IN, contentAfterFadeInInterpolator = PathInterpolator(0f, 0f, 0.6f, 1f) ) @@ -121,16 +119,6 @@ class ActivityLaunchAnimator( * cancelled by WM. */ private const val LONG_LAUNCH_TIMEOUT = 5_000L - - private fun createPositionXInterpolator(): Interpolator { - val path = - Path().apply { - moveTo(0f, 0f) - cubicTo(0.1217f, 0.0462f, 0.15f, 0.4686f, 0.1667f, 0.66f) - cubicTo(0.1834f, 0.8878f, 0.1667f, 1f, 1f, 1f) - } - return PathInterpolator(path) - } } /** diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt index 914e5f2c17bf..fd04b5ee0d9c 100644 --- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt +++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt @@ -51,6 +51,7 @@ object ComposeFacade : BaseComposeFacade { activity: ComponentActivity, viewModel: BaseCommunalViewModel, onOpenWidgetPicker: () -> Unit, + onEditDone: () -> Unit, ) { throwComposeUnavailableError() } diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt index 59bd95bd9027..5055ee1d73f6 100644 --- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt +++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt @@ -66,12 +66,14 @@ object ComposeFacade : BaseComposeFacade { activity: ComponentActivity, viewModel: BaseCommunalViewModel, onOpenWidgetPicker: () -> Unit, + onEditDone: () -> Unit, ) { activity.setContent { PlatformTheme { CommunalHub( viewModel = viewModel, onOpenWidgetPicker = onOpenWidgetPicker, + onEditDone = onEditDone, ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index e8ecd3a66186..2a9cf0fdc507 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -25,10 +25,12 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan @@ -36,23 +38,41 @@ import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.LayoutCoordinates +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.layout.positionInWindow +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView +import com.android.compose.theme.LocalAndroidColorScheme import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel @@ -66,21 +86,38 @@ fun CommunalHub( modifier: Modifier = Modifier, viewModel: BaseCommunalViewModel, onOpenWidgetPicker: (() -> Unit)? = null, + onEditDone: (() -> Unit)? = null, ) { val communalContent by viewModel.communalContent.collectAsState(initial = emptyList()) + var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } + var toolbarSize: IntSize? by remember { mutableStateOf(null) } + var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } + var isDraggingToRemove by remember { mutableStateOf(false) } + Box( modifier = modifier.fillMaxSize().background(Color.White), ) { CommunalHubLazyGrid( - modifier = Modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart), + modifier = Modifier.align(Alignment.CenterStart), communalContent = communalContent, - isEditMode = viewModel.isEditMode, viewModel = viewModel, - ) - if (viewModel.isEditMode && onOpenWidgetPicker != null) { - IconButton(onClick = onOpenWidgetPicker) { - Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text)) + contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize), + setGridCoordinates = { gridCoordinates = it }, + updateDragPositionForRemove = { + isDraggingToRemove = + checkForDraggingToRemove(it, removeButtonCoordinates, gridCoordinates) + isDraggingToRemove } + ) + + if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) { + Toolbar( + isDraggingToRemove = isDraggingToRemove, + setToolbarSize = { toolbarSize = it }, + setRemoveButtonCoordinates = { removeButtonCoordinates = it }, + onEditDone = onEditDone, + onOpenWidgetPicker = onOpenWidgetPicker, + ) } else { IconButton(onClick = viewModel::onOpenWidgetEditor) { Icon(Icons.Default.Edit, stringResource(R.string.button_to_open_widget_editor)) @@ -103,25 +140,38 @@ fun CommunalHub( @Composable private fun CommunalHubLazyGrid( communalContent: List<CommunalContentModel>, - isEditMode: Boolean, viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier, + contentPadding: PaddingValues, + setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit, + updateDragPositionForRemove: (offset: Offset) -> Boolean, ) { var gridModifier = modifier val gridState = rememberLazyGridState() var list = communalContent var dragDropState: GridDragDropState? = null - if (isEditMode && viewModel is CommunalEditModeViewModel) { + if (viewModel.isEditMode && viewModel is CommunalEditModeViewModel) { val contentListState = rememberContentListState(communalContent, viewModel) list = contentListState.list - dragDropState = rememberGridDragDropState(gridState, contentListState) - gridModifier = gridModifier.dragContainer(dragDropState) + dragDropState = + rememberGridDragDropState( + gridState = gridState, + contentListState = contentListState, + updateDragPositionForRemove = updateDragPositionForRemove + ) + gridModifier = + gridModifier + .fillMaxSize() + .dragContainer(dragDropState, beforeContentPadding(contentPadding)) + .onGloballyPositioned { setGridCoordinates(it) } + } else { + gridModifier = gridModifier.height(Dimensions.GridHeight) } LazyHorizontalGrid( modifier = gridModifier, state = gridState, rows = GridCells.Fixed(CommunalContentSize.FULL.span), - contentPadding = PaddingValues(horizontal = Dimensions.Spacing), + contentPadding = contentPadding, horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing), verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing), ) { @@ -130,19 +180,18 @@ private fun CommunalHubLazyGrid( key = { index -> list[index].key }, span = { index -> GridItemSpan(list[index].size.span) }, ) { index -> - val cardModifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth) + val cardModifier = Modifier.width(Dimensions.CardWidth) val size = SizeF( Dimensions.CardWidth.value, list[index].size.dp().value, ) - if (isEditMode && dragDropState != null) { + if (viewModel.isEditMode && dragDropState != null) { DraggableItem(dragDropState = dragDropState, enabled = true, index = index) { isDragging -> val elevation by animateDpAsState(if (isDragging) 4.dp else 1.dp) CommunalContent( modifier = cardModifier, - deleteOnClick = viewModel::onDeleteWidget, elevation = elevation, model = list[index], viewModel = viewModel, @@ -161,6 +210,95 @@ private fun CommunalHubLazyGrid( } } +/** + * Toolbar that contains action buttons to + * 1) open the widget picker + * 2) remove a widget from the grid and + * 3) exit the edit mode. + */ +@Composable +private fun Toolbar( + isDraggingToRemove: Boolean, + setToolbarSize: (toolbarSize: IntSize) -> Unit, + setRemoveButtonCoordinates: (coordinates: LayoutCoordinates) -> Unit, + onOpenWidgetPicker: () -> Unit, + onEditDone: () -> Unit, +) { + Row( + modifier = + Modifier.fillMaxWidth() + .padding( + top = Dimensions.ToolbarPaddingTop, + start = Dimensions.ToolbarPaddingHorizontal, + end = Dimensions.ToolbarPaddingHorizontal, + ) + .onSizeChanged { setToolbarSize(it) }, + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + val buttonContentPadding = + PaddingValues( + vertical = Dimensions.ToolbarButtonPaddingVertical, + horizontal = Dimensions.ToolbarButtonPaddingHorizontal, + ) + val spacerModifier = Modifier.width(Dimensions.ToolbarButtonSpaceBetween) + Button( + onClick = onOpenWidgetPicker, + colors = filledSecondaryButtonColors(), + contentPadding = buttonContentPadding + ) { + Icon(Icons.Default.Add, stringResource(R.string.button_to_open_widget_editor)) + Spacer(spacerModifier) + Text( + text = stringResource(R.string.hub_mode_add_widget_button_text), + ) + } + + val buttonColors = + if (isDraggingToRemove) filledButtonColors() else ButtonDefaults.outlinedButtonColors() + OutlinedButton( + onClick = {}, + colors = buttonColors, + contentPadding = buttonContentPadding, + modifier = Modifier.onGloballyPositioned { setRemoveButtonCoordinates(it) }, + ) { + Icon(Icons.Outlined.Delete, stringResource(R.string.button_to_open_widget_editor)) + Spacer(spacerModifier) + Text( + text = stringResource(R.string.button_to_remove_widget), + ) + } + + Button( + onClick = onEditDone, + colors = filledButtonColors(), + contentPadding = buttonContentPadding + ) { + Text( + text = stringResource(R.string.hub_mode_editing_exit_button_text), + ) + } + } +} + +@Composable +private fun filledButtonColors(): ButtonColors { + val colors = LocalAndroidColorScheme.current + return ButtonDefaults.buttonColors( + containerColor = colors.primary, + contentColor = colors.onPrimary, + ) +} + +@Composable +private fun filledSecondaryButtonColors(): ButtonColors { + val colors = LocalAndroidColorScheme.current + return ButtonDefaults.buttonColors( + containerColor = colors.secondary, + contentColor = colors.onSecondary, + ) +} + @Composable private fun CommunalContent( model: CommunalContentModel, @@ -168,11 +306,9 @@ private fun CommunalContent( size: SizeF, modifier: Modifier = Modifier, elevation: Dp = 0.dp, - deleteOnClick: ((id: Int) -> Unit)? = null, ) { when (model) { - is CommunalContentModel.Widget -> - WidgetContent(model, size, elevation, deleteOnClick, modifier) + is CommunalContentModel.Widget -> WidgetContent(model, size, elevation, modifier) is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier) is CommunalContentModel.Tutorial -> TutorialContent(modifier) is CommunalContentModel.Umo -> Umo(viewModel, modifier) @@ -184,19 +320,12 @@ private fun WidgetContent( model: CommunalContentModel.Widget, size: SizeF, elevation: Dp, - deleteOnClick: ((id: Int) -> Unit)?, modifier: Modifier = Modifier, ) { - // TODO(b/309009246): update background color Card( - modifier = modifier.fillMaxSize().background(Color.White), + modifier = modifier.height(size.height.dp), elevation = CardDefaults.cardElevation(draggedElevation = elevation), ) { - if (deleteOnClick != null) { - IconButton(onClick = { deleteOnClick(model.appWidgetId) }) { - Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget)) - } - } AndroidView( modifier = modifier, factory = { context -> @@ -249,6 +378,60 @@ private fun Umo(viewModel: BaseCommunalViewModel, modifier: Modifier = Modifier) ) } +/** + * Returns the `contentPadding` of the grid. Use the vertical padding to push the grid content area + * below the toolbar and let the grid take the max size. This ensures the item can be dragged + * outside the grid over the toolbar, without part of it getting clipped by the container. + */ +@Composable +private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): PaddingValues { + if (!isEditMode || toolbarSize == null) { + return PaddingValues(horizontal = Dimensions.Spacing) + } + val configuration = LocalConfiguration.current + val density = LocalDensity.current + val screenHeight = configuration.screenHeightDp.dp + val toolbarHeight = with(density) { Dimensions.ToolbarPaddingTop + toolbarSize.height.toDp() } + val verticalPadding = + ((screenHeight - toolbarHeight - Dimensions.GridHeight) / 2).coerceAtLeast( + Dimensions.Spacing + ) + return PaddingValues( + start = Dimensions.ToolbarPaddingHorizontal, + end = Dimensions.ToolbarPaddingHorizontal, + top = verticalPadding + toolbarHeight, + bottom = verticalPadding + ) +} + +@Composable +private fun beforeContentPadding(paddingValues: PaddingValues): ContentPaddingInPx { + return with(LocalDensity.current) { + ContentPaddingInPx( + startPadding = paddingValues.calculateLeftPadding(LayoutDirection.Ltr).toPx(), + topPadding = paddingValues.calculateTopPadding().toPx() + ) + } +} + +/** + * Check whether the pointer position that the item is being dragged at is within the coordinates of + * the remove button in the toolbar. Returns true if the item is removable. + */ +private fun checkForDraggingToRemove( + offset: Offset, + removeButtonCoordinates: LayoutCoordinates?, + gridCoordinates: LayoutCoordinates?, +): Boolean { + if (removeButtonCoordinates == null || gridCoordinates == null) { + return false + } + val pointer = gridCoordinates.positionInWindow() + offset + val removeButton = removeButtonCoordinates.positionInWindow() + return pointer.x in removeButton.x..removeButton.x + removeButtonCoordinates.size.width && + pointer.y in removeButton.y..removeButton.y + removeButtonCoordinates.size.height +} + private fun CommunalContentSize.dp(): Dp { return when (this) { CommunalContentSize.FULL -> Dimensions.CardHeightFull @@ -257,6 +440,8 @@ private fun CommunalContentSize.dp(): Dp { } } +data class ContentPaddingInPx(val startPadding: Float, val topPadding: Float) + object Dimensions { val CardWidth = 464.dp val CardHeightFull = 630.dp @@ -264,4 +449,11 @@ object Dimensions { val CardHeightThird = 199.dp val GridHeight = CardHeightFull val Spacing = 16.dp + + // The sizing/padding of the toolbar in glanceable hub edit mode + val ToolbarPaddingTop = 27.dp + val ToolbarPaddingHorizontal = 16.dp + val ToolbarButtonPaddingHorizontal = 24.dp + val ToolbarButtonPaddingVertical = 16.dp + val ToolbarButtonSpaceBetween = 8.dp } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index 6cfa2f233f46..5451d0550095 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -48,12 +48,18 @@ import kotlinx.coroutines.launch @Composable fun rememberGridDragDropState( gridState: LazyGridState, - contentListState: ContentListState + contentListState: ContentListState, + updateDragPositionForRemove: (offset: Offset) -> Boolean, ): GridDragDropState { val scope = rememberCoroutineScope() val state = remember(gridState, contentListState) { - GridDragDropState(state = gridState, contentListState = contentListState, scope = scope) + GridDragDropState( + state = gridState, + contentListState = contentListState, + scope = scope, + updateDragPositionForRemove = updateDragPositionForRemove + ) } LaunchedEffect(state) { while (true) { @@ -67,23 +73,30 @@ fun rememberGridDragDropState( /** * Handles drag and drop cards in the glanceable hub. While dragging to move, other items that are * affected will dynamically get positioned and the state is tracked by [ContentListState]. When - * dragging to remove, affected cards will be moved and [ContentListState.onRemove] is called to - * remove the dragged item. On dragging ends, call [ContentListState.onSaveList] to persist the - * change. + * dragging to remove, affected cards will be moved and [updateDragPositionForRemove] is called to + * check whether the dragged item can be removed. On dragging ends, call [ContentListState.onRemove] + * to remove the dragged item if condition met and call [ContentListState.onSaveList] to persist any + * change in ordering. */ class GridDragDropState internal constructor( private val state: LazyGridState, private val contentListState: ContentListState, private val scope: CoroutineScope, + private val updateDragPositionForRemove: (offset: Offset) -> Boolean ) { var draggingItemIndex by mutableStateOf<Int?>(null) private set + var isDraggingToRemove by mutableStateOf(false) + private set + internal val scrollChannel = Channel<Float>() private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero) private var draggingItemInitialOffset by mutableStateOf(Offset.Zero) + private var dragStartPointerOffset by mutableStateOf(Offset.Zero) + internal val draggingItemOffset: Offset get() = draggingItemLayoutInfo?.let { item -> @@ -94,27 +107,36 @@ internal constructor( private val draggingItemLayoutInfo: LazyGridItemInfo? get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex } - internal fun onDragStart(offset: Offset) { + internal fun onDragStart(offset: Offset, contentOffset: Offset) { state.layoutInfo.visibleItemsInfo .firstOrNull { item -> + // grid item offset is based off grid content container so we need to deduct + // before content padding from the initial pointer position item.isEditable && - offset.x.toInt() in item.offset.x..item.offsetEnd.x && - offset.y.toInt() in item.offset.y..item.offsetEnd.y + (offset.x - contentOffset.x).toInt() in item.offset.x..item.offsetEnd.x && + (offset.y - contentOffset.y).toInt() in item.offset.y..item.offsetEnd.y } ?.apply { + dragStartPointerOffset = offset - this.offset.toOffset() draggingItemIndex = index draggingItemInitialOffset = this.offset.toOffset() } } internal fun onDragInterrupted() { - if (draggingItemIndex != null) { + draggingItemIndex?.let { + if (isDraggingToRemove) { + contentListState.onRemove(it) + isDraggingToRemove = false + updateDragPositionForRemove(Offset.Zero) + } // persist list editing changes on dragging ends contentListState.onSaveList() draggingItemIndex = null } draggingItemDraggedDelta = Offset.Zero draggingItemInitialOffset = Offset.Zero + dragStartPointerOffset = Offset.Zero } internal fun onDrag(offset: Offset) { @@ -152,18 +174,13 @@ internal constructor( contentListState.onMove(draggingItem.index, targetItem.index) } draggingItemIndex = targetItem.index + isDraggingToRemove = false } else { val overscroll = checkForOverscroll(startOffset, endOffset) if (overscroll != 0f) { scrollChannel.trySend(overscroll) } - val removeOffset = checkForRemove(startOffset) - if (removeOffset != 0f) { - draggingItemIndex?.let { - contentListState.onRemove(it) - draggingItemIndex = null - } - } + isDraggingToRemove = checkForRemove(startOffset) } } @@ -185,14 +202,11 @@ internal constructor( } } - // TODO(b/309968801): a temporary solution to decide whether to remove card when it's dragged up - // and out of grid. Once we have a taskbar, calculate the intersection of the dragged item with - // the Remove button. - private fun checkForRemove(startOffset: Offset): Float { + /** Calls the callback with the updated drag position and returns whether to remove the item. */ + private fun checkForRemove(startOffset: Offset): Boolean { return if (draggingItemDraggedDelta.y < 0) - (startOffset.y + Dimensions.CardHeightHalf.value - state.layoutInfo.viewportStartOffset) - .coerceAtMost(0f) - else 0f + updateDragPositionForRemove(startOffset + dragStartPointerOffset) + else false } } @@ -204,14 +218,22 @@ private operator fun Offset.plus(size: Size): Offset { return Offset(x + size.width, y + size.height) } -fun Modifier.dragContainer(dragDropState: GridDragDropState): Modifier { - return pointerInput(dragDropState) { +fun Modifier.dragContainer( + dragDropState: GridDragDropState, + beforeContentPadding: ContentPaddingInPx +): Modifier { + return pointerInput(dragDropState, beforeContentPadding) { detectDragGesturesAfterLongPress( onDrag = { change, offset -> change.consume() dragDropState.onDrag(offset = offset) }, - onDragStart = { offset -> dragDropState.onDragStart(offset) }, + onDragStart = { offset -> + dragDropState.onDragStart( + offset, + Offset(beforeContentPadding.startPadding, beforeContentPadding.topPadding) + ) + }, onDragEnd = { dragDropState.onDragInterrupted() }, onDragCancel = { dragDropState.onDragInterrupted() } ) @@ -237,6 +259,7 @@ fun LazyGridItemScope.DraggableItem( Modifier.zIndex(1f).graphicsLayer { translationX = dragDropState.draggingItemOffset.x translationY = dragDropState.draggingItemOffset.y + alpha = if (dragDropState.isDraggingToRemove) 0.5f else 1f } } else { Modifier.animateItemPlacement() diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt index 009f8bb38e61..e538e093e60f 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt @@ -175,11 +175,7 @@ private fun <T> computeValue( canOverflow: Boolean, ): T { val state = layoutImpl.state.transitionState - if ( - state !is TransitionState.Transition || - state.fromScene == state.toScene || - !layoutImpl.isTransitionReady(state) - ) { + if (state !is TransitionState.Transition || !layoutImpl.isTransitionReady(state)) { return sharedValue.value } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt index 199832bc4ab6..de69c37d4630 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt @@ -47,12 +47,6 @@ internal fun CoroutineScope.animateToScene( when (state) { is TransitionState.Idle -> animate(layoutImpl, target) is TransitionState.Transition -> { - if (state.toScene == state.fromScene) { - // Same as idle. - animate(layoutImpl, target) - return - } - // A transition is currently running: first check whether `transition.toScene` or // `transition.fromScene` is the same as our target scene, in which case the transition // can be accelerated or reversed to end up in the target state. @@ -153,13 +147,13 @@ private fun CoroutineScope.animate( } private class OneOffTransition( - override val fromScene: SceneKey, - override val toScene: SceneKey, + fromScene: SceneKey, + toScene: SceneKey, override val currentScene: SceneKey, override val isInitiatedByUserInput: Boolean, override val isUserInputOngoing: Boolean, private val animatable: Animatable<Float, AnimationVector1D>, -) : TransitionState.Transition { +) : TransitionState.Transition(fromScene, toScene) { override val progress: Float get() = animatable.value } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index 31604a6817f0..431a8aef6d3d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -288,7 +288,6 @@ private fun shouldDrawElement( // Always draw the element if there is no ongoing transition or if the element is not shared. if ( state !is TransitionState.Transition || - state.fromScene == state.toScene || !layoutImpl.isTransitionReady(state) || state.fromScene !in element.sceneValues || state.toScene !in element.sceneValues @@ -374,7 +373,7 @@ private fun isElementOpaque( ): Boolean { val state = layoutImpl.state.transitionState - if (state !is TransitionState.Transition || state.fromScene == state.toScene) { + if (state !is TransitionState.Transition) { return true } @@ -611,7 +610,7 @@ private inline fun <T> computeValue( val state = layoutImpl.state.transitionState // There is no ongoing transition. - if (state !is TransitionState.Transition || state.fromScene == state.toScene) { + if (state !is TransitionState.Transition) { // Even if this element SceneTransitionLayout is not animated, the layout itself might be // animated (e.g. by another parent SceneTransitionLayout), in which case this element still // need to participate in the layout phase. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt index fa385d014ccb..7029da2edb0d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt @@ -131,10 +131,6 @@ private fun shouldComposeMovableElement( val fromScene = (transitionState as TransitionState.Transition).fromScene val toScene = transitionState.toScene - if (fromScene == toScene) { - check(fromScene == scene) - return true - } val fromReady = layoutImpl.isSceneReady(fromScene) val toReady = layoutImpl.isSceneReady(toScene) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt index ded6cc155b0b..32025b4f1258 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt @@ -73,37 +73,37 @@ enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) { internal fun Modifier.nestedScrollToScene( layoutImpl: SceneTransitionLayoutImpl, orientation: Orientation, - startBehavior: NestedScrollBehavior, - endBehavior: NestedScrollBehavior, + topOrLeftBehavior: NestedScrollBehavior, + bottomOrRightBehavior: NestedScrollBehavior, ) = this then NestedScrollToSceneElement( layoutImpl = layoutImpl, orientation = orientation, - startBehavior = startBehavior, - endBehavior = endBehavior, + topOrLeftBehavior = topOrLeftBehavior, + bottomOrRightBehavior = bottomOrRightBehavior, ) private data class NestedScrollToSceneElement( private val layoutImpl: SceneTransitionLayoutImpl, private val orientation: Orientation, - private val startBehavior: NestedScrollBehavior, - private val endBehavior: NestedScrollBehavior, + private val topOrLeftBehavior: NestedScrollBehavior, + private val bottomOrRightBehavior: NestedScrollBehavior, ) : ModifierNodeElement<NestedScrollToSceneNode>() { override fun create() = NestedScrollToSceneNode( layoutImpl = layoutImpl, orientation = orientation, - startBehavior = startBehavior, - endBehavior = endBehavior, + topOrLeftBehavior = topOrLeftBehavior, + bottomOrRightBehavior = bottomOrRightBehavior, ) override fun update(node: NestedScrollToSceneNode) { node.update( layoutImpl = layoutImpl, orientation = orientation, - startBehavior = startBehavior, - endBehavior = endBehavior, + topOrLeftBehavior = topOrLeftBehavior, + bottomOrRightBehavior = bottomOrRightBehavior, ) } @@ -111,23 +111,23 @@ private data class NestedScrollToSceneElement( name = "nestedScrollToScene" properties["layoutImpl"] = layoutImpl properties["orientation"] = orientation - properties["startBehavior"] = startBehavior - properties["endBehavior"] = endBehavior + properties["topOrLeftBehavior"] = topOrLeftBehavior + properties["bottomOrRightBehavior"] = bottomOrRightBehavior } } private class NestedScrollToSceneNode( layoutImpl: SceneTransitionLayoutImpl, orientation: Orientation, - startBehavior: NestedScrollBehavior, - endBehavior: NestedScrollBehavior, + topOrLeftBehavior: NestedScrollBehavior, + bottomOrRightBehavior: NestedScrollBehavior, ) : DelegatingNode() { private var priorityNestedScrollConnection: PriorityNestedScrollConnection = scenePriorityNestedScrollConnection( layoutImpl = layoutImpl, orientation = orientation, - startBehavior = startBehavior, - endBehavior = endBehavior, + topOrLeftBehavior = topOrLeftBehavior, + bottomOrRightBehavior = bottomOrRightBehavior, ) private var nestedScrollNode: DelegatableNode = @@ -148,8 +148,8 @@ private class NestedScrollToSceneNode( fun update( layoutImpl: SceneTransitionLayoutImpl, orientation: Orientation, - startBehavior: NestedScrollBehavior, - endBehavior: NestedScrollBehavior, + topOrLeftBehavior: NestedScrollBehavior, + bottomOrRightBehavior: NestedScrollBehavior, ) { // Clean up the old nested scroll connection priorityNestedScrollConnection.reset() @@ -160,8 +160,8 @@ private class NestedScrollToSceneNode( scenePriorityNestedScrollConnection( layoutImpl = layoutImpl, orientation = orientation, - startBehavior = startBehavior, - endBehavior = endBehavior, + topOrLeftBehavior = topOrLeftBehavior, + bottomOrRightBehavior = bottomOrRightBehavior, ) nestedScrollNode = nestedScrollModifierNode( @@ -175,12 +175,12 @@ private class NestedScrollToSceneNode( private fun scenePriorityNestedScrollConnection( layoutImpl: SceneTransitionLayoutImpl, orientation: Orientation, - startBehavior: NestedScrollBehavior, - endBehavior: NestedScrollBehavior, + topOrLeftBehavior: NestedScrollBehavior, + bottomOrRightBehavior: NestedScrollBehavior, ) = SceneNestedScrollHandler( gestureHandler = layoutImpl.gestureHandler(orientation = orientation), - startBehavior = startBehavior, - endBehavior = endBehavior, + topOrLeftBehavior = topOrLeftBehavior, + bottomOrRightBehavior = bottomOrRightBehavior, ) .connection diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt index 1b79dbdee510..983cff83ed57 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt @@ -73,17 +73,13 @@ fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTrans when (val state = transitionState) { is TransitionState.Idle -> ObservableTransitionState.Idle(state.currentScene) is TransitionState.Transition -> { - if (state.fromScene == state.toScene) { - ObservableTransitionState.Idle(state.currentScene) - } else { - ObservableTransitionState.Transition( - fromScene = state.fromScene, - toScene = state.toScene, - progress = snapshotFlow { state.progress }, - isInitiatedByUserInput = state.isInitiatedByUserInput, - isUserInputOngoing = snapshotFlow { state.isUserInputOngoing }, - ) - } + ObservableTransitionState.Transition( + fromScene = state.fromScene, + toScene = state.toScene, + progress = snapshotFlow { state.progress }, + isInitiatedByUserInput = state.isInitiatedByUserInput, + isUserInputOngoing = snapshotFlow { state.isUserInputOngoing }, + ) } } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt index f5561cb404b6..6a7a3a00d4fe 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt @@ -86,16 +86,26 @@ private class SceneScopeImpl( return element(layoutImpl, scene, key) } - override fun Modifier.nestedScrollToScene( - orientation: Orientation, - startBehavior: NestedScrollBehavior, - endBehavior: NestedScrollBehavior, + override fun Modifier.horizontalNestedScrollToScene( + leftBehavior: NestedScrollBehavior, + rightBehavior: NestedScrollBehavior, ): Modifier = nestedScrollToScene( layoutImpl = layoutImpl, - orientation = orientation, - startBehavior = startBehavior, - endBehavior = endBehavior, + orientation = Orientation.Horizontal, + topOrLeftBehavior = leftBehavior, + bottomOrRightBehavior = rightBehavior, + ) + + override fun Modifier.verticalNestedScrollToScene( + topBehavior: NestedScrollBehavior, + bottomBehavior: NestedScrollBehavior + ): Modifier = + nestedScrollToScene( + layoutImpl = layoutImpl, + orientation = Orientation.Vertical, + topOrLeftBehavior = topBehavior, + bottomOrRightBehavior = bottomBehavior, ) @Composable diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt index 01179d3b8f73..91decf4d8b7e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt @@ -49,8 +49,12 @@ internal class SceneGestureHandler( layoutImpl.state.transitionState = value } - internal var swipeTransition: SwipeTransition = SwipeTransition(currentScene, currentScene, 1f) - private set + private var _swipeTransition: SwipeTransition? = null + internal var swipeTransition: SwipeTransition + get() = _swipeTransition ?: error("SwipeTransition needs to be initialized") + set(value) { + _swipeTransition = value + } private fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) { if (isDrivingTransition || force) transitionState = newTransition @@ -61,7 +65,7 @@ internal class SceneGestureHandler( get() = layoutImpl.scene(transitionState.currentScene) internal val isDrivingTransition - get() = transitionState == swipeTransition + get() = transitionState == _swipeTransition /** * The velocity threshold at which the intent of the user is to swipe up or down. It is the same @@ -82,12 +86,15 @@ internal class SceneGestureHandler( private var actionDownOrRight: UserAction? = null private var actionUpOrLeftNoEdge: UserAction? = null private var actionDownOrRightNoEdge: UserAction? = null + private var upOrLeftScene: SceneKey? = null + private var downOrRightScene: SceneKey? = null internal fun onDragStarted(pointersDown: Int, startedPosition: Offset?, overSlop: Float) { if (isDrivingTransition) { // This [transition] was already driving the animation: simply take over it. // Stop animating and start from where the current offset. swipeTransition.cancelOffsetAnimation() + updateTargetScenes(swipeTransition._fromScene) return } @@ -105,11 +112,8 @@ internal class SceneGestureHandler( val fromScene = currentScene setCurrentActions(fromScene, startedPosition, pointersDown) - if (fromScene.upOrLeft() == null && fromScene.downOrRight() == null) { - return - } - - val (targetScene, distance) = fromScene.findTargetSceneAndDistance(overSlop) + val (targetScene, distance) = + findTargetSceneAndDistance(fromScene, overSlop, updateScenes = true) ?: return updateTransition(SwipeTransition(fromScene, targetScene, distance), force = true) } @@ -179,16 +183,21 @@ internal class SceneGestureHandler( val (fromScene, acceleratedOffset) = computeFromSceneConsideringAcceleratedSwipe(swipeTransition) - swipeTransition.dragOffset += acceleratedOffset - // Compute the target scene depending on the current offset. + val isNewFromScene = fromScene.key != swipeTransition.fromScene val (targetScene, distance) = - fromScene.findTargetSceneAndDistance(swipeTransition.dragOffset) + findTargetSceneAndDistance( + fromScene, + swipeTransition.dragOffset, + updateScenes = isNewFromScene, + ) + ?: run { + onDragStopped(delta, true) + return + } + swipeTransition.dragOffset += acceleratedOffset - // TODO(b/290184746): support long scroll A => B => C? especially for non fullscreen scenes - if ( - fromScene.key != swipeTransition.fromScene || targetScene.key != swipeTransition.toScene - ) { + if (isNewFromScene || targetScene.key != swipeTransition.toScene) { updateTransition( SwipeTransition(fromScene, targetScene, distance).apply { this.dragOffset = swipeTransition.dragOffset @@ -197,6 +206,11 @@ internal class SceneGestureHandler( } } + private fun updateTargetScenes(fromScene: Scene) { + upOrLeftScene = fromScene.upOrLeft() + downOrRightScene = fromScene.downOrRight() + } + /** * Change fromScene in the case where the user quickly swiped multiple times in the same * direction to accelerate the transition from A => B then B => C. @@ -214,37 +228,71 @@ internal class SceneGestureHandler( val absoluteDistance = swipeTransition.distance.absoluteValue // If the swipe was not committed, don't do anything. - if (fromScene == toScene || swipeTransition._currentScene != toScene) { + if (swipeTransition._currentScene != toScene) { return Pair(fromScene, 0f) } // If the offset is past the distance then let's change fromScene so that the user can swipe // to the next screen or go back to the previous one. val offset = swipeTransition.dragOffset - return if (offset <= -absoluteDistance && fromScene.upOrLeft() == toScene.key) { + return if (offset <= -absoluteDistance && upOrLeftScene == toScene.key) { Pair(toScene, absoluteDistance) - } else if (offset >= absoluteDistance && fromScene.downOrRight() == toScene.key) { + } else if (offset >= absoluteDistance && downOrRightScene == toScene.key) { Pair(toScene, -absoluteDistance) } else { Pair(fromScene, 0f) } } - // TODO(b/290184746): there are two bugs here: - // 1. if both upOrLeft and downOrRight become `null` during a transition this will crash - // 2. if one of them changes during a transition, the transition will jump cut to the new target - private inline fun Scene.findTargetSceneAndDistance( - directionOffset: Float - ): Pair<Scene, Float> { - val upOrLeft = upOrLeft() - val downOrRight = downOrRight() - val absoluteDistance = getAbsoluteDistance() + /** + * Returns the target scene and distance from [fromScene] in the direction [directionOffset]. + * + * @param fromScene the scene from which we look for the target + * @param directionOffset signed float that indicates the direction. Positive is down or right + * negative is up or left. + * @param updateScenes whether the target scenes should be updated to the current values held in + * the Scenes map. Usually we don't want to update them while doing a drag, because this could + * change the target scene (jump cutting) to a different scene, when some system state changed + * the targets the background. However, an update is needed any time we calculate the targets + * for a new fromScene. + * @return null when there are no targets in either direction. If one direction is null and you + * drag into the null direction this function will return the opposite direction, assuming + * that the users intention is to start the drag into the other direction eventually. If + * [directionOffset] is 0f and both direction are available, it will default to + * [upOrLeftScene]. + */ + private inline fun findTargetSceneAndDistance( + fromScene: Scene, + directionOffset: Float, + updateScenes: Boolean, + ): Pair<Scene, Float>? { + if (updateScenes) updateTargetScenes(fromScene) + val absoluteDistance = fromScene.getAbsoluteDistance() // Compute the target scene depending on the current offset. - return if ((directionOffset < 0f && upOrLeft != null) || downOrRight == null) { - Pair(layoutImpl.scene(upOrLeft!!), -absoluteDistance) - } else { - Pair(layoutImpl.scene(downOrRight), absoluteDistance) + return when { + upOrLeftScene == null && downOrRightScene == null -> null + (directionOffset < 0f && upOrLeftScene != null) || downOrRightScene == null -> + Pair(layoutImpl.scene(upOrLeftScene!!), -absoluteDistance) + else -> Pair(layoutImpl.scene(downOrRightScene!!), absoluteDistance) + } + } + + /** + * A strict version of [findTargetSceneAndDistance] that will return null when there is no Scene + * in [directionOffset] direction + */ + private inline fun findTargetSceneAndDistanceStrict( + fromScene: Scene, + directionOffset: Float, + ): Pair<Scene, Float>? { + val absoluteDistance = fromScene.getAbsoluteDistance() + return when { + directionOffset > 0f -> + upOrLeftScene?.let { Pair(layoutImpl.scene(it), -absoluteDistance) } + directionOffset < 0f -> + downOrRightScene?.let { Pair(layoutImpl.scene(it), absoluteDistance) } + else -> null } } @@ -311,20 +359,21 @@ internal class SceneGestureHandler( val startFromIdlePosition = swipeTransition.dragOffset == 0f if (startFromIdlePosition) { - // If there is a next scene, we start the overscroll animation. - val (targetScene, distance) = fromScene.findTargetSceneAndDistance(velocity) - val isValidTarget = distance != 0f && targetScene.key != fromScene.key - if (isValidTarget) { - updateTransition( - SwipeTransition(fromScene, targetScene, distance).apply { - _currentScene = swipeTransition._currentScene + // If there is a target scene, we start the overscroll animation. + val (targetScene, distance) = + findTargetSceneAndDistanceStrict(fromScene, velocity) + ?: run { + // We will not animate + transitionState = TransitionState.Idle(fromScene.key) + return } - ) - animateTo(targetScene = fromScene, targetOffset = 0f) - } else { - // We will not animate - transitionState = TransitionState.Idle(fromScene.key) - } + + updateTransition( + SwipeTransition(fromScene, targetScene, distance).apply { + _currentScene = swipeTransition._currentScene + } + ) + animateTo(targetScene = fromScene, targetOffset = 0f) } else { // We were between two scenes: animate to the initial scene. animateTo(targetScene = fromScene, targetOffset = 0f) @@ -410,15 +459,11 @@ internal class SceneGestureHandler( * above or to the left of [toScene]. */ val distance: Float - ) : TransitionState.Transition { + ) : TransitionState.Transition(_fromScene.key, _toScene.key) { var _currentScene by mutableStateOf(_fromScene) override val currentScene: SceneKey get() = _currentScene.key - override val fromScene: SceneKey = _fromScene.key - - override val toScene: SceneKey = _toScene.key - override val progress: Float get() { val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset @@ -495,8 +540,8 @@ private class SceneDraggableHandler( internal class SceneNestedScrollHandler( private val gestureHandler: SceneGestureHandler, - private val startBehavior: NestedScrollBehavior, - private val endBehavior: NestedScrollBehavior, + private val topOrLeftBehavior: NestedScrollBehavior, + private val bottomOrRightBehavior: NestedScrollBehavior, ) : NestedScrollHandler { override val connection: PriorityNestedScrollConnection = nestedScrollConnection() @@ -565,8 +610,8 @@ internal class SceneNestedScrollHandler( canStartPostScroll = { offsetAvailable, offsetBeforeStart -> val behavior: NestedScrollBehavior = when { - offsetAvailable > 0f -> startBehavior - offsetAvailable < 0f -> endBehavior + offsetAvailable > 0f -> topOrLeftBehavior + offsetAvailable < 0f -> bottomOrRightBehavior else -> return@PriorityNestedScrollConnection false } @@ -594,8 +639,8 @@ internal class SceneNestedScrollHandler( canStartPostFling = { velocityAvailable -> val behavior: NestedScrollBehavior = when { - velocityAvailable > 0f -> startBehavior - velocityAvailable < 0f -> endBehavior + velocityAvailable > 0f -> topOrLeftBehavior + velocityAvailable < 0f -> bottomOrRightBehavior else -> return@PriorityNestedScrollConnection false } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index afa184b15901..239971ff6be8 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -126,14 +126,24 @@ interface SceneScope { * Adds a [NestedScrollConnection] to intercept scroll events not handled by the scrollable * component. * - * @param orientation is used to determine if we handle top/bottom or left/right events. - * @param startBehavior when we should perform the overscroll animation at the top/left. - * @param endBehavior when we should perform the overscroll animation at the bottom/right. + * @param leftBehavior when we should perform the overscroll animation at the left. + * @param rightBehavior when we should perform the overscroll animation at the right. */ - fun Modifier.nestedScrollToScene( - orientation: Orientation, - startBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview, - endBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview, + fun Modifier.horizontalNestedScrollToScene( + leftBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview, + rightBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview, + ): Modifier + + /** + * Adds a [NestedScrollConnection] to intercept scroll events not handled by the scrollable + * component. + * + * @param topBehavior when we should perform the overscroll animation at the top. + * @param bottomBehavior when we should perform the overscroll animation at the bottom. + */ + fun Modifier.verticalNestedScrollToScene( + topBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview, + bottomBehavior: NestedScrollBehavior = NestedScrollBehavior.EdgeNoPreview, ): Modifier /** diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index 02ddccbc051b..00e33e24c41e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -172,7 +172,7 @@ internal class SceneTransitionLayoutImpl( val width: Int val height: Int val state = state.transitionState - if (state !is TransitionState.Transition || state.fromScene == state.toScene) { + if (state !is TransitionState.Transition) { width = placeable.width height = placeable.height } else { @@ -232,10 +232,7 @@ internal class SceneTransitionLayoutImpl( is TransitionState.Idle -> drawContent() is TransitionState.Transition -> { // Don't draw scenes that are not ready yet. - if ( - readyScenes.containsKey(key) || - state.fromScene == state.toScene - ) { + if (readyScenes.containsKey(key)) { drawContent() } } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index f48e9147eef4..623725582a9d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -39,11 +39,6 @@ class SceneTransitionLayoutState(initialScene: SceneKey) { fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean { val transition = transitionState as? TransitionState.Transition ?: return false - // TODO(b/310915136): Remove this check. - if (transition.fromScene == transition.toScene) { - return false - } - return (from == null || transition.fromScene == from) && (to == null || transition.toScene == to) } @@ -71,32 +66,30 @@ sealed interface TransitionState { /** No transition/animation is currently running. */ data class Idle(override val currentScene: SceneKey) : TransitionState - /** - * There is a transition animating between two scenes. - * - * Important note: [fromScene] and [toScene] might be the same, in which case this [Transition] - * should be treated the same as [Idle]. This is designed on purpose so that a [Transition] can - * be started without knowing in advance where it is transitioning to, making the logic of - * [swipeToScene] easier to reason about. - */ - interface Transition : TransitionState { - /** The scene this transition is starting from. */ - val fromScene: SceneKey + /** There is a transition animating between two scenes. */ + abstract class Transition( + /** The scene this transition is starting from. Can't be the same as toScene */ + val fromScene: SceneKey, - /** The scene this transition is going to. */ + /** The scene this transition is going to. Can't be the same as fromScene */ val toScene: SceneKey + ) : TransitionState { + + init { + check(fromScene != toScene) + } /** * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or * when flinging quickly during a swipe gesture. */ - val progress: Float + abstract val progress: Float /** Whether the transition was triggered by user input rather than being programmatic. */ - val isInitiatedByUserInput: Boolean + abstract val isInitiatedByUserInput: Boolean /** Whether user input is currently driving the transition. */ - val isUserInputOngoing: Boolean + abstract val isUserInputOngoing: Boolean } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt index aa942e039856..e6224df649ca 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt @@ -58,16 +58,22 @@ class SceneGestureHandlerTest { private val layoutState: SceneTransitionLayoutState = SceneTransitionLayoutState(internalCurrentScene) + val mutableUserActionsA: MutableMap<UserAction, SceneKey> = + mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC) + + val mutableUserActionsB: MutableMap<UserAction, SceneKey> = + mutableMapOf(Swipe.Up to SceneC, Swipe.Down to SceneA) + private val scenesBuilder: SceneTransitionLayoutScope.() -> Unit = { scene( key = SceneA, - userActions = mapOf(Swipe.Up to SceneB, Swipe.Down to SceneC), + userActions = mutableUserActionsA, ) { Text("SceneA") } scene( key = SceneB, - userActions = mapOf(Swipe.Up to SceneC, Swipe.Down to SceneA), + userActions = mutableUserActionsB, ) { Text("SceneB") } @@ -117,8 +123,8 @@ class SceneGestureHandlerTest { fun nestedScrollConnection(nestedScrollBehavior: NestedScrollBehavior) = SceneNestedScrollHandler( gestureHandler = sceneGestureHandler, - startBehavior = nestedScrollBehavior, - endBehavior = nestedScrollBehavior, + topOrLeftBehavior = nestedScrollBehavior, + bottomOrRightBehavior = nestedScrollBehavior, ) .connection @@ -412,6 +418,70 @@ class SceneGestureHandlerTest { } @Test + fun onAccelaratedScrollBothTargetsBecomeNull_settlesToIdle() = runGestureTest { + draggable.onDragStarted() + draggable.onDelta(up(0.2f)) + + draggable.onDelta(up(0.2f)) + draggable.onDragStopped(velocity = -velocityThreshold) + assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB) + + mutableUserActionsA.remove(Swipe.Up) + mutableUserActionsA.remove(Swipe.Down) + mutableUserActionsB.remove(Swipe.Up) + mutableUserActionsB.remove(Swipe.Down) + + // start accelaratedScroll and scroll over to B -> null + draggable.onDragStarted() + draggable.onDelta(up(0.5f)) + draggable.onDelta(up(0.5f)) + + // here onDragStopped is already triggered, but subsequent onDelta/onDragStopped calls may + // still be called. Make sure that they don't crash or change the scene + draggable.onDelta(up(0.5f)) + draggable.onDragStopped(0f) + + advanceUntilIdle() + assertIdle(SceneB) + + // These events can still come in after the animation has settled + draggable.onDelta(up(0.5f)) + draggable.onDragStopped(0f) + assertIdle(SceneB) + } + + @Test + fun onDragTargetsChanged_targetStaysTheSame() = runGestureTest { + draggable.onDragStarted(up(0.1f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.1f) + + mutableUserActionsA[Swipe.Up] = SceneC + draggable.onDelta(up(0.1f)) + // target stays B even though UserActions changed + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.2f) + draggable.onDragStopped(down(0.1f)) + advanceUntilIdle() + + // now target changed to C for new drag + draggable.onDragStarted(up(0.1f)) + assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0.1f) + } + + @Test + fun onDragTargetsChanged_targetsChangeWhenStartingNewDrag() = runGestureTest { + draggable.onDragStarted(up(0.1f)) + assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.1f) + + mutableUserActionsA[Swipe.Up] = SceneC + draggable.onDelta(up(0.1f)) + draggable.onDragStopped(down(0.1f)) + + // now target changed to C for new drag that started before previous drag settled to Idle + draggable.onDragStarted(up(0.1f)) + assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0.3f) + } + + @Test fun startGestureDuringAnimatingOffset_shouldImmediatelyStopTheAnimation() = runGestureTest { draggable.onDragStarted() assertTransition(currentScene = SceneA) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index 94c51ca50667..eeda8d46adfa 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -39,18 +39,6 @@ class SceneTransitionLayoutStateTest { } @Test - fun isTransitioningTo_fromSceneEqualToToScene() { - val state = SceneTransitionLayoutState(TestScenes.SceneA) - state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneA) - - assertThat(state.isTransitioning()).isFalse() - assertThat(state.isTransitioning(from = TestScenes.SceneA)).isFalse() - assertThat(state.isTransitioning(to = TestScenes.SceneB)).isFalse() - assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)) - .isFalse() - } - - @Test fun isTransitioningTo_transition() { val state = SceneTransitionLayoutState(TestScenes.SceneA) state.transitionState = transition(from = TestScenes.SceneA, to = TestScenes.SceneB) @@ -64,10 +52,8 @@ class SceneTransitionLayoutStateTest { } private fun transition(from: SceneKey, to: SceneKey): TransitionState.Transition { - return object : TransitionState.Transition { + return object : TransitionState.Transition(from, to) { override val currentScene: SceneKey = from - override val fromScene: SceneKey = from - override val toScene: SceneKey = to override val progress: Float = 0f override val isInitiatedByUserInput: Boolean = false override val isUserInputOngoing: Boolean = false diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt new file mode 100644 index 000000000000..74f50d8c844b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.data.repository + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.settings.FakeSettings +import com.google.common.truth.Truth +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorCorrectionRepositoryImplTest : SysuiTestCase() { + companion object { + val TEST_USER_1 = UserHandle.of(1)!! + val TEST_USER_2 = UserHandle.of(2)!! + } + + private val testDispatcher = StandardTestDispatcher() + private val scope = TestScope(testDispatcher) + private val settings: FakeSettings = FakeSettings() + + private lateinit var underTest: ColorCorrectionRepository + + @Before + fun setUp() { + underTest = + ColorCorrectionRepositoryImpl( + testDispatcher, + settings, + ) + } + + @Test + fun isEnabled_initiallyGetsSettingsValue() = + scope.runTest { + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + 1, + TEST_USER_1.identifier + ) + + underTest = + ColorCorrectionRepositoryImpl( + testDispatcher, + settings, + ) + + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + runCurrent() + + val actualValue: Boolean = underTest.isEnabled(TEST_USER_1).first() + Truth.assertThat(actualValue).isTrue() + } + + @Test + fun isEnabled_settingUpdated_valueUpdated() = + scope.runTest { + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.DISABLED, + TEST_USER_1.identifier + ) + runCurrent() + Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.ENABLED, + TEST_USER_1.identifier + ) + runCurrent() + Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isTrue() + + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.DISABLED, + TEST_USER_1.identifier + ) + runCurrent() + Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + } + + @Test + fun isEnabled_settingForUserOneOnly_valueUpdatedForUserOneOnly() = + scope.runTest { + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.DISABLED, + TEST_USER_1.identifier + ) + underTest.isEnabled(TEST_USER_2).launchIn(backgroundScope) + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.DISABLED, + TEST_USER_2.identifier + ) + + runCurrent() + Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + Truth.assertThat(underTest.isEnabled(TEST_USER_2).first()).isFalse() + + settings.putIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + ColorCorrectionRepositoryImpl.ENABLED, + TEST_USER_1.identifier + ) + runCurrent() + Truth.assertThat(underTest.isEnabled(TEST_USER_1).first()).isTrue() + Truth.assertThat(underTest.isEnabled(TEST_USER_2).first()).isFalse() + } + + @Test + fun setEnabled() = + scope.runTest { + val success = underTest.setIsEnabled(true, TEST_USER_1) + runCurrent() + Truth.assertThat(success).isTrue() + + val actualValue = + settings.getIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + TEST_USER_1.identifier + ) + Truth.assertThat(actualValue).isEqualTo(ColorCorrectionRepositoryImpl.ENABLED) + } + + @Test + fun setDisabled() = + scope.runTest { + val success = underTest.setIsEnabled(false, TEST_USER_1) + runCurrent() + Truth.assertThat(success).isTrue() + + val actualValue = + settings.getIntForUser( + ColorCorrectionRepositoryImpl.SETTING_NAME, + TEST_USER_1.identifier + ) + Truth.assertThat(actualValue).isEqualTo(ColorCorrectionRepositoryImpl.DISABLED) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index 16cfa2398fd5..1f8e29adc983 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -161,7 +161,7 @@ class CommunalInteractorTest : SysuiTestCase() { whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java)) val targets = listOf(target1, target2, target3) - smartspaceRepository.setLockscreenSmartspaceTargets(targets) + smartspaceRepository.setCommunalSmartspaceTargets(targets) val smartspaceContent by collectLastValue(underTest.smartspaceContent) assertThat(smartspaceContent?.size).isEqualTo(1) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt index 8896e6e64bd9..314dfdfd6f2a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt @@ -116,7 +116,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { whenever(target.smartspaceTargetId).thenReturn("target") whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER) whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java)) - smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target)) + smartspaceRepository.setCommunalSmartspaceTargets(listOf(target)) // Media playing. mediaRepository.mediaPlaying.value = true diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 7fbcae0d8986..8a71168324aa 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -135,7 +135,7 @@ class CommunalViewModelTest : SysuiTestCase() { whenever(target.smartspaceTargetId).thenReturn("target") whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER) whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java)) - smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target)) + smartspaceRepository.setCommunalSmartspaceTargets(listOf(target)) // Media playing. mediaRepository.mediaPlaying.value = true diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index 45aca175657e..d6d5b23a311d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -827,7 +827,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun isAuthenticatedIsResetToFalseWhenKeyguardIsGoingAway() = + fun isAuthenticatedIsResetToFalseWhenDeviceStartsGoingToSleep() = testScope.runTest { initCollectors() allPreconditionsToRunFaceAuthAreTrue() @@ -840,13 +840,13 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(authenticated()).isTrue() - keyguardRepository.setKeyguardGoingAway(true) + powerInteractor.setAsleepForTest() assertThat(authenticated()).isFalse() } @Test - fun isAuthenticatedIsResetToFalseWhenDeviceStartsGoingToSleep() = + fun isAuthenticatedIsResetToFalseWhenDeviceGoesToSleep() = testScope.runTest { initCollectors() allPreconditionsToRunFaceAuthAreTrue() @@ -865,7 +865,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } @Test - fun isAuthenticatedIsResetToFalseWhenDeviceGoesToSleep() = + fun isAuthenticatedIsResetToFalseWhenUserIsSwitching() = testScope.runTest { initCollectors() allPreconditionsToRunFaceAuthAreTrue() @@ -878,13 +878,16 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(authenticated()).isTrue() - powerInteractor.setAsleepForTest() + fakeUserRepository.setSelectedUserInfo( + primaryUser, + SelectionStatus.SELECTION_IN_PROGRESS + ) assertThat(authenticated()).isFalse() } @Test - fun isAuthenticatedIsResetToFalseWhenUserIsSwitching() = + fun isAuthenticatedIsResetToFalseWhenKeyguardDoneAnimationsFinished() = testScope.runTest { initCollectors() allPreconditionsToRunFaceAuthAreTrue() @@ -897,10 +900,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { assertThat(authenticated()).isTrue() - fakeUserRepository.setSelectedUserInfo( - primaryUser, - SelectionStatus.SELECTION_IN_PROGRESS - ) + keyguardRepository.keyguardDoneAnimationsFinished() assertThat(authenticated()).isFalse() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManagerTest.kt new file mode 100644 index 000000000000..e8504563b4ae --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManagerTest.kt @@ -0,0 +1,40 @@ +package com.android.systemui.keyguard.ui.preview + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class KeyguardRemotePreviewManagerTest : SysuiTestCase() { + + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + + @Test + fun onDestroy_clearsReferencesToRenderer() = + testScope.runTest { + val renderer = mock<KeyguardPreviewRenderer>() + val onDestroy: (PreviewLifecycleObserver) -> Unit = {} + + val observer = PreviewLifecycleObserver(this, testDispatcher, renderer, onDestroy) + + // Precondition check. + assertThat(observer.renderer).isNotNull() + assertThat(observer.onDestroy).isNotNull() + + observer.onDestroy() + + // The verification checks renderer/requestDestruction lambda because they-re + // non-singletons which can't leak KeyguardPreviewRenderer. + assertThat(observer.renderer).isNull() + assertThat(observer.onDestroy).isNull() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt new file mode 100644 index 000000000000..30d1822b28da --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.data.restoreprocessors + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.qs.pipeline.data.model.RestoreData +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +@OptIn(ExperimentalCoroutinesApi::class) +class WorkTileRestoreProcessorTest : SysuiTestCase() { + + private val underTest = WorkTileRestoreProcessor() + @Test + fun restoreWithWorkTile_removeTracking() = runTest { + val removeTracking by collectLastValue(underTest.removeTrackingForUser(UserHandle.of(USER))) + runCurrent() + + val restoreData = + RestoreData( + restoredTiles = listOf(TILE_SPEC), + restoredAutoAddedTiles = setOf(TILE_SPEC), + USER, + ) + + underTest.postProcessRestore(restoreData) + + assertThat(removeTracking).isEqualTo(Unit) + } + + @Test + fun restoreWithWorkTile_otherUser_noRemoveTracking() = runTest { + val removeTracking by + collectLastValue(underTest.removeTrackingForUser(UserHandle.of(USER + 1))) + runCurrent() + + val restoreData = + RestoreData( + restoredTiles = listOf(TILE_SPEC), + restoredAutoAddedTiles = setOf(TILE_SPEC), + USER, + ) + + underTest.postProcessRestore(restoreData) + + assertThat(removeTracking).isNull() + } + + @Test + fun restoreWithoutWorkTile_noSignal() = runTest { + val removeTracking by collectLastValue(underTest.removeTrackingForUser(UserHandle.of(USER))) + runCurrent() + + val restoreData = + RestoreData( + restoredTiles = emptyList(), + restoredAutoAddedTiles = emptySet(), + USER, + ) + + underTest.postProcessRestore(restoreData) + + assertThat(removeTracking).isNull() + } + + companion object { + private const val USER = 10 + private val TILE_SPEC = TileSpec.Companion.create("work") + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt index adccc84e494b..c7e7845f206c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt @@ -25,6 +25,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.pipeline.data.model.RestoreData +import com.android.systemui.qs.pipeline.data.model.RestoreProcessor +import com.android.systemui.qs.pipeline.data.model.workTileRestoreProcessor import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking import com.android.systemui.qs.pipeline.shared.TileSpec @@ -32,25 +37,28 @@ import com.android.systemui.qs.tiles.WorkModeTile import com.android.systemui.settings.FakeUserTracker import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class WorkTileAutoAddableTest : SysuiTestCase() { + private val kosmos = Kosmos() + + private val restoreProcessor: RestoreProcessor + get() = kosmos.workTileRestoreProcessor + private lateinit var userTracker: FakeUserTracker private lateinit var underTest: WorkTileAutoAddable @Before fun setup() { - MockitoAnnotations.initMocks(this) - userTracker = FakeUserTracker( _userId = USER_INFO_0.id, @@ -58,7 +66,7 @@ class WorkTileAutoAddableTest : SysuiTestCase() { _userProfiles = listOf(USER_INFO_0) ) - underTest = WorkTileAutoAddable(userTracker) + underTest = WorkTileAutoAddable(userTracker, kosmos.workTileRestoreProcessor) } @Test @@ -114,10 +122,80 @@ class WorkTileAutoAddableTest : SysuiTestCase() { assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Always) } + @Test + fun restoreDataWithWorkTile_noCurrentManagedProfile_triggersRemove() = runTest { + val userId = 0 + val signal by collectLastValue(underTest.autoAddSignal(userId)) + runCurrent() + + val restoreData = createRestoreWithWorkTile(userId) + + restoreProcessor.postProcessRestore(restoreData) + + assertThat(signal!!).isEqualTo(AutoAddSignal.RemoveTracking(SPEC)) + } + + @Test + fun restoreDataWithWorkTile_currentlyManagedProfile_doesntTriggerRemove() = runTest { + userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0) + val userId = 0 + val signals by collectValues(underTest.autoAddSignal(userId)) + runCurrent() + + val restoreData = createRestoreWithWorkTile(userId) + + restoreProcessor.postProcessRestore(restoreData) + + assertThat(signals).doesNotContain(AutoAddSignal.RemoveTracking(SPEC)) + } + + @Test + fun restoreDataWithoutWorkTile_noManagedProfile_doesntTriggerRemove() = runTest { + val userId = 0 + val signals by collectValues(underTest.autoAddSignal(userId)) + runCurrent() + + val restoreData = createRestoreWithoutWorkTile(userId) + + restoreProcessor.postProcessRestore(restoreData) + + assertThat(signals).doesNotContain(AutoAddSignal.RemoveTracking(SPEC)) + } + + @Test + fun restoreDataWithoutWorkTile_managedProfile_doesntTriggerRemove() = runTest { + userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0) + val userId = 0 + val signals by collectValues(underTest.autoAddSignal(userId)) + runCurrent() + + val restoreData = createRestoreWithoutWorkTile(userId) + + restoreProcessor.postProcessRestore(restoreData) + + assertThat(signals).doesNotContain(AutoAddSignal.RemoveTracking(SPEC)) + } + companion object { private val SPEC = TileSpec.create(WorkModeTile.TILE_SPEC) private val USER_INFO_0 = UserInfo(0, "", FLAG_PRIMARY or FLAG_FULL) private val USER_INFO_1 = UserInfo(1, "", FLAG_FULL) private val USER_INFO_WORK = UserInfo(10, "", FLAG_PROFILE or FLAG_MANAGED_PROFILE) + + private fun createRestoreWithWorkTile(userId: Int): RestoreData { + return RestoreData( + listOf(TileSpec.create("a"), SPEC, TileSpec.create("b")), + setOf(SPEC), + userId, + ) + } + + private fun createRestoreWithoutWorkTile(userId: Int): RestoreData { + return RestoreData( + listOf(TileSpec.create("a"), TileSpec.create("b")), + emptySet(), + userId, + ) + } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt index 41a7ec03408d..54b03a90229b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt @@ -183,6 +183,22 @@ class AutoAddInteractorTest : SysuiTestCase() { assertThat(autoAddedTiles).contains(SPEC) } + @Test + fun autoAddable_removeTrackingSignal_notRemovedButUnmarked() = + testScope.runTest { + autoAddRepository.markTileAdded(USER, SPEC) + val autoAddedTiles by collectLastValue(autoAddRepository.autoAddedTiles(USER)) + val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.Always) + + underTest = createInteractor(setOf(fakeAutoAddable)) + + fakeAutoAddable.sendRemoveTrackingSignal(USER) + runCurrent() + + verify(currentTilesInteractor, never()).removeTiles(any()) + assertThat(autoAddedTiles).doesNotContain(SPEC) + } + private fun createInteractor(autoAddables: Set<AutoAddable>): AutoAddInteractor { return AutoAddInteractor( autoAddables, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt index f73cab8a10a3..b2a9783d2e60 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt @@ -5,10 +5,15 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.qs.pipeline.data.model.RestoreData +import com.android.systemui.qs.pipeline.data.model.RestoreProcessor import com.android.systemui.qs.pipeline.data.repository.FakeAutoAddRepository import com.android.systemui.qs.pipeline.data.repository.FakeQSSettingsRestoredRepository import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter +import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractorTest.TestableRestoreProcessor.Companion.POSTPROCESS +import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractorTest.TestableRestoreProcessor.Companion.PREPROCESS +import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger +import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope @@ -17,7 +22,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.MockitoAnnotations +import org.mockito.Mockito.inOrder @RunWith(AndroidJUnit4::class) @SmallTest @@ -28,6 +33,9 @@ class RestoreReconciliationInteractorTest : SysuiTestCase() { private val qsSettingsRestoredRepository = FakeQSSettingsRestoredRepository() + private val restoreProcessor: TestableRestoreProcessor = TestableRestoreProcessor() + private val qsLogger: QSPipelineLogger = mock() + private lateinit var underTest: RestoreReconciliationInteractor private val testDispatcher = StandardTestDispatcher() @@ -35,13 +43,13 @@ class RestoreReconciliationInteractorTest : SysuiTestCase() { @Before fun setUp() { - MockitoAnnotations.initMocks(this) - underTest = RestoreReconciliationInteractor( tileSpecRepository, autoAddRepository, qsSettingsRestoredRepository, + setOf(restoreProcessor), + qsLogger, testScope.backgroundScope, testDispatcher ) @@ -85,6 +93,44 @@ class RestoreReconciliationInteractorTest : SysuiTestCase() { assertThat(autoAdd).isEqualTo(expectedAutoAdd.toTilesSet()) } + @Test + fun restoreProcessorsCalled() = + testScope.runTest { + val user = 10 + + val restoredSpecs = "a,c,d,f" + val restoredAutoAdded = "d,e" + + val restoreData = + RestoreData( + restoredSpecs.toTilesList(), + restoredAutoAdded.toTilesSet(), + user, + ) + + qsSettingsRestoredRepository.onDataRestored(restoreData) + runCurrent() + + assertThat(restoreProcessor.calls).containsExactly(PREPROCESS, POSTPROCESS).inOrder() + } + + private class TestableRestoreProcessor : RestoreProcessor { + val calls = mutableListOf<Any>() + + override suspend fun preProcessRestore(restoreData: RestoreData) { + calls.add(PREPROCESS) + } + + override suspend fun postProcessRestore(restoreData: RestoreData) { + calls.add(POSTPROCESS) + } + + companion object { + val PREPROCESS = Any() + val POSTPROCESS = Any() + } + } + companion object { private fun String.toTilesList() = TilesSettingConverter.toTilesList(this) private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt new file mode 100644 index 000000000000..96d57743e2ee --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.domain.interactor + +import android.content.pm.UserInfo +import android.os.UserManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.FakeQSFactory +import com.android.systemui.qs.pipeline.data.model.RestoreData +import com.android.systemui.qs.pipeline.data.repository.fakeRestoreRepository +import com.android.systemui.qs.pipeline.data.repository.fakeTileSpecRepository +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.qsTileFactory +import com.android.systemui.settings.fakeUserTracker +import com.android.systemui.settings.userTracker +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** + * This integration test is for testing the solution to b/314781280. In particular, there are two + * issues we want to verify after a restore of a device with a work profile and a work mode tile: + * * When the work profile is re-enabled in the target device, it is auto-added. + * * The tile is auto-added in the same position that it was in the restored device. + */ +@MediumTest +@RunWith(AndroidJUnit4::class) +@OptIn(ExperimentalCoroutinesApi::class) +class WorkProfileAutoAddedAfterRestoreTest : SysuiTestCase() { + + private val kosmos = Kosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) } + // Getter here so it can change when there is a managed profile. + private val workTileAvailable: Boolean + get() = hasManagedProfile() + private val currentUser: Int + get() = kosmos.userTracker.userId + + private val testScope: TestScope + get() = kosmos.testScope + + @Before + fun setUp() { + mSetFlagsRule.enableFlags(FLAG_QS_NEW_PIPELINE) + + kosmos.qsTileFactory = FakeQSFactory(::tileCreator) + kosmos.restoreReconciliationInteractor.start() + kosmos.autoAddInteractor.init(kosmos.currentTilesInteractor) + } + + @Test + fun workTileRestoredAndPreviouslyAutoAdded_notAvailable_willBeAutoaddedInCorrectPosition() = + testScope.runTest { + val tiles by collectLastValue(kosmos.currentTilesInteractor.currentTiles) + + // Set up + val currentTiles = listOf("a".toTileSpec()) + kosmos.fakeTileSpecRepository.setTiles(currentUser, currentTiles) + + val restoredTiles = + listOf(WORK_TILE_SPEC) + listOf("b", "c", "d").map { it.toTileSpec() } + val restoredAutoAdded = setOf(WORK_TILE_SPEC) + + val restoreData = RestoreData(restoredTiles, restoredAutoAdded, currentUser) + + // WHEN we restore tiles that auto-added the WORK tile and it's not available (there + // are no managed profiles) + kosmos.fakeRestoreRepository.onDataRestored(restoreData) + + // THEN the work tile is not part of the current tiles + assertThat(tiles!!).hasSize(3) + assertThat(tiles!!.map { it.spec }).doesNotContain(WORK_TILE_SPEC) + + // WHEN we add a work profile + createManagedProfileAndAdd() + + // THEN the work profile is added in the correct place + assertThat(tiles!!.first().spec).isEqualTo(WORK_TILE_SPEC) + } + + @Test + fun workTileNotRestoredAndPreviouslyAutoAdded_wontBeAutoAddedWhenWorkProfileIsAdded() = + testScope.runTest { + val tiles by collectLastValue(kosmos.currentTilesInteractor.currentTiles) + + // Set up + val currentTiles = listOf("a".toTileSpec()) + kosmos.fakeTileSpecRepository.setTiles(currentUser, currentTiles) + runCurrent() + + val restoredTiles = listOf("b", "c", "d").map { it.toTileSpec() } + val restoredAutoAdded = setOf(WORK_TILE_SPEC) + + val restoreData = RestoreData(restoredTiles, restoredAutoAdded, currentUser) + + // WHEN we restore tiles that auto-added the WORK tile + kosmos.fakeRestoreRepository.onDataRestored(restoreData) + + // THEN the work tile is not part of the current tiles + assertThat(tiles!!).hasSize(3) + assertThat(tiles!!.map { it.spec }).doesNotContain(WORK_TILE_SPEC) + + // WHEN we add a work profile + createManagedProfileAndAdd() + + // THEN the work profile is not added because the user had manually removed it in the + // past + assertThat(tiles!!.map { it.spec }).doesNotContain(WORK_TILE_SPEC) + } + + private fun tileCreator(spec: String): QSTile { + return if (spec == WORK_TILE_SPEC.spec) { + FakeQSTile(currentUser, workTileAvailable) + } else { + FakeQSTile(currentUser) + } + } + + private fun hasManagedProfile(): Boolean { + return kosmos.userTracker.userProfiles.any { it.isManagedProfile } + } + + private fun TestScope.createManagedProfileAndAdd() { + kosmos.fakeUserTracker.set( + listOf(USER_0_INFO, MANAGED_USER_INFO), + 0, + ) + runCurrent() + } + + private companion object { + val WORK_TILE_SPEC = "work".toTileSpec() + val USER_0_INFO = + UserInfo( + 0, + "zero", + "", + UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL, + ) + val MANAGED_USER_INFO = + UserInfo( + 10, + "ten-managed", + "", + 0, + UserManager.USER_TYPE_PROFILE_MANAGED, + ) + + fun String.toTileSpec() = TileSpec.create(this) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt index 2b744ac8398a..00405d0a07e7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.alarm.domain import android.app.AlarmManager +import android.graphics.drawable.TestStubDrawable import android.widget.Switch import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -40,7 +41,14 @@ class AlarmTileMapperTest : SysuiTestCase() { private val kosmos = Kosmos() private val alarmTileConfig = kosmos.qsAlarmTileConfig // Using lazy (versus =) to make sure we override the right context -- see b/311612168 - private val mapper by lazy { AlarmTileMapper(context.orCreateTestableResources.resources) } + private val mapper by lazy { + AlarmTileMapper( + context.orCreateTestableResources + .apply { addOverride(R.drawable.ic_alarm, TestStubDrawable()) } + .resources, + context.theme + ) + } @Test fun notAlarmSet() { @@ -100,7 +108,7 @@ class AlarmTileMapperTest : SysuiTestCase() { ): QSTileState { val label = context.getString(R.string.status_bar_alarm) return QSTileState( - { Icon.Resource(R.drawable.ic_alarm, null) }, + { Icon.Loaded(context.getDrawable(R.drawable.ic_alarm)!!, null) }, label, activationState, secondaryLabel, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt new file mode 100644 index 000000000000..8ee6d2005350 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.colorcorrection.domain + +import android.graphics.drawable.TestStubDrawable +import android.widget.Switch +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.android.systemui.qs.tiles.impl.colorcorrection.qsColorCorrectionTileConfig +import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorCorrectionTileMapperTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val colorCorrectionTileConfig = kosmos.qsColorCorrectionTileConfig + private val subtitleArray = + context.resources.getStringArray(R.array.tile_states_color_correction) + // Using lazy (versus =) to make sure we override the right context -- see b/311612168 + private val mapper by lazy { + ColorCorrectionTileMapper( + context.orCreateTestableResources + .apply { addOverride(R.drawable.ic_qs_color_correction, TestStubDrawable()) } + .resources, + context.theme + ) + } + + @Test + fun disabledModel() { + val inputModel = ColorCorrectionTileModel(false) + + val outputState = mapper.map(colorCorrectionTileConfig, inputModel) + + val expectedState = + createColorCorrectionTileState(QSTileState.ActivationState.INACTIVE, subtitleArray[1]) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun enabledModel() { + val inputModel = ColorCorrectionTileModel(true) + + val outputState = mapper.map(colorCorrectionTileConfig, inputModel) + + val expectedState = + createColorCorrectionTileState(QSTileState.ActivationState.ACTIVE, subtitleArray[2]) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + private fun createColorCorrectionTileState( + activationState: QSTileState.ActivationState, + secondaryLabel: String + ): QSTileState { + val label = context.getString(R.string.quick_settings_color_correction_label) + return QSTileState( + { Icon.Loaded(context.getDrawable(R.drawable.ic_qs_color_correction)!!, null) }, + label, + activationState, + secondaryLabel, + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), + label, + null, + QSTileState.SideViewIcon.None, + QSTileState.EnabledState.ENABLED, + Switch::class.qualifiedName + ) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt new file mode 100644 index 000000000000..8c612acad887 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository +import com.android.systemui.coroutines.collectValues +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.toCollection +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorCorrectionTileDataInteractorTest : SysuiTestCase() { + + private val colorCorrectionRepository = FakeColorCorrectionRepository() + private val underTest: ColorCorrectionTileDataInteractor = + ColorCorrectionTileDataInteractor(colorCorrectionRepository) + + @Test + fun alwaysAvailable() = runTest { + val availability = underTest.availability(TEST_USER).toCollection(mutableListOf()) + + assertThat(availability).hasSize(1) + assertThat(availability.last()).isTrue() + } + + @Test + fun dataMatchesTheRepository() = runTest { + val dataList: List<ColorCorrectionTileModel> by + collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))) + runCurrent() + + colorCorrectionRepository.setIsEnabled(true, TEST_USER) + runCurrent() + + colorCorrectionRepository.setIsEnabled(false, TEST_USER) + runCurrent() + + assertThat(dataList).hasSize(3) + assertThat(dataList.map { it.isEnabled }).isEqualTo(listOf(false, true, false)) + } + + private companion object { + val TEST_USER = UserHandle.of(1)!! + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt new file mode 100644 index 000000000000..3049cc079a1c --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor + +import android.os.UserHandle +import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorCorrectionTileUserActionInteractorTest : SysuiTestCase() { + + private val testUser = UserHandle.CURRENT + private val repository = FakeColorCorrectionRepository() + private val inputHandler = FakeQSTileIntentUserInputHandler() + + private val underTest = + ColorCorrectionUserActionInteractor( + repository, + inputHandler, + ) + + @Test + fun handleClickWhenEnabled() = runTest { + val wasEnabled = true + repository.setIsEnabled(wasEnabled, testUser) + + underTest.handleInput(QSTileInputTestKtx.click(ColorCorrectionTileModel(wasEnabled))) + + assertThat(repository.isEnabled(testUser).value).isEqualTo(!wasEnabled) + } + + @Test + fun handleClickWhenDisabled() = runTest { + val wasEnabled = false + repository.setIsEnabled(wasEnabled, testUser) + + underTest.handleInput(QSTileInputTestKtx.click(ColorCorrectionTileModel(wasEnabled))) + + assertThat(repository.isEnabled(testUser).value).isEqualTo(!wasEnabled) + } + + @Test + fun handleLongClickWhenDisabled() = runTest { + val enabled = false + + underTest.handleInput(QSTileInputTestKtx.longClick(ColorCorrectionTileModel(enabled))) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_CORRECTION_SETTINGS) + } + } + + @Test + fun handleLongClickWhenEnabled() = runTest { + val enabled = true + + underTest.handleInput(QSTileInputTestKtx.longClick(ColorCorrectionTileModel(enabled))) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_CORRECTION_SETTINGS) + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt index 7b2ac90b9766..b60f483cccbb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.impl.flashlight.domain +import android.graphics.drawable.TestStubDrawable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -35,7 +36,17 @@ import org.junit.runner.RunWith class FlashlightMapperTest : SysuiTestCase() { private val kosmos = Kosmos() private val qsTileConfig = kosmos.qsFlashlightTileConfig - private val mapper by lazy { FlashlightMapper(context.orCreateTestableResources.resources) } + private val mapper by lazy { + FlashlightMapper( + context.orCreateTestableResources + .apply { + addOverride(R.drawable.qs_flashlight_icon_off, TestStubDrawable()) + addOverride(R.drawable.qs_flashlight_icon_on, TestStubDrawable()) + } + .resources, + context.theme + ) + } @Test fun mapsDisabledDataToInactiveState() { @@ -56,20 +67,20 @@ class FlashlightMapperTest : SysuiTestCase() { @Test fun mapsEnabledDataToOnIconState() { - val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_on, null) - val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(true)) + val expectedIcon = + Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_on)!!, null) val actualIcon = tileState.icon() assertThat(actualIcon).isEqualTo(expectedIcon) } @Test fun mapsDisabledDataToOffIconState() { - val expectedIcon = Icon.Resource(R.drawable.qs_flashlight_icon_off, null) - val tileState: QSTileState = mapper.map(qsTileConfig, FlashlightTileModel(false)) + val expectedIcon = + Icon.Loaded(context.getDrawable(R.drawable.qs_flashlight_icon_off)!!, null) val actualIcon = tileState.icon() assertThat(actualIcon).isEqualTo(expectedIcon) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt index 8791877f8863..ea74a4c0d398 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.impl.location.domain +import android.graphics.drawable.TestStubDrawable import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -36,7 +37,17 @@ class LocationTileMapperTest : SysuiTestCase() { private val kosmos = Kosmos() private val qsTileConfig = kosmos.qsLocationTileConfig - private val mapper by lazy { LocationTileMapper(context.orCreateTestableResources.resources) } + private val mapper by lazy { + LocationTileMapper( + context.orCreateTestableResources + .apply { + addOverride(R.drawable.qs_location_icon_off, TestStubDrawable()) + addOverride(R.drawable.qs_location_icon_on, TestStubDrawable()) + } + .resources, + context.theme + ) + } @Test fun mapsDisabledDataToInactiveState() { @@ -56,20 +67,18 @@ class LocationTileMapperTest : SysuiTestCase() { @Test fun mapsEnabledDataToOnIconState() { - val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_on, null) - val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(true)) + val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_on)!!, null) val actualIcon = tileState.icon() Truth.assertThat(actualIcon).isEqualTo(expectedIcon) } @Test fun mapsDisabledDataToOffIconState() { - val expectedIcon = Icon.Resource(R.drawable.qs_location_icon_off, null) - val tileState: QSTileState = mapper.map(qsTileConfig, LocationTileModel(false)) + val expectedIcon = Icon.Loaded(context.getDrawable(R.drawable.qs_location_icon_off)!!, null) val actualIcon = tileState.icon() Truth.assertThat(actualIcon).isEqualTo(expectedIcon) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt index d1824129590b..d162c778f607 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.impl.saver.domain +import android.graphics.drawable.TestStubDrawable import android.widget.Switch import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -37,7 +38,17 @@ class DataSaverTileMapperTest : SysuiTestCase() { private val dataSaverTileConfig = kosmos.qsDataSaverTileConfig // Using lazy (versus =) to make sure we override the right context -- see b/311612168 - private val mapper by lazy { DataSaverTileMapper(context.orCreateTestableResources.resources) } + private val mapper by lazy { + DataSaverTileMapper( + context.orCreateTestableResources + .apply { + addOverride(R.drawable.qs_data_saver_icon_off, TestStubDrawable()) + addOverride(R.drawable.qs_data_saver_icon_on, TestStubDrawable()) + } + .resources, + context.theme + ) + } @Test fun activeStateMatchesEnabledModel() { @@ -80,7 +91,7 @@ class DataSaverTileMapperTest : SysuiTestCase() { else context.resources.getStringArray(R.array.tile_states_saver)[0] return QSTileState( - { Icon.Resource(iconRes, null) }, + { Icon.Loaded(context.getDrawable(iconRes)!!, null) }, label, activationState, secondaryLabel, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt index 87f50090e58b..a9776068b20c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.uimodenight.domain import android.app.UiModeManager +import android.graphics.drawable.TestStubDrawable import android.text.TextUtils import android.view.View import android.widget.Switch @@ -41,7 +42,15 @@ class UiModeNightTileMapperTest : SysuiTestCase() { private val qsTileConfig = kosmos.qsUiModeNightTileConfig private val mapper by lazy { - UiModeNightTileMapper(context.orCreateTestableResources.resources) + UiModeNightTileMapper( + context.orCreateTestableResources + .apply { + addOverride(R.drawable.qs_light_dark_theme_icon_off, TestStubDrawable()) + addOverride(R.drawable.qs_light_dark_theme_icon_on, TestStubDrawable()) + } + .resources, + context.theme + ) } private fun createUiNightModeTileState( @@ -60,7 +69,7 @@ class UiModeNightTileMapperTest : SysuiTestCase() { expandedAccessibilityClass: KClass<out View>? = Switch::class, ): QSTileState { return QSTileState( - { Icon.Resource(iconRes, null) }, + { Icon.Loaded(context.getDrawable(iconRes)!!, null) }, label, activationState, secondaryLabel, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt new file mode 100644 index 000000000000..ef2046d85a14 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.smartspace + +import android.app.smartspace.SmartspaceManager +import android.app.smartspace.SmartspaceSession +import android.app.smartspace.SmartspaceTarget +import android.content.Context +import android.graphics.drawable.Drawable +import android.testing.TestableLooper +import android.view.View +import android.widget.FrameLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.smartspace.CommunalSmartspaceController +import com.android.systemui.plugins.BcSmartspaceConfigPlugin +import com.android.systemui.plugins.BcSmartspaceDataPlugin +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.util.concurrency.Execution +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import java.util.Optional +import java.util.concurrent.Executor +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper +class CommunalSmartspaceControllerTest : SysuiTestCase() { + @Mock private lateinit var smartspaceManager: SmartspaceManager + + @Mock private lateinit var execution: Execution + + @Mock private lateinit var uiExecutor: Executor + + @Mock private lateinit var targetFilter: SmartspaceTargetFilter + + @Mock private lateinit var plugin: BcSmartspaceDataPlugin + + @Mock private lateinit var precondition: SmartspacePrecondition + + @Mock private lateinit var listener: BcSmartspaceDataPlugin.SmartspaceTargetListener + + @Mock private lateinit var session: SmartspaceSession + + private lateinit var controller: CommunalSmartspaceController + + // TODO(b/272811280): Remove usage of real view + private val fakeParent = FrameLayout(context) + + /** + * A class which implements SmartspaceView and extends View. This is mocked to provide the right + * object inheritance and interface implementation used in CommunalSmartspaceController + */ + private class TestView(context: Context?) : View(context), SmartspaceView { + override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {} + + override fun registerConfigProvider(plugin: BcSmartspaceConfigPlugin?) {} + + override fun setPrimaryTextColor(color: Int) {} + + override fun setUiSurface(uiSurface: String) {} + + override fun setDozeAmount(amount: Float) {} + + override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {} + + override fun setFalsingManager(falsingManager: FalsingManager?) {} + + override fun setDnd(image: Drawable?, description: String?) {} + + override fun setNextAlarm(image: Drawable?, description: String?) {} + + override fun setMediaTarget(target: SmartspaceTarget?) {} + + override fun getSelectedPage(): Int { + return 0 + } + + override fun getCurrentCardTopPadding(): Int { + return 0 + } + } + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(session) + + controller = + CommunalSmartspaceController( + context, + smartspaceManager, + execution, + uiExecutor, + precondition, + Optional.of(targetFilter), + Optional.of(plugin) + ) + } + + /** Ensures smartspace session begins on a listener only flow. */ + @Test + fun testConnectOnListen() { + `when`(precondition.conditionsMet()).thenReturn(true) + controller.addListener(listener) + + verify(smartspaceManager).createSmartspaceSession(any()) + + var targetListener = + withArgCaptor<SmartspaceSession.OnTargetsAvailableListener> { + verify(session).addOnTargetsAvailableListener(any(), capture()) + } + + `when`(targetFilter.filterSmartspaceTarget(any())).thenReturn(true) + + var target = Mockito.mock(SmartspaceTarget::class.java) + targetListener.onTargetsAvailable(listOf(target)) + + var targets = + withArgCaptor<List<SmartspaceTarget>> { verify(plugin).onTargetsAvailable(capture()) } + + assertThat(targets.contains(target)).isTrue() + + controller.removeListener(listener) + + verify(session).close() + } + + /** + * Ensures session is closed and weather plugin unregisters the notifier when weather smartspace + * view is detached. + */ + @Test + fun testDisconnect_emitsEmptyListAndRemovesNotifier() { + `when`(precondition.conditionsMet()).thenReturn(true) + controller.addListener(listener) + + verify(smartspaceManager).createSmartspaceSession(any()) + + controller.removeListener(listener) + + verify(session).close() + + // And the listener receives an empty list of targets and unregisters the notifier + verify(plugin).onTargetsAvailable(emptyList()) + verify(plugin).registerSmartspaceEventNotifier(null) + } +} diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index 64c0f99f4ba7..c99cb39f91bf 100644 --- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -44,6 +44,7 @@ public interface BcSmartspaceDataPlugin extends Plugin { String UI_SURFACE_HOME_SCREEN = "home"; String UI_SURFACE_MEDIA = "media_data_manager"; String UI_SURFACE_DREAM = "dream"; + String UI_SURFACE_GLANCEABLE_HUB = "glanceable_hub"; String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA"; int VERSION = 1; diff --git a/packages/SystemUI/res-keyguard/drawable/bouncer_password_text_view_focused_background.xml b/packages/SystemUI/res-keyguard/drawable/bouncer_password_text_view_focused_background.xml deleted file mode 100644 index 02e10cd4ad7c..000000000000 --- a/packages/SystemUI/res-keyguard/drawable/bouncer_password_text_view_focused_background.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2023 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. ---> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_focused="true"> - <shape android:shape="rectangle"> - <corners android:radius="16dp" /> - <stroke android:width="3dp" - android:color="@color/bouncer_password_focus_color" /> - </shape> - </item> -</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml index 0b35559148af..66c54f2a668e 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml @@ -23,7 +23,7 @@ android:layout_marginTop="@dimen/keyguard_lock_padding" android:importantForAccessibility="no" android:ellipsize="marquee" - android:focusable="false" + android:focusable="true" android:gravity="center" android:singleLine="true" /> </merge> diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 6e6709f94abb..88f7bcd5d907 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -76,7 +76,6 @@ </style> <style name="Widget.TextView.Password" parent="@android:style/Widget.TextView"> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> - <item name="android:background">@drawable/bouncer_password_text_view_focused_background</item> <item name="android:gravity">center</item> <item name="android:textColor">?android:attr/textColorPrimary</item> </style> diff --git a/packages/SystemUI/res/drawable/arrow_pointing_down.xml b/packages/SystemUI/res/drawable/arrow_pointing_down.xml new file mode 100644 index 000000000000..be39683cd78d --- /dev/null +++ b/packages/SystemUI/res/drawable/arrow_pointing_down.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2023 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/record_issue_dialog.xml b/packages/SystemUI/res/layout/record_issue_dialog.xml new file mode 100644 index 000000000000..53ad9f157a2e --- /dev/null +++ b/packages/SystemUI/res/layout/record_issue_dialog.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:orientation="vertical" > + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:textAppearance="@style/TextAppearance.Dialog.Body.Message" + android:text="@string/qs_record_issue_dropdown_header" /> + + <Button + android:id="@+id/issue_type_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/qs_record_issue_dropdown_prompt" + android:lines="1" + android:drawableRight="@drawable/arrow_pointing_down" + android:layout_marginTop="@dimen/qqs_layout_margin_top" + android:focusable="false" + android:clickable="true" /> + + <!-- Screen Record Switch --> + <LinearLayout + android:id="@+id/screenrecord_switch_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/qqs_layout_margin_top" + android:orientation="horizontal"> + + <ImageView + android:layout_width="@dimen/screenrecord_option_icon_size" + android:layout_height="@dimen/screenrecord_option_icon_size" + android:layout_weight="0" + android:src="@drawable/ic_screenrecord" + app:tint="?androidprv:attr/materialColorOnSurface" + android:layout_gravity="center" + android:layout_marginEnd="@dimen/screenrecord_option_padding" /> + + <TextView + android:layout_width="0dp" + android:layout_height="wrap_content" + android:minHeight="@dimen/screenrecord_option_icon_size" + android:layout_weight="1" + android:layout_gravity="fill_vertical" + android:gravity="center" + android:text="@string/quick_settings_screen_record_label" + android:textAppearance="@style/TextAppearance.Dialog.Body.Message" + android:importantForAccessibility="no"/> + + <Switch + android:id="@+id/screenrecord_switch" + android:layout_width="wrap_content" + android:minHeight="@dimen/screenrecord_option_icon_size" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_gravity="fill_vertical" + android:layout_weight="0" + android:contentDescription="@string/quick_settings_screen_record_label" /> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index a22fd18fc2c0..bcc3c83b4560 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -93,9 +93,6 @@ <color name="qs_user_switcher_selected_avatar_icon_color">#202124</color> <!-- Color of background circle of user avatars in quick settings user switcher --> <color name="qs_user_switcher_avatar_background">#3C4043</color> - <!-- Color of border for keyguard password input when focused --> - <color name="bouncer_password_focus_color">@*android:color/system_secondary_dark</color> - <!-- Accessibility floating menu --> <color name="accessibility_floating_menu_background">#B3000000</color> <!-- 70% --> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 462fc95b8cd1..5f6a39a91b8b 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -56,8 +56,6 @@ <color name="kg_user_switcher_restricted_avatar_icon_color">@color/GM2_grey_600</color> <!-- Color of background circle of user avatars in keyguard user switcher --> <color name="user_avatar_color_bg">?android:attr/colorBackgroundFloating</color> - <!-- Color of border for keyguard password input when focused --> - <color name="bouncer_password_focus_color">@*android:color/system_secondary_light</color> <!-- Icon color for user avatars in user switcher quick settings --> <color name="qs_user_switcher_avatar_icon_color">#3C4043</color> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 78b701caa3b6..e10925d551e2 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -831,6 +831,20 @@ <!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] --> <string name="qs_record_issue_stop">Stop</string> + <!-- QuickSettings: Issue Type Drop down options in Record Issue Start Dialog [CHAR LIMIT=50] --> + <string name="qs_record_issue_dropdown_header">What part of your device experience was affected?</string> + <!-- QuickSettings: Issue Type Drop down prompt in Record Issue Start Dialog [CHAR LIMIT=30] --> + <string name="qs_record_issue_dropdown_prompt">Select issue type</string> + <!-- QuickSettings: Screen record switch label in Record Issue Start Dialog [CHAR LIMIT=20] --> + <string name="qs_record_issue_dropdown_screenrecord">Screen record</string> + + <!-- QuickSettings: Issue Type Drop down choices list in Record Issue Start Dialog [CHAR LIMIT=30] --> + <string-array name="qs_record_issue_types"> + <item>Performance</item> + <item>User Interface</item> + <item>Battery</item> + </string-array> + <!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] --> <string name="quick_settings_onehanded_label">One-handed mode</string> @@ -1066,9 +1080,11 @@ <!-- Description for the button that opens the widget editor on click. [CHAR LIMIT=50] --> <string name="button_to_open_widget_editor">Open the widget editor</string> <!-- Description for the button that removes a widget on click. [CHAR LIMIT=50] --> - <string name="button_to_remove_widget">Remove a widget</string> + <string name="button_to_remove_widget">Remove</string> <!-- Text for the button that launches the hub mode widget picker. [CHAR LIMIT=50] --> - <string name="hub_mode_add_widget_button_text">Add Widget</string> + <string name="hub_mode_add_widget_button_text">Add widget</string> + <!-- Text for the button that exits the hub mode editing mode. [CHAR LIMIT=50] --> + <string name="hub_mode_editing_exit_button_text">Done</string> <!-- Related to user switcher --><skip/> diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt index 80f70a0cd2f2..30648291366a 100644 --- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt +++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricModalities.kt @@ -37,6 +37,10 @@ data class BiometricModalities( val hasSfps: Boolean get() = hasFingerprint && fingerprintProperties!!.isAnySidefpsType + /** If UDFPS authentication is available. */ + val hasUdfps: Boolean + get() = hasFingerprint && fingerprintProperties!!.isAnyUdfpsType + /** If fingerprint authentication is available (and [faceProperties] is non-null). */ val hasFace: Boolean get() = faceProperties != null diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java index df7182b90f12..0169f59a5a20 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java @@ -49,6 +49,7 @@ public final class InteractionJankMonitorWrapper { public static final int CUJ_APP_SWIPE_TO_RECENTS = Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS; public static final int CUJ_OPEN_SEARCH_RESULT = Cuj.CUJ_LAUNCHER_OPEN_SEARCH_RESULT; public static final int CUJ_LAUNCHER_UNFOLD_ANIM = Cuj.CUJ_LAUNCHER_UNFOLD_ANIM; + public static final int CUJ_SEARCH_QSB_OPEN = Cuj.CUJ_LAUNCHER_SEARCH_QSB_OPEN; @IntDef({ CUJ_APP_LAUNCH_FROM_RECENTS, @@ -66,6 +67,7 @@ public final class InteractionJankMonitorWrapper { CUJ_CLOSE_ALL_APPS_TO_HOME, CUJ_OPEN_SEARCH_RESULT, CUJ_LAUNCHER_UNFOLD_ANIM, + CUJ_SEARCH_QSB_OPEN, }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 9764de1993e5..36fe75f69a45 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -168,6 +168,7 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView // Set selected property on so the view can send accessibility events. mPasswordEntry.setSelected(true); + mPasswordEntry.setDefaultFocusHighlightEnabled(false); mOkButton = findViewById(R.id.key_enter); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt index ca859de73a36..24aa11e10f30 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,61 +16,16 @@ package com.android.systemui.accessibility -import com.android.systemui.qs.tileimpl.QSTileImpl -import com.android.systemui.qs.tiles.ColorCorrectionTile -import com.android.systemui.qs.tiles.ColorInversionTile -import com.android.systemui.qs.tiles.DreamTile -import com.android.systemui.qs.tiles.FontScalingTile -import com.android.systemui.qs.tiles.NightDisplayTile -import com.android.systemui.qs.tiles.OneHandedModeTile -import com.android.systemui.qs.tiles.ReduceBrightColorsTile +import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository +import com.android.systemui.accessibility.data.repository.ColorCorrectionRepositoryImpl +import com.android.systemui.accessibility.qs.QSAccessibilityModule import dagger.Binds import dagger.Module -import dagger.multibindings.IntoMap -import dagger.multibindings.StringKey -@Module +@Module(includes = [QSAccessibilityModule::class]) interface AccessibilityModule { - - /** Inject ColorInversionTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(ColorInversionTile.TILE_SPEC) - fun bindColorInversionTile(colorInversionTile: ColorInversionTile): QSTileImpl<*> - - /** Inject NightDisplayTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(NightDisplayTile.TILE_SPEC) - fun bindNightDisplayTile(nightDisplayTile: NightDisplayTile): QSTileImpl<*> - - /** Inject ReduceBrightColorsTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(ReduceBrightColorsTile.TILE_SPEC) - fun bindReduceBrightColorsTile(reduceBrightColorsTile: ReduceBrightColorsTile): QSTileImpl<*> - - /** Inject OneHandedModeTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(OneHandedModeTile.TILE_SPEC) - fun bindOneHandedModeTile(oneHandedModeTile: OneHandedModeTile): QSTileImpl<*> - - /** Inject ColorCorrectionTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(ColorCorrectionTile.TILE_SPEC) - fun bindColorCorrectionTile(colorCorrectionTile: ColorCorrectionTile): QSTileImpl<*> - - /** Inject DreamTile into tileMap in QSModule */ - @Binds - @IntoMap - @StringKey(DreamTile.TILE_SPEC) - fun bindDreamTile(dreamTile: DreamTile): QSTileImpl<*> - - /** Inject FontScalingTile into tileMap in QSModule */ @Binds - @IntoMap - @StringKey(FontScalingTile.TILE_SPEC) - fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*> + abstract fun colorCorrectionRepository( + impl: ColorCorrectionRepositoryImpl + ): ColorCorrectionRepository } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt new file mode 100644 index 000000000000..6483ae44d5ec --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.data.repository + +import android.os.UserHandle +import android.provider.Settings.Secure +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.withContext + +/** Provides data related to color correction. */ +interface ColorCorrectionRepository { + /** Observable for whether color correction is enabled */ + fun isEnabled(userHandle: UserHandle): Flow<Boolean> + + /** Sets color correction enabled state. */ + suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean +} + +@SysUISingleton +class ColorCorrectionRepositoryImpl +@Inject +constructor( + @Background private val bgCoroutineContext: CoroutineContext, + private val secureSettings: SecureSettings, +) : ColorCorrectionRepository { + + companion object { + const val SETTING_NAME = Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED + const val DISABLED = 0 + const val ENABLED = 1 + } + + override fun isEnabled(userHandle: UserHandle): Flow<Boolean> = + secureSettings + .observerFlow(userHandle.identifier, SETTING_NAME) + .onStart { emit(Unit) } + .map { secureSettings.getIntForUser(SETTING_NAME, userHandle.identifier) == ENABLED } + .distinctUntilChanged() + .flowOn(bgCoroutineContext) + + override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean = + withContext(bgCoroutineContext) { + secureSettings.putIntForUser( + SETTING_NAME, + if (isEnabled) ENABLED else DISABLED, + userHandle.identifier + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt new file mode 100644 index 000000000000..df7fdb8e6058 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.qs + +import com.android.systemui.qs.QsEventLogger +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.qs.tiles.ColorCorrectionTile +import com.android.systemui.qs.tiles.ColorInversionTile +import com.android.systemui.qs.tiles.DreamTile +import com.android.systemui.qs.tiles.FontScalingTile +import com.android.systemui.qs.tiles.NightDisplayTile +import com.android.systemui.qs.tiles.OneHandedModeTile +import com.android.systemui.qs.tiles.ReduceBrightColorsTile +import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.ColorCorrectionTileMapper +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionUserActionInteractor +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel +import com.android.systemui.res.R +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.multibindings.IntoMap +import dagger.multibindings.StringKey + +@Module +interface QSAccessibilityModule { + + /** Inject ColorInversionTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(ColorInversionTile.TILE_SPEC) + fun bindColorInversionTile(colorInversionTile: ColorInversionTile): QSTileImpl<*> + + /** Inject NightDisplayTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(NightDisplayTile.TILE_SPEC) + fun bindNightDisplayTile(nightDisplayTile: NightDisplayTile): QSTileImpl<*> + + /** Inject ReduceBrightColorsTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(ReduceBrightColorsTile.TILE_SPEC) + fun bindReduceBrightColorsTile(reduceBrightColorsTile: ReduceBrightColorsTile): QSTileImpl<*> + + /** Inject OneHandedModeTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(OneHandedModeTile.TILE_SPEC) + fun bindOneHandedModeTile(oneHandedModeTile: OneHandedModeTile): QSTileImpl<*> + + /** Inject ColorCorrectionTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(ColorCorrectionTile.TILE_SPEC) + fun bindColorCorrectionTile(colorCorrectionTile: ColorCorrectionTile): QSTileImpl<*> + + /** Inject DreamTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(DreamTile.TILE_SPEC) + fun bindDreamTile(dreamTile: DreamTile): QSTileImpl<*> + + /** Inject FontScalingTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(FontScalingTile.TILE_SPEC) + fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*> + + companion object { + + const val COLOR_CORRECTION_TILE_SPEC = "color_correction" + + @Provides + @IntoMap + @StringKey(COLOR_CORRECTION_TILE_SPEC) + fun provideColorCorrectionTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = + QSTileConfig( + tileSpec = TileSpec.create(COLOR_CORRECTION_TILE_SPEC), + uiConfig = + QSTileUIConfig.Resource( + iconRes = R.drawable.ic_qs_color_correction, + labelRes = R.string.quick_settings_color_correction_label, + ), + instanceId = uiEventLogger.getNewInstanceId(), + ) + + /** Inject ColorCorrectionTile into tileViewModelMap in QSModule */ + @Provides + @IntoMap + @StringKey(COLOR_CORRECTION_TILE_SPEC) + fun provideColorCorrectionTileViewModel( + factory: QSTileViewModelFactory.Static<ColorCorrectionTileModel>, + mapper: ColorCorrectionTileMapper, + stateInteractor: ColorCorrectionTileDataInteractor, + userActionInteractor: ColorCorrectionUserActionInteractor + ): QSTileViewModel = + factory.create( + TileSpec.create(COLOR_CORRECTION_TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index 32d9067bd9e4..90e4a3821634 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -376,6 +376,22 @@ object BiometricViewBinder { } } + // Talkback directional guidance + backgroundView.setOnHoverListener { _, event -> + launch { + viewModel.onAnnounceAccessibilityHint( + event, + accessibilityManager.isTouchExplorationEnabled + ) + } + false + } + launch { + viewModel.accessibilityHint.collect { message -> + if (message.isNotBlank()) view.announceForAccessibility(message) + } + } + // Play haptics launch { viewModel.hapticsToPlay.collect { hapticFeedbackConstant -> diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index 647aaf392ed8..6d0a58e202bd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -21,9 +21,12 @@ import android.hardware.biometrics.BiometricPrompt import android.util.Log import android.view.HapticFeedbackConstants import android.view.MotionEvent +import com.android.systemui.Flags.bpTalkback +import com.android.systemui.biometrics.UdfpsUtils import com.android.systemui.biometrics.Utils import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor +import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.shared.model.BiometricModalities import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.biometrics.shared.model.DisplayRotation @@ -35,7 +38,9 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -49,7 +54,9 @@ class PromptViewModel constructor( displayStateInteractor: DisplayStateInteractor, promptSelectorInteractor: PromptSelectorInteractor, - @Application context: Context, + @Application private val context: Context, + private val udfpsOverlayInteractor: UdfpsOverlayInteractor, + private val udfpsUtils: UdfpsUtils ) { /** The set of modalities available for this prompt */ val modalities: Flow<BiometricModalities> = @@ -69,6 +76,11 @@ constructor( val faceIconHeight: Int = context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size) + private val _accessibilityHint = MutableSharedFlow<String>() + + /** Hint for talkback directional guidance */ + val accessibilityHint: Flow<String> = _accessibilityHint.asSharedFlow() + private val _isAuthenticating: MutableStateFlow<Boolean> = MutableStateFlow(false) /** If the user is currently authenticating (i.e. at least one biometric is scanning). */ @@ -516,6 +528,40 @@ constructor( return false } + /** Sets the message used for UDFPS directional guidance */ + suspend fun onAnnounceAccessibilityHint( + event: MotionEvent, + touchExplorationEnabled: Boolean, + ): Boolean { + if (bpTalkback() && modalities.first().hasUdfps && touchExplorationEnabled) { + // TODO(b/315184924): Remove uses of UdfpsUtils + val scaledTouch = + udfpsUtils.getTouchInNativeCoordinates( + event.getPointerId(0), + event, + udfpsOverlayInteractor.udfpsOverlayParams.value + ) + if ( + !udfpsUtils.isWithinSensorArea( + event.getPointerId(0), + event, + udfpsOverlayInteractor.udfpsOverlayParams.value + ) + ) { + _accessibilityHint.emit( + udfpsUtils.onTouchOutsideOfSensorArea( + touchExplorationEnabled, + context, + scaledTouch.x, + scaledTouch.y, + udfpsOverlayInteractor.udfpsOverlayParams.value + ) + ) + } + } + return false + } + /** * Switch to the credential view. * diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 1a2a4253e761..e342c6bca6fa 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -122,7 +122,7 @@ constructor( if (!smartspaceRepository.isSmartspaceRemoteViewsEnabled) { flowOf(emptyList()) } else { - smartspaceRepository.lockscreenSmartspaceTargets.map { targets -> + smartspaceRepository.communalSmartspaceTargets.map { targets -> targets .filter { target -> target.featureType == SmartspaceTarget.FEATURE_TIMER && diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt new file mode 100644 index 000000000000..c5610c877f57 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.smartspace + +import android.app.smartspace.SmartspaceConfig +import android.app.smartspace.SmartspaceManager +import android.app.smartspace.SmartspaceSession +import android.app.smartspace.SmartspaceTarget +import android.content.Context +import android.util.Log +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.BcSmartspaceDataPlugin +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView +import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_GLANCEABLE_HUB +import com.android.systemui.smartspace.SmartspacePrecondition +import com.android.systemui.smartspace.SmartspaceTargetFilter +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN +import com.android.systemui.util.concurrency.Execution +import java.util.Optional +import java.util.concurrent.Executor +import javax.inject.Inject +import javax.inject.Named + +/** Controller for managing the smartspace view on the dream */ +@SysUISingleton +class CommunalSmartspaceController +@Inject +constructor( + private val context: Context, + private val smartspaceManager: SmartspaceManager?, + private val execution: Execution, + @Main private val uiExecutor: Executor, + @Named(DREAM_SMARTSPACE_PRECONDITION) private val precondition: SmartspacePrecondition, + @Named(DREAM_SMARTSPACE_TARGET_FILTER) + private val optionalTargetFilter: Optional<SmartspaceTargetFilter>, + @Named(GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>, +) { + companion object { + private const val TAG = "CommunalSmartspaceCtrlr" + } + + private var session: SmartspaceSession? = null + private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null) + private var targetFilter: SmartspaceTargetFilter? = optionalTargetFilter.orElse(null) + + // A shadow copy of listeners is maintained to track whether the session should remain open. + private var listeners = mutableSetOf<SmartspaceTargetListener>() + + private var unfilteredListeners = mutableSetOf<SmartspaceTargetListener>() + + // Smartspace can be used on multiple displays, such as when the user casts their screen + private var smartspaceViews = mutableSetOf<SmartspaceView>() + + var preconditionListener = + object : SmartspacePrecondition.Listener { + override fun onCriteriaChanged() { + reloadSmartspace() + } + } + + init { + precondition.addListener(preconditionListener) + } + + var filterListener = + object : SmartspaceTargetFilter.Listener { + override fun onCriteriaChanged() { + reloadSmartspace() + } + } + + init { + targetFilter?.addListener(filterListener) + } + + private val sessionListener = + SmartspaceSession.OnTargetsAvailableListener { targets -> + execution.assertIsMainThread() + + val filteredTargets = + targets.filter { targetFilter?.filterSmartspaceTarget(it) ?: true } + plugin?.onTargetsAvailable(filteredTargets) + } + + private fun hasActiveSessionListeners(): Boolean { + return smartspaceViews.isNotEmpty() || + listeners.isNotEmpty() || + unfilteredListeners.isNotEmpty() + } + + private fun connectSession() { + if (smartspaceManager == null) { + return + } + if (plugin == null) { + return + } + if (session != null || !hasActiveSessionListeners()) { + return + } + + if (!precondition.conditionsMet()) { + return + } + + val newSession = + smartspaceManager.createSmartspaceSession( + SmartspaceConfig.Builder(context, UI_SURFACE_GLANCEABLE_HUB).build() + ) + Log.d(TAG, "Starting smartspace session for dream") + newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener) + this.session = newSession + + plugin?.registerSmartspaceEventNotifier { e -> session?.notifySmartspaceEvent(e) } + + reloadSmartspace() + } + + /** Disconnects the smartspace view from the smartspace service and cleans up any resources. */ + private fun disconnect() { + if (hasActiveSessionListeners()) return + + execution.assertIsMainThread() + + if (session == null) { + return + } + + session?.let { + it.removeOnTargetsAvailableListener(sessionListener) + it.close() + } + + session = null + + plugin?.registerSmartspaceEventNotifier(null) + plugin?.onTargetsAvailable(emptyList()) + Log.d(TAG, "Ending smartspace session for dream") + } + + fun addListener(listener: SmartspaceTargetListener) { + addAndRegisterListener(listener, plugin) + } + + fun removeListener(listener: SmartspaceTargetListener) { + removeAndUnregisterListener(listener, plugin) + } + + private fun addAndRegisterListener( + listener: SmartspaceTargetListener, + smartspaceDataPlugin: BcSmartspaceDataPlugin? + ) { + execution.assertIsMainThread() + smartspaceDataPlugin?.registerListener(listener) + listeners.add(listener) + + connectSession() + } + + private fun removeAndUnregisterListener( + listener: SmartspaceTargetListener, + smartspaceDataPlugin: BcSmartspaceDataPlugin? + ) { + execution.assertIsMainThread() + smartspaceDataPlugin?.unregisterListener(listener) + listeners.remove(listener) + disconnect() + } + + private fun reloadSmartspace() { + session?.requestSmartspaceUpdate() + } + + private fun onTargetsAvailableUnfiltered(targets: List<SmartspaceTarget>) { + unfilteredListeners.forEach { it.onSmartspaceTargetsUpdated(targets) } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index 7b94fc182fe2..573a748b4290 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -76,6 +76,10 @@ constructor( Intent(applicationContext, WidgetPickerActivity::class.java) ) }, + onEditDone = { + // TODO(b/315154364): in a separate change, lock the device and transition to GH + finish() + } ) } } diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java index 7ac1cc796d4b..9d4ed20ca582 100644 --- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java +++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java @@ -25,11 +25,15 @@ import static com.android.systemui.controls.dagger.ControlsComponent.Visibility. import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.graphics.drawable.Drawable; import android.util.Log; import android.view.View; import android.widget.ImageView; +import androidx.annotation.Nullable; + import com.android.internal.logging.UiEventLogger; +import com.android.settingslib.Utils; import com.android.systemui.CoreStartable; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent; @@ -43,6 +47,7 @@ import com.android.systemui.dagger.qualifiers.SystemUser; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.shared.condition.Monitor; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.ViewController; import com.android.systemui.util.condition.ConditionalCoreStartable; @@ -195,34 +200,70 @@ public class DreamHomeControlsComplication implements Complication { private final ActivityStarter mActivityStarter; private final Context mContext; + private final ConfigurationController mConfigurationController; private final ControlsComponent mControlsComponent; private final UiEventLogger mUiEventLogger; + private final ConfigurationController.ConfigurationListener mConfigurationListener = + new ConfigurationController.ConfigurationListener() { + @Override + public void onUiModeChanged() { + reloadResources(); + } + }; + @Inject DreamHomeControlsChipViewController( @Named(DREAM_HOME_CONTROLS_CHIP_VIEW) ImageView view, ActivityStarter activityStarter, Context context, + ConfigurationController configurationController, ControlsComponent controlsComponent, UiEventLogger uiEventLogger) { super(view); mActivityStarter = activityStarter; mContext = context; + mConfigurationController = configurationController; mControlsComponent = controlsComponent; mUiEventLogger = uiEventLogger; } @Override protected void onViewAttached() { - mView.setImageResource(mControlsComponent.getTileImageId()); - mView.setContentDescription(mContext.getString(mControlsComponent.getTileTitleId())); + reloadResources(); mView.setOnClickListener(this::onClickHomeControls); + mConfigurationController.addCallback(mConfigurationListener); } @Override - protected void onViewDetached() {} + protected void onViewDetached() { + mConfigurationController.removeCallback(mConfigurationListener); + } + + private void reloadResources() { + final String title = getControlsTitle(); + if (title != null) { + mView.setContentDescription(title); + } + mView.setImageResource(mControlsComponent.getTileImageId()); + mView.setImageTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary)); + final Drawable background = mView.getBackground(); + if (background != null) { + background.setTintList( + Utils.getColorAttr(mContext, com.android.internal.R.attr.colorSurface)); + } + } + + @Nullable + private String getControlsTitle() { + try { + return mContext.getString(mControlsComponent.getTileTitleId()); + } catch (Resources.NotFoundException e) { + return null; + } + } private void onClickHomeControls(View v) { if (DEBUG) Log.d(TAG, "home controls complication tapped"); diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java index 08d0595eba23..b6dcfcbfad56 100644 --- a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java +++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java @@ -24,9 +24,8 @@ import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.widget.ImageView; -import com.android.settingslib.Utils; -import com.android.systemui.res.R; import com.android.systemui.complication.DreamHomeControlsComplication; +import com.android.systemui.res.R; import com.android.systemui.shared.shadow.DoubleShadowIconDrawable; import com.android.systemui.shared.shadow.DoubleShadowTextHelper; @@ -98,7 +97,7 @@ public interface DreamHomeControlsComplicationComponent { @DreamHomeControlsComplicationScope @Named(DREAM_HOME_CONTROLS_BACKGROUND_DRAWABLE) static Drawable providesHomeControlsBackground(Context context, Resources resources) { - final Drawable background = new DoubleShadowIconDrawable(createShadowInfo( + return new DoubleShadowIconDrawable(createShadowInfo( resources, R.dimen.dream_overlay_bottom_affordance_key_text_shadow_radius, R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dx, @@ -117,11 +116,6 @@ public interface DreamHomeControlsComplicationComponent { R.dimen.dream_overlay_bottom_affordance_width), resources.getDimensionPixelSize(R.dimen.dream_overlay_bottom_affordance_inset) ); - - background.setTintList( - Utils.getColorAttr(context, com.android.internal.R.attr.colorSurface)); - - return background; } private static DoubleShadowTextHelper.ShadowInfo createShadowInfo(Resources resources, diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt index 65d44957222a..3a927396527c 100644 --- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt +++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt @@ -63,6 +63,7 @@ interface BaseComposeFacade { activity: ComponentActivity, viewModel: BaseCommunalViewModel, onOpenWidgetPicker: () -> Unit, + onEditDone: () -> Unit, ) /** Create a [View] to represent [viewModel] on screen. */ diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt index 63b01edb01fa..0daa058720ba 100644 --- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt @@ -35,23 +35,22 @@ import java.util.concurrent.Executor import javax.inject.Inject /** Dialog to select contrast options */ -class ContrastDialogDelegate @Inject constructor( - private val sysuiDialogFactory : SystemUIDialog.Factory, +class ContrastDialogDelegate +@Inject +constructor( + private val sysuiDialogFactory: SystemUIDialog.Factory, @Main private val mainExecutor: Executor, private val uiModeManager: UiModeManager, private val userTracker: UserTracker, private val secureSettings: SecureSettings, ) : SystemUIDialog.Delegate, UiModeManager.ContrastChangeListener { - override fun createDialog(): SystemUIDialog { - return sysuiDialogFactory.create(this) - } - @VisibleForTesting lateinit var contrastButtons: Map<Int, FrameLayout> lateinit var dialogView: View @VisibleForTesting var initialContrast: Float = fromContrastLevel(CONTRAST_LEVEL_STANDARD) - override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + override fun createDialog(): SystemUIDialog { + val dialog = sysuiDialogFactory.create(this) dialogView = dialog.layoutInflater.inflate(R.layout.contrast_dialog, null) with(dialog) { setView(dialogView) @@ -67,12 +66,16 @@ class ContrastDialogDelegate @Inject constructor( } setPositiveButton(com.android.settingslib.R.string.done) { _, _ -> dialog.dismiss() } } + + return dialog + } + + override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { contrastButtons = mapOf( - CONTRAST_LEVEL_STANDARD to dialogView.requireViewById( - R.id.contrast_button_standard), - CONTRAST_LEVEL_MEDIUM to dialogView.requireViewById(R.id.contrast_button_medium), - CONTRAST_LEVEL_HIGH to dialogView.requireViewById(R.id.contrast_button_high) + CONTRAST_LEVEL_STANDARD to dialog.requireViewById(R.id.contrast_button_standard), + CONTRAST_LEVEL_MEDIUM to dialog.requireViewById(R.id.contrast_button_medium), + CONTRAST_LEVEL_HIGH to dialog.requireViewById(R.id.contrast_button_high) ) contrastButtons.forEach { (contrastLevel, contrastButton) -> diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index d5b95d6721f9..5ec51f4c3dad 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -16,6 +16,12 @@ package com.android.systemui.flags +import com.android.server.notification.Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS +import com.android.server.notification.Flags.FLAG_POLITE_NOTIFICATIONS +import com.android.server.notification.Flags.FLAG_VIBRATE_WHILE_UNLOCKED +import com.android.server.notification.Flags.crossAppPoliteNotifications +import com.android.server.notification.Flags.politeNotifications +import com.android.server.notification.Flags.vibrateWhileUnlocked import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR import com.android.systemui.Flags.keyguardBottomAreaRefactor import com.android.systemui.dagger.SysUISingleton @@ -36,5 +42,14 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha val keyguardBottomAreaRefactor = FlagToken( FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor()) KeyguardShadeMigrationNssl.token dependsOn keyguardBottomAreaRefactor + + val crossAppPoliteNotifToken = + FlagToken(FLAG_CROSS_APP_POLITE_NOTIFICATIONS, crossAppPoliteNotifications()) + val politeNotifToken = FlagToken(FLAG_POLITE_NOTIFICATIONS, politeNotifications()) + crossAppPoliteNotifToken dependsOn politeNotifToken + + val vibrateWhileUnlockedToken = + FlagToken(FLAG_VIBRATE_WHILE_UNLOCKED, vibrateWhileUnlocked()) + vibrateWhileUnlockedToken dependsOn politeNotifToken } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index b753ff742100..b1d4587c20d8 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -111,7 +111,7 @@ object Flags { // TODO(b/301955929) @JvmField val NOTIF_LS_BACKGROUND_THREAD = - unreleasedFlag("notification_lockscreen_mgr_bg_thread", teamfood = true) + releasedFlag("notification_lockscreen_mgr_bg_thread") // 200 - keyguard/lockscreen // ** Flag retired ** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index b7260f2b2b94..57f3b7071d1d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -141,6 +141,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.flags.SystemPropertiesHelper; import com.android.systemui.keyguard.dagger.KeyguardModule; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.log.SessionTracker; @@ -1319,6 +1320,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private DeviceConfigProxy mDeviceConfig; private DozeParameters mDozeParameters; private SelectedUserInteractor mSelectedUserInteractor; + private KeyguardInteractor mKeyguardInteractor; private final KeyguardStateController mKeyguardStateController; private final KeyguardStateController.Callback mKeyguardStateControllerCallback = @@ -1400,7 +1402,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel, SystemPropertiesHelper systemPropertiesHelper, Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager, - SelectedUserInteractor selectedUserInteractor) { + SelectedUserInteractor selectedUserInteractor, + KeyguardInteractor keyguardInteractor) { mContext = context; mUserTracker = userTracker; mFalsingCollector = falsingCollector; @@ -1441,6 +1444,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, })); mDozeParameters = dozeParameters; mSelectedUserInteractor = selectedUserInteractor; + mKeyguardInteractor = keyguardInteractor; mStatusBarStateController = statusBarStateController; statusBarStateController.addCallback(this); @@ -2618,6 +2622,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, setPendingLock(false); // user may have authenticated during the screen off animation handleHide(); + mKeyguardInteractor.keyguardDoneAnimationsFinished(); mUpdateMonitor.clearFingerprintRecognizedWhenKeyguardDone(currentUser); Trace.endSection(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 331d892304b3..3925dd1620b4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -52,6 +52,7 @@ import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager; import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule; import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule; import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule; import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger; import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl; @@ -154,7 +155,8 @@ public class KeyguardModule { Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel, SystemPropertiesHelper systemPropertiesHelper, Lazy<WindowManagerLockscreenVisibilityManager> wmLockscreenVisibilityManager, - SelectedUserInteractor selectedUserInteractor) { + SelectedUserInteractor selectedUserInteractor, + KeyguardInteractor keyguardInteractor) { return new KeyguardViewMediator( context, uiEventLogger, @@ -199,7 +201,8 @@ public class KeyguardModule { dreamingToLockscreenTransitionViewModel, systemPropertiesHelper, wmLockscreenVisibilityManager, - selectedUserInteractor); + selectedUserInteractor, + keyguardInteractor); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index eceaf6c3c4fd..4d60dd0bea62 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -187,7 +187,8 @@ constructor( faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false private val _isAuthRunning = MutableStateFlow(false) - override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning + override val isAuthRunning: StateFlow<Boolean> + get() = _isAuthRunning private val keyguardSessionId: InstanceId? get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD) @@ -253,13 +254,6 @@ constructor( ) .andAllFlows("canFaceAuthRun", faceAuthLog) .flowOn(backgroundDispatcher) - .onEach { - faceAuthLogger.canFaceAuthRunChanged(it) - if (!it) { - // Cancel currently running auth if any of the gating checks are false. - cancel() - } - } .stateIn(applicationScope, SharingStarted.Eagerly, false) // Face detection can run only when lockscreen bypass is enabled @@ -287,12 +281,9 @@ constructor( ) .andAllFlows("canFaceDetectRun", faceDetectLog) .flowOn(backgroundDispatcher) - .onEach { - if (!it) { - cancelDetection() - } - } .stateIn(applicationScope, SharingStarted.Eagerly, false) + observeFaceAuthGatingChecks() + observeFaceDetectGatingChecks() observeFaceAuthResettingConditions() listenForSchedulingWatchdog() processPendingAuthRequests() @@ -313,14 +304,14 @@ constructor( } private fun observeFaceAuthResettingConditions() { - // Clear auth status when keyguard is going away or when the user is switching or device - // starts going to sleep. + // Clear auth status when keyguard done animations finished or when the user is switching + // or device starts going to sleep. merge( powerInteractor.isAsleep, if (featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) { keyguardTransitionInteractor.isInTransitionToState(KeyguardState.GONE) } else { - keyguardRepository.isKeyguardGoingAway + keyguardRepository.keyguardDoneAnimationsFinished.map { true } }, userRepository.selectedUser.map { it.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS @@ -347,6 +338,17 @@ constructor( pendingAuthenticateRequest.value = null } + private fun observeFaceDetectGatingChecks() { + canRunDetection + .onEach { + if (!it) { + cancelDetection() + } + } + .flowOn(mainDispatcher) + .launchIn(applicationScope) + } + private fun isUdfps() = deviceEntryFingerprintAuthRepository.availableFpSensorType.map { it == BiometricType.UNDER_DISPLAY_FINGERPRINT @@ -405,6 +407,20 @@ constructor( ) } + private fun observeFaceAuthGatingChecks() { + canRunFaceAuth + .onEach { + faceAuthLogger.canFaceAuthRunChanged(it) + if (!it) { + // Cancel currently running auth if any of the gating checks are false. + faceAuthLogger.cancellingFaceAuth() + cancel() + } + } + .flowOn(mainDispatcher) + .launchIn(applicationScope) + } + private val faceAuthCallback = object : FaceManager.AuthenticationCallback() { override fun onAuthenticationFailed() { @@ -539,7 +555,7 @@ constructor( authenticate(it.uiEvent, it.fallbackToDetection) } } - .flowOn(backgroundDispatcher) + .flowOn(mainDispatcher) .launchIn(applicationScope) } @@ -635,7 +651,6 @@ constructor( override fun cancel() { if (authCancellationSignal == null) return - faceAuthLogger.cancellingFaceAuth() authCancellationSignal?.cancel() cancelNotReceivedHandlerJob?.cancel() cancelNotReceivedHandlerJob = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index b51edab6dfe8..31ef100abbcb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -160,6 +160,9 @@ interface KeyguardRepository { /** Last point that [KeyguardRootView] was tapped */ val lastRootViewTapPosition: MutableStateFlow<Point?> + /** Is the ambient indication area visible? */ + val ambientIndicationVisible: MutableStateFlow<Boolean> + /** Observable for the [StatusBarState] */ val statusBarState: StateFlow<StatusBarState> @@ -189,6 +192,17 @@ interface KeyguardRepository { /** Observable updated when keyguardDone should be called either now or soon. */ val keyguardDone: Flow<KeyguardDone> + /** + * Emits after the keyguard is done animating away. + * + * TODO(b/278086361): Remove once KEYGUARD_WM_STATE_REFACTOR flag is removed. + */ + @Deprecated( + "Use KeyguardTransitionInteractor flows instead. The closest match for " + + "'keyguardDoneAnimationsFinished' is when the GONE transition is finished." + ) + val keyguardDoneAnimationsFinished: Flow<Unit> + /** Receive whether clock should be centered on lockscreen. */ val clockShouldBeCentered: Flow<Boolean> @@ -236,6 +250,17 @@ interface KeyguardRepository { suspend fun setKeyguardDone(keyguardDoneType: KeyguardDone) fun setClockShouldBeCentered(shouldBeCentered: Boolean) + + /** + * Updates signal that the keyguard done animations are finished + * + * TODO(b/278086361): Remove once KEYGUARD_WM_STATE_REFACTOR flag is removed. + */ + @Deprecated( + "Use KeyguardTransitionInteractor flows instead. The closest match for " + + "'keyguardDoneAnimationsFinished' is when the GONE transition is finished." + ) + fun keyguardDoneAnimationsFinished() } /** Encapsulates application state for the keyguard. */ @@ -266,6 +291,11 @@ constructor( _keyguardDone.emit(keyguardDoneType) } + override val keyguardDoneAnimationsFinished: MutableSharedFlow<Unit> = MutableSharedFlow() + override fun keyguardDoneAnimationsFinished() { + keyguardDoneAnimationsFinished.tryEmit(Unit) + } + private val _animateBottomAreaDozingTransitions = MutableStateFlow(false) override val animateBottomAreaDozingTransitions = _animateBottomAreaDozingTransitions.asStateFlow() @@ -423,6 +453,8 @@ constructor( override val lastRootViewTapPosition: MutableStateFlow<Point?> = MutableStateFlow(null) + override val ambientIndicationVisible: MutableStateFlow<Boolean> = MutableStateFlow(false) + override val isDreamingWithOverlay: Flow<Boolean> = conflatedCallbackFlow { val callback = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index c12efe875b0b..21651ba2cc2b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -174,6 +174,9 @@ constructor( /** Last point that [KeyguardRootView] view was tapped */ val lastRootViewTapPosition: Flow<Point?> = repository.lastRootViewTapPosition.asStateFlow() + /** Is the ambient indication area visible? */ + val ambientIndicationVisible: Flow<Boolean> = repository.ambientIndicationVisible.asStateFlow() + /** Whether the primary bouncer is showing or not. */ val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerShow @@ -311,6 +314,14 @@ constructor( repository.lastRootViewTapPosition.value = point } + fun setAmbientIndicationVisible(isVisible: Boolean) { + repository.ambientIndicationVisible.value = isVisible + } + + fun keyguardDoneAnimationsFinished() { + repository.keyguardDoneAnimationsFinished() + } + companion object { private const val TAG = "KeyguardInteractor" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt index fb20000471a2..e3f47397eca3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt @@ -31,7 +31,6 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository @@ -72,7 +71,6 @@ constructor( private val context: Context, @Application private val applicationScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, - @Background private val backgroundDispatcher: CoroutineDispatcher, private val repository: DeviceEntryFaceAuthRepository, private val primaryBouncerInteractor: Lazy<PrimaryBouncerInteractor>, private val alternateBouncerInteractor: AlternateBouncerInteractor, @@ -109,7 +107,6 @@ constructor( fallbackToDetect = false ) } - .flowOn(backgroundDispatcher) .launchIn(applicationScope) alternateBouncerInteractor.isVisible @@ -121,7 +118,6 @@ constructor( fallbackToDetect = false ) } - .flowOn(backgroundDispatcher) .launchIn(applicationScope) merge( @@ -150,7 +146,6 @@ constructor( fallbackToDetect = true ) } - .flowOn(backgroundDispatcher) .launchIn(applicationScope) deviceEntryFingerprintAuthRepository.isLockedOut @@ -163,7 +158,6 @@ constructor( } } } - .flowOn(backgroundDispatcher) .launchIn(applicationScope) // User switching should stop face auth and then when it is complete we should trigger face @@ -187,7 +181,6 @@ constructor( ) } } - .flowOn(backgroundDispatcher) .launchIn(applicationScope) } @@ -302,7 +295,6 @@ constructor( trustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, userInfo.id) } } - .flowOn(backgroundDispatcher) .onEach { (isAuthenticated, _) -> listeners.forEach { it.onAuthenticatedChanged(isAuthenticated) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt index acfd3b0bcf57..24240dfe7402 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt @@ -30,6 +30,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants +import com.android.systemui.util.kotlin.logD import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -58,11 +59,14 @@ constructor( observer = PreviewLifecycleObserver( - renderer, applicationScope, mainDispatcher, + renderer, ::destroyObserver, ) + + logD(TAG) { "Created observer $observer" } + // Destroy any previous renderer associated with this token. activePreviews[renderer.id]?.let { destroyObserver(it) } activePreviews[renderer.id] = observer @@ -80,6 +84,8 @@ constructor( observer, ) ) + // NOTE: The process on the other side can retain messenger indefinitely. + // (e.g. GC might not trigger and cleanup the reference) val msg = Message.obtain() msg.replyTo = messenger result.putParcelable(KEY_PREVIEW_CALLBACK, msg) @@ -99,57 +105,84 @@ constructor( } } - private class PreviewLifecycleObserver( - private val renderer: KeyguardPreviewRenderer, - private val scope: CoroutineScope, - private val mainDispatcher: CoroutineDispatcher, - private val requestDestruction: (PreviewLifecycleObserver) -> Unit, - ) : Handler.Callback, IBinder.DeathRecipient { + companion object { + internal const val TAG = "KeyguardRemotePreviewManager" + @VisibleForTesting const val KEY_PREVIEW_SURFACE_PACKAGE = "surface_package" + @VisibleForTesting const val KEY_PREVIEW_CALLBACK = "callback" + } +} - private var isDestroyedOrDestroying = false +/** + * Handles messages from the other process and handles cleanup. + * + * NOTE: The other process might hold on to reference of this class indefinitely. It's entirely + * possible that GC won't trigger and we'll leak this for all times even if [onDestroy] was called. + * This helps make sure no non-Singleton objects are retained beyond destruction to prevent leaks. + */ +@VisibleForTesting(VisibleForTesting.PRIVATE) +class PreviewLifecycleObserver( + private val scope: CoroutineScope, + private val mainDispatcher: CoroutineDispatcher, + renderer: KeyguardPreviewRenderer, + onDestroy: (PreviewLifecycleObserver) -> Unit, +) : Handler.Callback, IBinder.DeathRecipient { + + private var isDestroyedOrDestroying = false + // These two are null after destruction + @VisibleForTesting var renderer: KeyguardPreviewRenderer? + @VisibleForTesting var onDestroy: ((PreviewLifecycleObserver) -> Unit)? + + init { + this.renderer = renderer + this.onDestroy = onDestroy + } - override fun handleMessage(message: Message): Boolean { - if (isDestroyedOrDestroying) { - return true - } + override fun handleMessage(message: Message): Boolean { + if (isDestroyedOrDestroying) { + return true + } - when (message.what) { - KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> { - message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId -> - renderer.onSlotSelected(slotId = slotId) - } + when (message.what) { + KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> { + message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId -> + checkNotNull(renderer).onSlotSelected(slotId = slotId) } - KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> { - renderer.hideSmartspace( + } + KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> { + checkNotNull(renderer) + .hideSmartspace( message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE) ) - } - else -> requestDestruction(this) } - - return true + else -> checkNotNull(onDestroy).invoke(this) } - override fun binderDied() { - requestDestruction(this) + return true + } + + override fun binderDied() { + onDestroy?.invoke(this) + } + + fun onDestroy(): Pair<IBinder?, Int>? { + if (isDestroyedOrDestroying) { + return null } - fun onDestroy(): Pair<IBinder?, Int>? { - if (isDestroyedOrDestroying) { - return null - } + logD(TAG) { "Destroying $this" } - isDestroyedOrDestroying = true - val hostToken = renderer.hostToken + isDestroyedOrDestroying = true + return renderer?.let { rendererToDestroy -> + this.renderer = null + this.onDestroy = null + val hostToken = rendererToDestroy.hostToken hostToken?.unlinkToDeath(this, 0) - scope.launch(mainDispatcher) { renderer.destroy() } - return renderer.id + scope.launch(mainDispatcher) { rendererToDestroy.destroy() } + rendererToDestroy.id } } companion object { private const val TAG = "KeyguardRemotePreviewManager" - @VisibleForTesting const val KEY_PREVIEW_SURFACE_PACKAGE = "surface_package" - @VisibleForTesting const val KEY_PREVIEW_CALLBACK = "callback" } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 1d4520ff8f03..26dace00ad76 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -180,7 +180,9 @@ constructor( goneToAodTransitionViewModel .enterFromTopTranslationY(enterFromTopAmount) .onStart { emit(0f) }, - occludedToLockscreenTransitionViewModel.lockscreenTranslationY, + occludedToLockscreenTransitionViewModel.lockscreenTranslationY.onStart { + emit(0f) + }, ) { keyguardTransitionY, burnInTranslationY, @@ -193,6 +195,7 @@ constructor( occludedToLockscreenTransitionTranslationY } } + .distinctUntilChanged() val translationX: Flow<Float> = burnIn().map { it.translationX.toFloat() } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt index 14d4b6800b0a..c87fd14a943d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt @@ -119,7 +119,7 @@ internal constructor( ::AnimatingColorTransition ) - val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_secondary95) + val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_neutral20) val surfaceColor = animatingColorTransitionFactory(bgColor, ::surfaceFromScheme) { surfaceColor -> val colorList = ColorStateList.valueOf(surfaceColor) diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt index 6d55d0549d2e..de490a58a498 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt @@ -27,7 +27,6 @@ import android.os.UserManager import android.util.Log import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback -import com.android.systemui.res.R import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon @@ -43,6 +42,7 @@ import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEnabledKey import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskInfoResolver +import com.android.systemui.res.R import com.android.systemui.stylus.StylusManager import dagger.Lazy import java.util.concurrent.Executor @@ -100,7 +100,8 @@ constructor( isEnabled && isUserUnlocked && isDefaultNotesAppSet && - (isConfigSelected || isStylusEverUsed) + isConfigSelected && + isStylusEverUsed ) { val contentDescription = ContentDescription.Resource(pickerNameResourceId) val icon = Icon.Resource(pickerIconResourceId, contentDescription) diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt index fdc70a83e8b1..76ef8a2b813c 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogControllerV2.kt @@ -278,7 +278,12 @@ class PrivacyDialogControllerV2( d.setShowForAllUsers(true) d.addOnDismissListener(onDialogDismissed) if (view != null) { - dialogLaunchAnimator.showFromView(d, view) + val controller = getPrivacyDialogController(view) + if (controller == null) { + d.show() + } else { + dialogLaunchAnimator.show(d, controller) + } } else { d.show() } @@ -291,6 +296,13 @@ class PrivacyDialogControllerV2( } } + private fun getPrivacyDialogController(source: View): DialogLaunchAnimator.Controller? { + val delegate = DialogLaunchAnimator.Controller.fromView(source) ?: return null + return object : DialogLaunchAnimator.Controller by delegate { + override fun shouldAnimateExit() = false + } + } + /** Dismisses the dialog */ fun dismissDialog() { dialog?.dismiss() diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt index b50798e59953..4bad45f19673 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt @@ -20,6 +20,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory +import com.android.systemui.qs.pipeline.data.model.RestoreProcessor import com.android.systemui.qs.pipeline.data.repository.DefaultTilesQSHostRepository import com.android.systemui.qs.pipeline.data.repository.DefaultTilesRepository import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository @@ -39,14 +40,17 @@ import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap +import dagger.multibindings.Multibinds -@Module(includes = [QSAutoAddModule::class]) +@Module(includes = [QSAutoAddModule::class, RestoreProcessorsModule::class]) abstract class QSPipelineModule { /** Implementation for [TileSpecRepository] */ @Binds abstract fun provideTileSpecRepository(impl: TileSpecSettingsRepository): TileSpecRepository + @Multibinds abstract fun provideRestoreProcessors(): Set<RestoreProcessor> + @Binds abstract fun provideDefaultTilesRepository( impl: DefaultTilesQSHostRepository diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/RestoreProcessorsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/RestoreProcessorsModule.kt new file mode 100644 index 000000000000..e970c84d166f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/RestoreProcessorsModule.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.dagger + +import com.android.systemui.qs.pipeline.data.model.RestoreProcessor +import com.android.systemui.qs.pipeline.data.restoreprocessors.WorkTileRestoreProcessor +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet + +@Module +interface RestoreProcessorsModule { + + @Binds + @IntoSet + fun bindWorkTileRestoreProcessor(impl: WorkTileRestoreProcessor): RestoreProcessor +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessor.kt new file mode 100644 index 000000000000..8f7de1976b36 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessor.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.data.model + +/** + * Perform processing of the [RestoreData] before or after it's applied to repositories. + * + * The order in which the restore processors are applied in not deterministic. + * + * In order to declare a restore processor, add it in [RestoreProcessingModule] using + * + * ``` + * @Binds + * @IntoSet + * `` + */ +interface RestoreProcessor { + + /** Should be called before applying the restore to the necessary repositories */ + suspend fun preProcessRestore(restoreData: RestoreData) {} + + /** Should be called after requesting the repositories to update. */ + suspend fun postProcessRestore(restoreData: RestoreData) {} +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt index 7998dfbe3f92..d40f3f4db5f3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt @@ -20,9 +20,8 @@ import android.util.SparseArray import com.android.systemui.dagger.SysUISingleton import com.android.systemui.qs.pipeline.data.model.RestoreData import com.android.systemui.qs.pipeline.shared.TileSpec -import kotlinx.coroutines.flow.Flow import javax.inject.Inject -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.Flow /** Repository to track what QS tiles have been auto-added */ interface AutoAddRepository { @@ -49,8 +48,9 @@ interface AutoAddRepository { @SysUISingleton class AutoAddSettingRepository @Inject -constructor(private val userAutoAddRepositoryFactory: UserAutoAddRepository.Factory) : - AutoAddRepository { +constructor( + private val userAutoAddRepositoryFactory: UserAutoAddRepository.Factory, +) : AutoAddRepository { private val userAutoAddRepositories = SparseArray<UserAutoAddRepository>() diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt index 6cee1161a104..e718eea09665 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt @@ -10,6 +10,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.pipeline.data.model.RestoreData +import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository.Companion.BUFFER_CAPACITY import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter.toTilesList import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger import javax.inject.Inject @@ -28,6 +29,14 @@ import kotlinx.coroutines.flow.shareIn /** Provides restored data (from Backup and Restore) for Quick Settings pipeline */ interface QSSettingsRestoredRepository { val restoreData: Flow<RestoreData> + + companion object { + // This capacity is the number of restore data that we will keep buffered in the shared + // flow. It is unlikely that at any given time there would be this many restores being + // processed by consumers, but just in case that a couple of users are restored at the + // same time and they need to be replayed for the consumers of the flow. + const val BUFFER_CAPACITY = 10 + } } @SysUISingleton @@ -86,11 +95,6 @@ constructor( private companion object { private const val TAG = "QSSettingsRestoredBroadcastRepository" - // This capacity is the number of restore data that we will keep buffered in the shared - // flow. It is unlikely that at any given time there would be this many restores being - // processed by consumers, but just in case that a couple of users are restored at the - // same time and they need to be replayed for the consumers of the flow. - private const val BUFFER_CAPACITY = 10 private val INTENT_FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED) private const val TILES_SETTING = Settings.Secure.QS_TILES diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessor.kt new file mode 100644 index 000000000000..7376aa90883f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessor.kt @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.data.restoreprocessors + +import android.os.UserHandle +import android.util.SparseIntArray +import androidx.annotation.GuardedBy +import androidx.core.util.getOrDefault +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.qs.pipeline.data.model.RestoreData +import com.android.systemui.qs.pipeline.data.model.RestoreProcessor +import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository +import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.tiles.WorkModeTile +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map + +/** + * Processor for restore data for work tile. + * + * It will indicate when auto-add tracking may be removed for a user. This may be necessary if the + * tile will be destroyed due to being not available, but needs to be added once work profile is + * enabled (after restore), in the same position as it was in the restored data. + */ +@SysUISingleton +class WorkTileRestoreProcessor @Inject constructor() : RestoreProcessor { + + @GuardedBy("lastRestorePosition") private val lastRestorePosition = SparseIntArray() + + private val _removeTrackingForUser = + MutableSharedFlow<Int>(extraBufferCapacity = QSSettingsRestoredRepository.BUFFER_CAPACITY) + + /** + * Flow indicating that we may need to remove auto-add tracking for the work tile for a given + * user. + */ + fun removeTrackingForUser(userHandle: UserHandle): Flow<Unit> { + return _removeTrackingForUser.filter { it == userHandle.identifier }.map {} + } + + override suspend fun postProcessRestore(restoreData: RestoreData) { + if (TILE_SPEC in restoreData.restoredTiles) { + synchronized(lastRestorePosition) { + lastRestorePosition.put( + restoreData.userId, + restoreData.restoredTiles.indexOf(TILE_SPEC) + ) + } + _removeTrackingForUser.emit(restoreData.userId) + } + } + + fun pollLastPosition(userId: Int): Int { + return synchronized(lastRestorePosition) { + lastRestorePosition.getOrDefault(userId, TileSpecRepository.POSITION_AT_END).also { + lastRestorePosition.delete(userId) + } + } + } + + companion object { + private val TILE_SPEC = TileSpec.create(WorkModeTile.TILE_SPEC) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt index 5e3c34841c50..b22119966460 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt @@ -17,8 +17,10 @@ package com.android.systemui.qs.pipeline.domain.autoaddable import android.content.pm.UserInfo +import android.os.UserHandle import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.qs.pipeline.data.restoreprocessors.WorkTileRestoreProcessor import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking import com.android.systemui.qs.pipeline.domain.model.AutoAddable @@ -28,6 +30,8 @@ import com.android.systemui.settings.UserTracker import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.merge /** * [AutoAddable] for [WorkModeTile.TILE_SPEC]. @@ -36,17 +40,37 @@ import kotlinx.coroutines.flow.Flow * signal to remove it if there is not. */ @SysUISingleton -class WorkTileAutoAddable @Inject constructor(private val userTracker: UserTracker) : AutoAddable { +class WorkTileAutoAddable +@Inject +constructor( + private val userTracker: UserTracker, + private val workTileRestoreProcessor: WorkTileRestoreProcessor, +) : AutoAddable { private val spec = TileSpec.create(WorkModeTile.TILE_SPEC) override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> { - return conflatedCallbackFlow { + val removeTrackingDueToRestore: Flow<AutoAddSignal> = + workTileRestoreProcessor.removeTrackingForUser(UserHandle.of(userId)).mapNotNull { + val profiles = userTracker.userProfiles + if (profiles.any { it.id == userId } && !profiles.any { it.isManagedProfile }) { + // Only remove auto-added if there are no managed profiles for this user + AutoAddSignal.RemoveTracking(spec) + } else { + null + } + } + val signalsFromCallback = conflatedCallbackFlow { fun maybeSend(profiles: List<UserInfo>) { if (profiles.any { it.id == userId }) { // We are looking at the profiles of the correct user. if (profiles.any { it.isManagedProfile }) { - trySend(AutoAddSignal.Add(spec)) + trySend( + AutoAddSignal.Add( + spec, + workTileRestoreProcessor.pollLastPosition(userId), + ) + ) } else { trySend(AutoAddSignal.Remove(spec)) } @@ -65,6 +89,7 @@ class WorkTileAutoAddable @Inject constructor(private val userTracker: UserTrack awaitClose { userTracker.removeCallback(callback) } } + return merge(removeTrackingDueToRestore, signalsFromCallback) } override val autoAddTracking = AutoAddTracking.Always diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt index b74739322fcd..cde38359a871 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt @@ -103,6 +103,10 @@ constructor( qsPipelineLogger.logTileAutoRemoved(userId, signal.spec) repository.unmarkTileAdded(userId, signal.spec) } + is AutoAddSignal.RemoveTracking -> { + qsPipelineLogger.logTileUnmarked(userId, signal.spec) + repository.unmarkTileAdded(userId, signal.spec) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt index 9844903eff26..a5be14ec3776 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt @@ -3,14 +3,15 @@ package com.android.systemui.qs.pipeline.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.qs.pipeline.data.model.RestoreProcessor import com.android.systemui.qs.pipeline.data.repository.AutoAddRepository import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository +import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flatMapConcat import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.take @@ -33,6 +34,8 @@ constructor( private val tileSpecRepository: TileSpecRepository, private val autoAddRepository: AutoAddRepository, private val qsSettingsRestoredRepository: QSSettingsRestoredRepository, + private val restoreProcessors: Set<@JvmSuppressWildcards RestoreProcessor>, + private val qsPipelineLogger: QSPipelineLogger, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) { @@ -40,14 +43,30 @@ constructor( @OptIn(ExperimentalCoroutinesApi::class) fun start() { applicationScope.launch(backgroundDispatcher) { - qsSettingsRestoredRepository.restoreData.flatMapConcat { data -> - autoAddRepository.autoAddedTiles(data.userId) - .take(1) - .map { tiles -> data to tiles } - }.collect { (restoreData, autoAdded) -> - tileSpecRepository.reconcileRestore(restoreData, autoAdded) - autoAddRepository.reconcileRestore(restoreData) - } + qsSettingsRestoredRepository.restoreData + .flatMapConcat { data -> + autoAddRepository.autoAddedTiles(data.userId).take(1).map { tiles -> + data to tiles + } + } + .collect { (restoreData, autoAdded) -> + restoreProcessors.forEach { + it.preProcessRestore(restoreData) + qsPipelineLogger.logRestoreProcessorApplied( + it::class.simpleName, + QSPipelineLogger.RestorePreprocessorStep.PREPROCESSING, + ) + } + tileSpecRepository.reconcileRestore(restoreData, autoAdded) + autoAddRepository.reconcileRestore(restoreData) + restoreProcessors.forEach { + it.postProcessRestore(restoreData) + qsPipelineLogger.logRestoreProcessorApplied( + it::class.simpleName, + QSPipelineLogger.RestorePreprocessorStep.POSTPROCESSING, + ) + } + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt index ed7b8bd4c2f4..8263680d7fad 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt @@ -34,4 +34,9 @@ sealed interface AutoAddSignal { data class Remove( override val spec: TileSpec, ) : AutoAddSignal + + /** Signal for remove the auto-add marker from the tile, but not remove the tile */ + data class RemoveTracking( + override val spec: TileSpec, + ) : AutoAddSignal } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt index bca86c9ee8af..7d2c6c8f70fb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt @@ -209,6 +209,18 @@ constructor( ) } + fun logTileUnmarked(userId: Int, spec: TileSpec) { + tileAutoAddLogBuffer.log( + AUTO_ADD_TAG, + LogLevel.DEBUG, + { + int1 = userId + str1 = spec.toString() + }, + { "Tile $str1 unmarked as auto-added for user $int1" } + ) + } + fun logSettingsRestored(restoreData: RestoreData) { restoreLogBuffer.log( RESTORE_TAG, @@ -226,6 +238,21 @@ constructor( ) } + fun logRestoreProcessorApplied( + restoreProcessorClassName: String?, + step: RestorePreprocessorStep, + ) { + restoreLogBuffer.log( + RESTORE_TAG, + LogLevel.DEBUG, + { + str1 = restoreProcessorClassName + str2 = step.name + }, + { "Restore $str2 processed by $str1" } + ) + } + /** Reasons for destroying an existing tile. */ enum class TileDestroyedReason(val readable: String) { TILE_REMOVED("Tile removed from current set"), @@ -234,4 +261,9 @@ constructor( EXISTING_TILE_NOT_AVAILABLE("Existing tile not available"), TILE_NOT_PRESENT_IN_NEW_USER("Tile not present in new user"), } + + enum class RestorePreprocessorStep { + PREPROCESSING, + POSTPROCESSING + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 38bbce45e143..17e6375967fc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -19,6 +19,7 @@ import android.util.Log; import androidx.annotation.Nullable; +import com.android.systemui.accessibility.qs.QSAccessibilityModule; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; @@ -41,7 +42,7 @@ import javax.inject.Provider; * com.android.systemui.qs.tiles.DreamTile#TILE_SPEC}) * * After, create or find an existing Module class to house the tile's binding method (e.g. {@link - * com.android.systemui.accessibility.AccessibilityModule}). If creating a new module, add your + * QSAccessibilityModule}). If creating a new module, add your * module to the SystemUI dagger graph by including it in an appropriate module. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt index a4088f81f062..0434b2d89e32 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles +import android.app.AlertDialog import android.content.Intent import android.os.Handler import android.os.Looper @@ -24,8 +25,11 @@ import android.text.TextUtils import android.view.View import android.widget.Switch import androidx.annotation.VisibleForTesting +import com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN import com.android.internal.logging.MetricsLogger import com.android.systemui.Flags.recordIssueQsTile +import com.android.systemui.animation.DialogCuj +import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter @@ -36,7 +40,11 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.recordissue.RecordIssueDialogDelegate import com.android.systemui.res.R +import com.android.systemui.statusbar.phone.KeyguardDismissUtil +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject class RecordIssueTile @@ -50,7 +58,11 @@ constructor( metricsLogger: MetricsLogger, statusBarStateController: StatusBarStateController, activityStarter: ActivityStarter, - qsLogger: QSLogger + qsLogger: QSLogger, + private val keyguardDismissUtil: KeyguardDismissUtil, + private val keyguardStateController: KeyguardStateController, + private val dialogLaunchAnimator: DialogLaunchAnimator, + private val sysuiDialogFactory: SystemUIDialog.Factory, ) : QSTileImpl<QSTile.BooleanState>( host, @@ -76,11 +88,41 @@ constructor( handlesLongClick = false } - override fun handleClick(view: View?) { - isRecording = !isRecording + @VisibleForTesting + public override fun handleClick(view: View?) { + if (isRecording) { + isRecording = false + } else { + mUiHandler.post { showPrompt(view) } + } refreshState() } + private fun showPrompt(view: View?) { + val dialog: AlertDialog = + RecordIssueDialogDelegate(sysuiDialogFactory) { + isRecording = true + refreshState() + } + .createDialog() + val dismissAction = + ActivityStarter.OnDismissAction { + // We animate from the touched view only if we are not on the keyguard, given + // that if we are we will dismiss it which will also collapse the shade. + if (view != null && !keyguardStateController.isShowing) { + dialogLaunchAnimator.showFromView( + dialog, + view, + DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC) + ) + } else { + dialog.show() + } + false + } + keyguardDismissUtil.executeWhenUnlocked(dismissAction, false, true) + } + override fun getLongClickIntent(): Intent? = null @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt index 09d7a1f7142d..17b78ebf106c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt @@ -24,7 +24,7 @@ interface QSTileUserActionInteractor<DATA_TYPE> { * [QSTileInput.data]. It's guaranteed that [QSTileInput.userId] is the same as the id passed to * [QSTileDataInteractor] to get [QSTileInput.data]. * - * It's safe to run long running computations inside this function in this. + * It's safe to run long running computations inside this function. */ @WorkerThread suspend fun handleInput(input: QSTileInput<DATA_TYPE>) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt index 4a34276671c1..b325b4daeb81 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt @@ -16,6 +16,8 @@ package com.android.systemui.qs.tiles.di +import android.content.Context +import android.content.res.Resources.Theme import com.android.systemui.qs.external.CustomTileStatePersister import com.android.systemui.qs.external.CustomTileStatePersisterImpl import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler @@ -27,6 +29,7 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProviderImpl import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel import dagger.Binds import dagger.Module +import dagger.Provides import dagger.multibindings.Multibinds /** Module listing subcomponents */ @@ -57,4 +60,9 @@ interface QSTilesModule { @Binds fun bindCustomTileStatePersister(impl: CustomTileStatePersisterImpl): CustomTileStatePersister + + companion object { + + @Provides fun provideTilesTheme(context: Context): Theme = context.theme + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java index 2350b5dce8b8..9d214e7141a8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java @@ -59,12 +59,12 @@ import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils; import com.android.systemui.Prefs; -import com.android.systemui.res.R; import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.wifitrackerlib.WifiEntry; @@ -157,14 +157,6 @@ public class InternetDialog extends SystemUIDialog implements // Wi-Fi scanning progress bar protected boolean mIsProgressBarVisible; - protected boolean mIsSearchingHidden; - protected final Runnable mHideProgressBarRunnable = () -> { - setProgressBarVisible(false); - }; - protected Runnable mHideSearchingRunnable = () -> { - mIsSearchingHidden = true; - mInternetDialogSubTitle.setText(getSubtitleText()); - }; @Inject public InternetDialog(Context context, InternetDialogFactory internetDialogFactory, @@ -285,8 +277,6 @@ public class InternetDialog extends SystemUIDialog implements if (DEBUG) { Log.d(TAG, "onStop"); } - mHandler.removeCallbacks(mHideProgressBarRunnable); - mHandler.removeCallbacks(mHideSearchingRunnable); mMobileNetworkLayout.setOnClickListener(null); mConnectedWifListLayout.setOnClickListener(null); if (mSecondaryMobileNetworkLayout != null) { @@ -335,7 +325,6 @@ public class InternetDialog extends SystemUIDialog implements return; } - showProgressBar(); final boolean isDeviceLocked = mInternetDialogController.isDeviceLocked(); final boolean isWifiEnabled = mInternetDialogController.isWifiEnabled(); final boolean isWifiScanEnabled = mInternetDialogController.isWifiScanEnabled(); @@ -641,8 +630,7 @@ public class InternetDialog extends SystemUIDialog implements @Nullable CharSequence getSubtitleText() { - return mInternetDialogController.getSubtitleText( - mIsProgressBarVisible && !mIsSearchingHidden); + return mInternetDialogController.getSubtitleText(mIsProgressBarVisible); } private Drawable getSignalStrengthDrawable(int subId) { @@ -657,20 +645,6 @@ public class InternetDialog extends SystemUIDialog implements return mInternetDialogController.getMobileNetworkSummary(subId); } - protected void showProgressBar() { - if (!mInternetDialogController.isWifiEnabled() - || mInternetDialogController.isDeviceLocked()) { - setProgressBarVisible(false); - return; - } - setProgressBarVisible(true); - if (mConnectedWifiEntry != null || mWifiEntriesCount > 0) { - mHandler.postDelayed(mHideProgressBarRunnable, PROGRESS_DELAY_MS); - } else if (!mIsSearchingHidden) { - mHandler.postDelayed(mHideSearchingRunnable, PROGRESS_DELAY_MS); - } - } - private void setProgressBarVisible(boolean visible) { if (mIsProgressBarVisible == visible) { return; @@ -823,6 +797,11 @@ public class InternetDialog extends SystemUIDialog implements } @Override + public void onWifiScan(boolean isScan) { + setProgressBarVisible(isScan); + } + + @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (mAlertDialog != null && !mAlertDialog.isShowing()) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index f516f5521d25..592cb3b18e80 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -75,7 +75,6 @@ import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.SignalStrengthUtil; import com.android.settingslib.wifi.WifiUtils; import com.android.settingslib.wifi.dpp.WifiDppIntentHelper; -import com.android.systemui.res.R; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -84,6 +83,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.res.R; import com.android.systemui.statusbar.connectivity.AccessPointController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.LocationController; @@ -1129,6 +1129,15 @@ public class InternetDialogController implements AccessPointController.AccessPoi public void onSettingsActivityTriggered(Intent settingsIntent) { } + @Override + public void onWifiScan(boolean isScan) { + if (!isWifiEnabled() || isDeviceLocked()) { + mCallback.onWifiScan(false); + return; + } + mCallback.onWifiScan(isScan); + } + private class InternetTelephonyCallback extends TelephonyCallback implements TelephonyCallback.DataConnectionStateListener, TelephonyCallback.DisplayInfoListener, @@ -1372,6 +1381,8 @@ public class InternetDialogController implements AccessPointController.AccessPoi void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries, @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries); + + void onWifiScan(boolean isScan); } void makeOverlayToast(int stringId) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt index cfb544226c83..9b8dba166274 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.airplane.domain import android.content.res.Resources +import android.content.res.Resources.Theme import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper @@ -27,18 +28,25 @@ import com.android.systemui.res.R import javax.inject.Inject /** Maps [AirplaneModeTileModel] to [QSTileState]. */ -class AirplaneModeMapper @Inject constructor(@Main private val resources: Resources) : - QSTileDataToStateMapper<AirplaneModeTileModel> { +class AirplaneModeMapper +@Inject +constructor( + @Main private val resources: Resources, + val theme: Theme, +) : QSTileDataToStateMapper<AirplaneModeTileModel> { override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState = - QSTileState.build(resources, config.uiConfig) { + QSTileState.build(resources, theme, config.uiConfig) { val icon = - Icon.Resource( - if (data.isEnabled) { - R.drawable.qs_airplane_icon_on - } else { - R.drawable.qs_airplane_icon_off - }, + Icon.Loaded( + resources.getDrawable( + if (data.isEnabled) { + R.drawable.qs_airplane_icon_on + } else { + R.drawable.qs_airplane_icon_off + }, + theme, + ), contentDescription = null ) this.icon = { icon } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt index 63865777e14f..e075e76595d4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.alarm.domain import android.content.res.Resources +import android.content.res.Resources.Theme import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel @@ -30,14 +31,18 @@ import java.util.TimeZone import javax.inject.Inject /** Maps [AlarmTileModel] to [QSTileState]. */ -class AlarmTileMapper @Inject constructor(@Main private val resources: Resources) : - QSTileDataToStateMapper<AlarmTileModel> { +class AlarmTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Theme, +) : QSTileDataToStateMapper<AlarmTileModel> { companion object { val formatter12Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("E hh:mm a") val formatter24Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("E HH:mm") } override fun map(config: QSTileConfig, data: AlarmTileModel): QSTileState = - QSTileState.build(resources, config.uiConfig) { + QSTileState.build(resources, theme, config.uiConfig) { when (data) { is AlarmTileModel.NextAlarmSet -> { activationState = QSTileState.ActivationState.ACTIVE diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt new file mode 100644 index 000000000000..1efbfd70fa98 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.colorcorrection.domain + +import android.content.res.Resources +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import javax.inject.Inject + +/** Maps [ColorCorrectionTileModel] to [QSTileState]. */ +class ColorCorrectionTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Resources.Theme, +) : QSTileDataToStateMapper<ColorCorrectionTileModel> { + + override fun map(config: QSTileConfig, data: ColorCorrectionTileModel): QSTileState = + QSTileState.build(resources, theme, config.uiConfig) { + val subtitleArray = resources.getStringArray(R.array.tile_states_color_correction) + + if (data.isEnabled) { + activationState = QSTileState.ActivationState.ACTIVE + secondaryLabel = subtitleArray[2] + } else { + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = subtitleArray[1] + } + contentDescription = label + supportedActions = + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt new file mode 100644 index 000000000000..cd33d451ba81 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor + +import android.os.UserHandle +import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map + +/** Observes color correction state changes providing the [ColorCorrectionTileModel]. */ +class ColorCorrectionTileDataInteractor +@Inject +constructor( + private val colorCorrectionRepository: ColorCorrectionRepository, +) : QSTileDataInteractor<ColorCorrectionTileModel> { + + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger> + ): Flow<ColorCorrectionTileModel> { + return colorCorrectionRepository.isEnabled(user).map { ColorCorrectionTileModel(it) } + } + override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt new file mode 100644 index 000000000000..d1838029db4e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor + +import android.content.Intent +import android.provider.Settings +import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import javax.inject.Inject + +/** Handles color correction tile clicks. */ +class ColorCorrectionUserActionInteractor +@Inject +constructor( + private val colorCorrectionRepository: ColorCorrectionRepository, + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, +) : QSTileUserActionInteractor<ColorCorrectionTileModel> { + + override suspend fun handleInput(input: QSTileInput<ColorCorrectionTileModel>): Unit = + with(input) { + when (action) { + is QSTileUserAction.Click -> { + colorCorrectionRepository.setIsEnabled( + !data.isEnabled, + user, + ) + } + is QSTileUserAction.LongClick -> { + qsTileIntentUserActionHandler.handle( + action.view, + Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS) + ) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/model/ColorCorrectionTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/model/ColorCorrectionTileModel.kt new file mode 100644 index 000000000000..66487e1bba60 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/model/ColorCorrectionTileModel.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.colorcorrection.domain.model + +/** + * Color correction tile model. + * + * @param isEnabled is true when the color correction is enabled; + */ +@JvmInline value class ColorCorrectionTileModel(val isEnabled: Boolean) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt index 881a6bd156d2..1b3b5848a7ce 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.flashlight.domain import android.content.res.Resources +import android.content.res.Resources.Theme import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper @@ -27,18 +28,25 @@ import com.android.systemui.res.R import javax.inject.Inject /** Maps [FlashlightTileModel] to [QSTileState]. */ -class FlashlightMapper @Inject constructor(@Main private val resources: Resources) : - QSTileDataToStateMapper<FlashlightTileModel> { +class FlashlightMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Theme, +) : QSTileDataToStateMapper<FlashlightTileModel> { override fun map(config: QSTileConfig, data: FlashlightTileModel): QSTileState = - QSTileState.build(resources, config.uiConfig) { + QSTileState.build(resources, theme, config.uiConfig) { val icon = - Icon.Resource( - if (data.isEnabled) { - R.drawable.qs_flashlight_icon_on - } else { - R.drawable.qs_flashlight_icon_off - }, + Icon.Loaded( + resources.getDrawable( + if (data.isEnabled) { + R.drawable.qs_flashlight_icon_on + } else { + R.drawable.qs_flashlight_icon_off + }, + theme, + ), contentDescription = null ) this.icon = { icon } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt index 7e7034d65efd..fe5445d00670 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.impl.location.domain import android.content.res.Resources +import android.content.res.Resources.Theme import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper @@ -27,18 +28,25 @@ import com.android.systemui.res.R import javax.inject.Inject /** Maps [LocationTileModel] to [QSTileState]. */ -class LocationTileMapper @Inject constructor(@Main private val resources: Resources) : - QSTileDataToStateMapper<LocationTileModel> { +class LocationTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Theme, +) : QSTileDataToStateMapper<LocationTileModel> { override fun map(config: QSTileConfig, data: LocationTileModel): QSTileState = - QSTileState.build(resources, config.uiConfig) { + QSTileState.build(resources, theme, config.uiConfig) { val icon = - Icon.Resource( - if (data.isEnabled) { - R.drawable.qs_location_icon_on - } else { - R.drawable.qs_location_icon_off - }, + Icon.Loaded( + resources.getDrawable( + if (data.isEnabled) { + R.drawable.qs_location_icon_on + } else { + R.drawable.qs_location_icon_off + }, + theme, + ), contentDescription = null ) this.icon = { icon } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt index 25b09131522b..df25600228a5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt @@ -27,20 +27,28 @@ import com.android.systemui.res.R import javax.inject.Inject /** Maps [DataSaverTileModel] to [QSTileState]. */ -class DataSaverTileMapper @Inject constructor(@Main private val resources: Resources) : - QSTileDataToStateMapper<DataSaverTileModel> { +class DataSaverTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Resources.Theme, +) : QSTileDataToStateMapper<DataSaverTileModel> { override fun map(config: QSTileConfig, data: DataSaverTileModel): QSTileState = - QSTileState.build(resources, config.uiConfig) { + QSTileState.build(resources, theme, config.uiConfig) { with(data) { + val iconRes: Int if (isEnabled) { activationState = QSTileState.ActivationState.ACTIVE - icon = { Icon.Resource(R.drawable.qs_data_saver_icon_on, null) } + iconRes = R.drawable.qs_data_saver_icon_on secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[2] } else { activationState = QSTileState.ActivationState.INACTIVE - icon = { Icon.Resource(R.drawable.qs_data_saver_icon_off, null) } + iconRes = R.drawable.qs_data_saver_icon_off secondaryLabel = resources.getStringArray(R.array.tile_states_saver)[1] } + val loadedIcon = + Icon.Loaded(resources.getDrawable(iconRes, theme), contentDescription = null) + icon = { loadedIcon } contentDescription = label supportedActions = setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt index 3f30c75a6b6a..ffef2b6ecfb5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles.impl.uimodenight.domain import android.app.UiModeManager import android.content.res.Resources +import android.content.res.Resources.Theme import android.text.TextUtils import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.qualifiers.Main @@ -31,15 +32,19 @@ import java.time.format.DateTimeFormatter import javax.inject.Inject /** Maps [UiModeNightTileModel] to [QSTileState]. */ -class UiModeNightTileMapper @Inject constructor(@Main private val resources: Resources) : - QSTileDataToStateMapper<UiModeNightTileModel> { +class UiModeNightTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Theme, +) : QSTileDataToStateMapper<UiModeNightTileModel> { companion object { val formatter12Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm a") val formatter24Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm") } override fun map(config: QSTileConfig, data: UiModeNightTileModel): QSTileState = with(data) { - QSTileState.build(resources, config.uiConfig) { + QSTileState.build(resources, theme, config.uiConfig) { var shouldSetSecondaryLabel = false if (isPowerSave) { @@ -116,8 +121,9 @@ class UiModeNightTileMapper @Inject constructor(@Main private val resources: Res if (activationState == QSTileState.ActivationState.ACTIVE) R.drawable.qs_light_dark_theme_icon_on else R.drawable.qs_light_dark_theme_icon_off - val iconResource = Icon.Resource(iconRes, null) - icon = { iconResource } + val loadedIcon = + Icon.Loaded(resources.getDrawable(iconRes, theme), contentDescription = null) + icon = { loadedIcon } supportedActions = if (activationState == QSTileState.ActivationState.UNAVAILABLE) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt index 23e0cb66bb6a..be1b7404314f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.viewmodel import android.content.res.Resources +import android.content.res.Resources.Theme import android.service.quicksettings.Tile import android.view.View import android.widget.Switch @@ -47,14 +48,17 @@ data class QSTileState( fun build( resources: Resources, + theme: Theme, config: QSTileUIConfig, build: Builder.() -> Unit - ): QSTileState = - build( - { Icon.Resource(config.iconRes, null) }, + ): QSTileState { + val iconDrawable = resources.getDrawable(config.iconRes, theme) + return build( + { Icon.Loaded(iconDrawable, null) }, resources.getString(config.labelRes), build, ) + } fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState = Builder(icon, label).apply(build).build() diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt new file mode 100644 index 000000000000..8221c635741b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recordissue + +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.Color +import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater +import android.view.WindowManager +import android.widget.Button +import android.widget.PopupMenu +import android.widget.Switch +import com.android.systemui.res.R +import com.android.systemui.statusbar.phone.SystemUIDialog + +class RecordIssueDialogDelegate( + private val factory: SystemUIDialog.Factory, + private val onStarted: Runnable +) : SystemUIDialog.Delegate { + + @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch + private lateinit var issueTypeButton: Button + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + dialog.apply { + setView(LayoutInflater.from(context).inflate(R.layout.record_issue_dialog, null)) + setTitle(context.getString(R.string.qs_record_issue_label)) + setIcon(R.drawable.qs_record_issue_icon_off) + setNegativeButton(R.string.cancel) { _, _ -> dismiss() } + setPositiveButton(R.string.qs_record_issue_start) { _, _ -> + onStarted.run() + dismiss() + } + } + } + + override fun createDialog(): SystemUIDialog = factory.create(this) + + override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + dialog.apply { + window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS) + window?.setGravity(Gravity.CENTER) + + screenRecordSwitch = requireViewById(R.id.screenrecord_switch) + issueTypeButton = requireViewById(R.id.issue_type_button) + issueTypeButton.setOnClickListener { onIssueTypeClicked(context) } + } + } + + private fun onIssueTypeClicked(context: Context) { + val selectedCategory = issueTypeButton.text.toString() + val popupMenu = PopupMenu(context, issueTypeButton) + + context.resources.getStringArray(R.array.qs_record_issue_types).forEachIndexed { i, cat -> + popupMenu.menu.add(0, 0, i, cat).apply { + setIcon(R.drawable.arrow_pointing_down) + if (selectedCategory != cat) { + iconTintList = ColorStateList.valueOf(Color.TRANSPARENT) + } + } + } + popupMenu.apply { + setOnMenuItemClickListener { + issueTypeButton.text = it.title + true + } + setForceShowIcon(true) + show() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt index 8a93ef65b4bf..d3459b109d79 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt @@ -32,6 +32,7 @@ import javax.inject.Inject * TODO(b/200063118): Make this class the one source of truth for the state of panel expansion. */ @SysUISingleton +@Deprecated("Use ShadeInteractor instead") class ShadeExpansionStateManager @Inject constructor() { private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>() @@ -49,6 +50,7 @@ class ShadeExpansionStateManager @Inject constructor() { * * @see #addExpansionListener */ + @Deprecated("Use ShadeInteractor instead") fun addExpansionListener(listener: ShadeExpansionListener): ShadeExpansionChangeEvent { expansionListeners.add(listener) return ShadeExpansionChangeEvent(fraction, expanded, tracking, dragDownPxAmount) @@ -60,6 +62,7 @@ class ShadeExpansionStateManager @Inject constructor() { } /** Adds a listener that will be notified when the panel state has changed. */ + @Deprecated("Use ShadeInteractor instead") fun addStateListener(listener: ShadeStateListener) { stateListeners.add(listener) } diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt index c59ef2632f15..d26fded19cc1 100644 --- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt +++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt @@ -59,6 +59,11 @@ abstract class SmartspaceModule { * The BcSmartspaceDataPlugin for the standalone weather. */ const val WEATHER_SMARTSPACE_DATA_PLUGIN = "weather_smartspace_data_plugin" + + /** + * The BcSmartspaceDataProvider for the glanceable hub. + */ + const val GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN = "glanceable_hub_smartspace_data_plugin" } @BindsOptionalOf @@ -78,4 +83,8 @@ abstract class SmartspaceModule { abstract fun bindSmartspacePrecondition( lockscreenPrecondition: LockscreenPrecondition? ): SmartspacePrecondition? + + @BindsOptionalOf + @Named(GLANCEABLE_HUB_SMARTSPACE_DATA_PLUGIN) + abstract fun optionalBcSmartspaceDataPlugin(): BcSmartspaceDataPlugin? } diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt index 2fc0ec290a90..095d30ef55df 100644 --- a/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt @@ -19,9 +19,9 @@ package com.android.systemui.smartspace.data.repository import android.app.smartspace.SmartspaceTarget import android.os.Parcelable import android.widget.RemoteViews +import com.android.systemui.communal.smartspace.CommunalSmartspaceController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.BcSmartspaceDataPlugin -import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -32,37 +32,37 @@ interface SmartspaceRepository { /** Whether [RemoteViews] are passed through smartspace targets. */ val isSmartspaceRemoteViewsEnabled: Boolean - /** Smartspace targets for the lockscreen surface. */ - val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> + /** Smartspace targets for the communal surface. */ + val communalSmartspaceTargets: Flow<List<SmartspaceTarget>> } @SysUISingleton class SmartspaceRepositoryImpl @Inject constructor( - private val lockscreenSmartspaceController: LockscreenSmartspaceController, + private val communalSmartspaceController: CommunalSmartspaceController, ) : SmartspaceRepository, BcSmartspaceDataPlugin.SmartspaceTargetListener { override val isSmartspaceRemoteViewsEnabled: Boolean get() = android.app.smartspace.flags.Flags.remoteViews() - private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> = + private val _communalSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> = MutableStateFlow(emptyList()) - override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> = - _lockscreenSmartspaceTargets + override val communalSmartspaceTargets: Flow<List<SmartspaceTarget>> = + _communalSmartspaceTargets .onStart { - lockscreenSmartspaceController.addListener(listener = this@SmartspaceRepositoryImpl) + communalSmartspaceController.addListener(listener = this@SmartspaceRepositoryImpl) } .onCompletion { - lockscreenSmartspaceController.removeListener( + communalSmartspaceController.removeListener( listener = this@SmartspaceRepositoryImpl ) } override fun onSmartspaceTargetsUpdated(targetsNullable: MutableList<out Parcelable>?) { targetsNullable?.let { targets -> - _lockscreenSmartspaceTargets.value = targets.filterIsInstance<SmartspaceTarget>() + _communalSmartspaceTargets.value = targets.filterIsInstance<SmartspaceTarget>() } - ?: run { _lockscreenSmartspaceTargets.value = emptyList() } + ?: run { _communalSmartspaceTargets.value = emptyList() } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt index 490994d805fe..fc474d2a1307 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt @@ -89,5 +89,12 @@ interface AccessPointController { * "wifi_start_connect_ssid" set as an extra */ fun onSettingsActivityTriggered(settingsIntent: Intent?) + + /** + * Called whenever a Wi-Fi scan is triggered. + * + * @param isScan Whether Wi-Fi scan is triggered or not. + */ + fun onWifiScan(isScan: Boolean) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java index 91ca148c93c9..3a31851bcb4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java @@ -213,6 +213,12 @@ public class AccessPointControllerImpl implements AccessPointController, } } + private void fireWifiScanCallback(boolean isScan) { + for (AccessPointCallback callback : mCallbacks) { + callback.onWifiScan(isScan); + } + } + void dump(PrintWriter pw) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw); ipw.println("AccessPointControllerImpl:"); @@ -240,6 +246,14 @@ public class AccessPointControllerImpl implements AccessPointController, } @Override + public void onWifiEntriesChanged(@WifiPickerTracker.WifiEntriesChangedReason int reason) { + onWifiEntriesChanged(); + if (reason == WifiPickerTracker.WIFI_ENTRIES_CHANGED_REASON_SCAN_RESULTS) { + fireWifiScanCallback(false /* isScan */); + } + } + + @Override public void onNumSavedNetworksChanged() { // Do nothing } @@ -249,6 +263,11 @@ public class AccessPointControllerImpl implements AccessPointController, // Do nothing } + @Override + public void onScanRequested() { + fireWifiScanCallback(true /* isScan */); + } + private final WifiEntry.ConnectCallback mConnectCallback = new WifiEntry.ConnectCallback() { @Override public void onConnectResult(int status) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt index 92391e7c76f0..e1e30e1d74f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.notification.icon.ui.viewbinder import android.graphics.Color import android.graphics.Rect +import android.util.Log import android.view.View +import android.view.ViewGroup import android.widget.FrameLayout import androidx.annotation.ColorInt import androidx.collection.ArrayMap @@ -220,7 +222,7 @@ object NotificationIconContainerViewBinder { notifyBindingFailures: (Collection<String>) -> Unit, viewStore: IconViewStore, bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> }, - ): Unit = coroutineScope { + ) { val iconSizeFlow: Flow<Int> = configuration.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_icon_size_sp, @@ -235,6 +237,21 @@ object NotificationIconContainerViewBinder { -> FrameLayout.LayoutParams(iconSize + 2 * iconHPadding, statusBarHeight) } + try { + bindIcons(view, layoutParams, notifyBindingFailures, viewStore, bindIcon) + } finally { + // Detach everything so that child SBIVs don't hold onto a reference to the container. + view.detachAllIcons() + } + } + + private suspend fun Flow<NotificationIconsViewData>.bindIcons( + view: NotificationIconContainer, + layoutParams: Flow<FrameLayout.LayoutParams>, + notifyBindingFailures: (Collection<String>) -> Unit, + viewStore: IconViewStore, + bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit, + ): Unit = coroutineScope { val failedBindings = mutableSetOf<String>() val boundViewsByNotifKey = ArrayMap<String, Pair<StatusBarIconView, Job>>() var prevIcons = NotificationIconsViewData() @@ -266,9 +283,17 @@ object NotificationIconContainerViewBinder { continue } failedBindings.remove(notifKey) - // The view might still be transiently added if it was just removed and added - // again - view.removeTransientView(sbiv) + (sbiv.parent as? ViewGroup)?.run { + if (this !== view) { + Log.wtf(TAG, "StatusBarIconView($notifKey) has an unexpected parent") + } + // If the container was re-inflated and re-bound, then SBIVs might still be + // attached to the prior view. + removeView(sbiv) + // The view might still be transiently added if it was just removed and + // added again. + removeTransientView(sbiv) + } view.addView(sbiv, idx) boundViewsByNotifKey.remove(notifKey)?.second?.cancel() boundViewsByNotifKey[notifKey] = @@ -351,7 +376,8 @@ object NotificationIconContainerViewBinder { fun iconView(key: String): StatusBarIconView? } - @ColorInt private val DEFAULT_AOD_ICON_COLOR = Color.WHITE + @ColorInt private const val DEFAULT_AOD_ICON_COLOR = Color.WHITE + private const val TAG = "NotifIconContainerViewBinder" } /** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt index adf6cca1ac65..625fdc1c12f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt @@ -20,6 +20,8 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import android.content.Context import com.android.systemui.common.ui.data.repository.ConfigurationRepository import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.res.R import com.android.systemui.statusbar.policy.SplitShadeStateController import javax.inject.Inject @@ -28,6 +30,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart @@ -39,7 +42,9 @@ class SharedNotificationContainerInteractor constructor( configurationRepository: ConfigurationRepository, private val context: Context, - private val splitShadeStateController: SplitShadeStateController + private val splitShadeStateController: SplitShadeStateController, + keyguardInteractor: KeyguardInteractor, + deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, ) { private val _topPosition = MutableStateFlow(0f) @@ -75,6 +80,19 @@ constructor( } .distinctUntilChanged() + /** + * The notification shelf can extend over the lock icon area if: + * * UDFPS supported. Ambient indication will always appear below + * * UDFPS not supported and ambient indication not visible, which will appear above lock icon + */ + val useExtraShelfSpace: Flow<Boolean> = + combine( + keyguardInteractor.ambientIndicationVisible, + deviceEntryUdfpsInteractor.isUdfpsSupported, + ) { ambientIndicationVisible, isUdfpsSupported -> + isUdfpsSupported || !ambientIndicationVisible + } + val isSplitShadeEnabled: Flow<Boolean> = configurationBasedDimensions .map { dimens: ConfigurationBasedDimensions -> dimens.useSplitShade } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt index af56a3f51281..12927b87630e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt @@ -101,12 +101,13 @@ object SharedNotificationContainerBinder { launch { viewModel - .getMaxNotifications { space -> + .getMaxNotifications { space, extraShelfSpace -> + val shelfHeight = controller.getShelfHeight().toFloat() notificationStackSizeCalculator.computeMaxKeyguardNotifications( controller.getView(), space, - 0f, // Vertical space for shelf is already accounted for - controller.getShelfHeight().toFloat(), + if (extraShelfSpace) shelfHeight else 0f, + shelfHeight, ) } .collect { controller.setMaxDisplayedNotifications(it) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 9594bc3bfd86..eff91e55d9a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -229,7 +229,7 @@ constructor( * When expanding or when the user is interacting with the shade, keep the count stable; do not * emit a value. */ - fun getMaxNotifications(calculateSpace: (Float) -> Int): Flow<Int> { + fun getMaxNotifications(calculateSpace: (Float, Boolean) -> Int): Flow<Int> { val showLimitedNotifications = isOnLockscreenWithoutShade val showUnlimitedNotifications = combine( @@ -245,11 +245,17 @@ constructor( shadeInteractor.isUserInteracting, bounds, interactor.notificationStackChanged.onStart { emit(Unit) }, - ) { showLimitedNotifications, showUnlimitedNotifications, isUserInteracting, bounds, _ - -> + interactor.useExtraShelfSpace, + ) { flows -> + val showLimitedNotifications = flows[0] as Boolean + val showUnlimitedNotifications = flows[1] as Boolean + val isUserInteracting = flows[2] as Boolean + val bounds = flows[3] as NotificationContainerBounds + val useExtraShelfSpace = flows[5] as Boolean + if (!isUserInteracting) { if (showLimitedNotifications) { - emit(calculateSpace(bounds.bottom - bounds.top)) + emit(calculateSpace(bounds.bottom - bounds.top, useExtraShelfSpace)) } else if (showUnlimitedNotifications) { emit(-1) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 00e78a49ba19..0dabafbdecb0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -400,6 +400,21 @@ public class NotificationIconContainer extends ViewGroup { } } + /** + * Removes all child {@link StatusBarIconView} instances from this container, immediately and + * without animation. This should be called when tearing down this container so that external + * icon views are not holding onto a reference thru {@link View#getParent()}. + */ + public void detachAllIcons() { + boolean animsWereEnabled = mAnimationsEnabled; + boolean wasChangingPositions = mChangingViewPositions; + mAnimationsEnabled = false; + mChangingViewPositions = true; + removeAllViews(); + mChangingViewPositions = wasChangingPositions; + mAnimationsEnabled = animsWereEnabled; + } + public boolean areIconsOverflowing() { return mIsShowingOverflowDot; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index 756c440eca89..0c5472f0ecfb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -41,6 +41,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.log.core.LogLevel; import com.android.systemui.res.R; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; @@ -49,6 +50,7 @@ import dagger.Lazy; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Objects; +import java.util.function.Consumer; import javax.inject.Inject; @@ -210,23 +212,33 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum private void notifyKeyguardChanged() { Trace.beginSection("KeyguardStateController#notifyKeyguardChanged"); // Copy the list to allow removal during callback. - new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged); + invokeForEachCallback(Callback::onKeyguardShowingChanged); Trace.endSection(); } private void notifyKeyguardFaceAuthEnabledChanged() { + invokeForEachCallback(Callback::onFaceEnrolledChanged); + } + + private void invokeForEachCallback(Consumer<Callback> consumer) { // Copy the list to allow removal during callback. - new ArrayList<>(mCallbacks).forEach(callback -> { + ArrayList<Callback> copyOfCallbacks = new ArrayList<>(mCallbacks); + for (int i = 0; i < copyOfCallbacks.size(); i++) { + Callback callback = copyOfCallbacks.get(i); + // Temporary fix for b/315731775, callback is null even though only non-null callbacks + // are added to the list by addCallback if (callback != null) { - callback.onFaceEnrolledChanged(); + consumer.accept(callback); + } else { + mLogger.log("KeyguardStateController callback is null", LogLevel.DEBUG); } - }); + } } private void notifyUnlockedChanged() { Trace.beginSection("KeyguardStateController#notifyUnlockedChanged"); // Copy the list to allow removal during callback. - new ArrayList<>(mCallbacks).forEach(Callback::onUnlockedChanged); + invokeForEachCallback(Callback::onUnlockedChanged); Trace.endSection(); } @@ -242,10 +254,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardFadingAway", keyguardFadingAway ? 1 : 0); mKeyguardFadingAway = keyguardFadingAway; - ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks); - for (int i = 0; i < callbacks.size(); i++) { - callbacks.get(i).onKeyguardFadingAwayChanged(); - } + invokeForEachCallback(Callback::onKeyguardFadingAwayChanged); } } @@ -359,7 +368,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardGoingAway", keyguardGoingAway ? 1 : 0); mKeyguardGoingAway = keyguardGoingAway; - new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardGoingAwayChanged); + invokeForEachCallback(Callback::onKeyguardGoingAwayChanged); } } @@ -368,7 +377,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum if (mPrimaryBouncerShowing != showing) { mPrimaryBouncerShowing = showing; - new ArrayList<>(mCallbacks).forEach(Callback::onPrimaryBouncerShowingChanged); + invokeForEachCallback(Callback::onPrimaryBouncerShowingChanged); } } @@ -392,13 +401,13 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum boolean dismissingFromTouch) { mDismissAmount = dismissAmount; mDismissingFromTouch = dismissingFromTouch; - new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardDismissAmountChanged); + invokeForEachCallback(Callback::onKeyguardDismissAmountChanged); } @Override public void setLaunchTransitionFadingAway(boolean fadingAway) { mLaunchTransitionFadingAway = fadingAway; - new ArrayList<>(mCallbacks).forEach(Callback::onLaunchTransitionFadingAwayChanged); + invokeForEachCallback(Callback::onLaunchTransitionFadingAwayChanged); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 886fa70d715d..2b9ad50c1257 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -18,8 +18,8 @@ package com.android.systemui.theme; import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; import static com.android.systemui.Flags.themeOverlayControllerWakefulnessDeprecation; +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_HOME; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_LOCK; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET; @@ -364,15 +364,23 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - boolean newWorkProfile = Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction()); - boolean isManagedProfile = mUserManager.isManagedProfile( - intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); - if (newWorkProfile) { - if (!mDeviceProvisionedController.isCurrentUserSetup() && isManagedProfile) { + boolean newProfile = Intent.ACTION_PROFILE_ADDED.equals(intent.getAction()); + if (newProfile) { + UserHandle newUserHandle = intent.getParcelableExtra(Intent.EXTRA_USER, + android.os.UserHandle.class); + boolean isManagedProfile = + mUserManager.isManagedProfile(newUserHandle.getIdentifier()); + if (!mDeviceProvisionedController.isUserSetup(newUserHandle.getIdentifier()) + && isManagedProfile) { Log.i(TAG, "User setup not finished when " + intent.getAction() + " was received. Deferring... Managed profile? " + isManagedProfile); return; } + if (android.os.Flags.allowPrivateProfile() && isPrivateProfile(newUserHandle)) { + mDeferredThemeEvaluation = true; + Log.i(TAG, "Deferring theme for private profile till user setup is complete"); + return; + } if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added."); reevaluateSystemTheme(true /* forceReload */); } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(intent.getAction())) { @@ -432,7 +440,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { public void start() { if (DEBUG) Log.d(TAG, "Start"); final IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); + filter.addAction(Intent.ACTION_PROFILE_ADDED); filter.addAction(Intent.ACTION_WALLPAPER_CHANGED); mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor, UserHandle.ALL); @@ -608,6 +616,15 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { return new FabricatedOverlay.Builder("com.android.systemui", name, "android").build(); } + @VisibleForTesting + protected boolean isPrivateProfile(UserHandle userHandle) { + Context usercontext = mContext.createContextAsUser(userHandle,0); + if (usercontext.getSystemService(UserManager.class).isPrivateProfile()) { + return true; + } + return false; + } + private void createOverlays(int color) { boolean nightMode = isNightMode(); mColorScheme = new ColorScheme(color, nightMode, mThemeStyle); @@ -784,7 +801,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { Set<UserHandle> managedProfiles = new HashSet<>(); for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) { - if (userInfo.isManagedProfile()) { + if (userInfo.isProfile()) { managedProfiles.add(userInfo.getUserHandle()); } } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Log.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Log.kt new file mode 100644 index 000000000000..2f6c450e924c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Log.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.kotlin + +import android.util.Log + +/** Logs message at [Log.DEBUG] level. Won't call the lambda if [DEBUG] is not loggable. */ +inline fun logD(tag: String, messageLambda: () -> String) { + if (Log.isLoggable(tag, Log.DEBUG)) { + Log.d(tag, messageLambda.invoke()) + } +} + +/** Logs message at [Log.VERBOSE] level. Won't call the lambda if [VERBOSE] is not loggable. */ +inline fun logV(tag: String, messageLambda: () -> String) { + if (Log.isLoggable(tag, Log.VERBOSE)) { + Log.v(tag, messageLambda.invoke()) + } +} + +/** Logs message at [Log.INFO] level. Won't call the lambda if [INFO] is not loggable. */ +inline fun logI(tag: String, messageLambda: () -> String) { + if (Log.isLoggable(tag, Log.INFO)) { + Log.i(tag, messageLambda.invoke()) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index ea20d29556dc..91219f02818c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -47,12 +47,14 @@ import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorI import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl +import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel import com.android.systemui.display.data.repository.FakeDisplayRepository import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.events.ANIMATING_OUT +import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -101,6 +103,12 @@ open class AuthContainerViewTest : SysuiTestCase() { lateinit var interactionJankMonitor: InteractionJankMonitor @Mock lateinit var vibrator: VibratorHelper + @Mock + lateinit var udfpsUtils: UdfpsUtils + @Mock + lateinit var authController: AuthController + @Mock + lateinit var selectedUserInteractor: SelectedUserInteractor private val testScope = TestScope(StandardTestDispatcher()) private val fakeExecutor = FakeExecutor(FakeSystemClock()) @@ -123,6 +131,7 @@ open class AuthContainerViewTest : SysuiTestCase() { private lateinit var displayRepository: FakeDisplayRepository private lateinit var displayStateInteractor: DisplayStateInteractor + private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor) @@ -140,6 +149,12 @@ open class AuthContainerViewTest : SysuiTestCase() { displayStateRepository, displayRepository, ) + udfpsOverlayInteractor = + UdfpsOverlayInteractor( + authController, + selectedUserInteractor, + testScope.backgroundScope, + ) } @After @@ -532,6 +547,8 @@ open class AuthContainerViewTest : SysuiTestCase() { displayStateInteractor, promptSelectorInteractor, context, + udfpsOverlayInteractor, + udfpsUtils ), { credentialViewModel }, Handler(TestableLooper.get(this).looper), diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt index 22e3e7fedda0..74c43131b955 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.biometrics.shared.model +import android.hardware.fingerprint.FingerprintSensorProperties import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.faceSensorPropertiesInternal @@ -35,6 +36,46 @@ class BiometricModalitiesTest : SysuiTestCase() { } @Test + fun hasUdfps() { + with( + BiometricModalities( + fingerprintProperties = fingerprintSensorPropertiesInternal( + sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL + ).first(), + ) + ) { + assertThat(isEmpty).isFalse() + assertThat(hasUdfps).isTrue() + assertThat(hasSfps).isFalse() + assertThat(hasFace).isFalse() + assertThat(hasFaceOnly).isFalse() + assertThat(hasFingerprint).isTrue() + assertThat(hasFingerprintOnly).isTrue() + assertThat(hasFaceAndFingerprint).isFalse() + } + } + + @Test + fun hasSfps() { + with( + BiometricModalities( + fingerprintProperties = fingerprintSensorPropertiesInternal( + sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON + ).first(), + ) + ) { + assertThat(isEmpty).isFalse() + assertThat(hasUdfps).isFalse() + assertThat(hasSfps).isTrue() + assertThat(hasFace).isFalse() + assertThat(hasFaceOnly).isFalse() + assertThat(hasFingerprint).isTrue() + assertThat(hasFingerprintOnly).isTrue() + assertThat(hasFaceAndFingerprint).isFalse() + } + } + + @Test fun fingerprintOnly() { with( BiometricModalities( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index d06cbbb5e433..7475235cfeae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.biometrics.ui.viewmodel import android.content.res.Configuration +import android.graphics.Point import android.hardware.biometrics.PromptInfo import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorProperties @@ -25,7 +26,10 @@ import android.view.HapticFeedbackConstants import android.view.MotionEvent import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils +import com.android.systemui.Flags.FLAG_BP_TALKBACK import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.AuthController +import com.android.systemui.biometrics.UdfpsUtils import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakePromptRepository @@ -33,6 +37,7 @@ import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl +import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.extractAuthenticatorTypes import com.android.systemui.biometrics.faceSensorPropertiesInternal import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal @@ -45,8 +50,10 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.display.data.repository.FakeDisplayRepository import com.android.systemui.res.R -import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -77,7 +84,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @JvmField @Rule var mockitoRule = MockitoJUnit.rule() @Mock private lateinit var lockPatternUtils: LockPatternUtils - @Mock private lateinit var vibrator: VibratorHelper + @Mock private lateinit var authController: AuthController + @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor + @Mock private lateinit var udfpsUtils: UdfpsUtils private val fakeExecutor = FakeExecutor(FakeSystemClock()) private val testScope = TestScope() @@ -87,6 +96,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa private lateinit var displayStateRepository: FakeDisplayStateRepository private lateinit var displayRepository: FakeDisplayRepository private lateinit var displayStateInteractor: DisplayStateInteractor + private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor private lateinit var selector: PromptSelectorInteractor private lateinit var viewModel: PromptViewModel @@ -116,11 +126,24 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa displayStateRepository, displayRepository, ) + udfpsOverlayInteractor = + UdfpsOverlayInteractor( + authController, + selectedUserInteractor, + testScope.backgroundScope + ) selector = PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils) selector.resetPrompt() - viewModel = PromptViewModel(displayStateInteractor, selector, mContext) + viewModel = + PromptViewModel( + displayStateInteractor, + selector, + mContext, + udfpsOverlayInteractor, + udfpsUtils + ) iconViewModel = viewModel.iconViewModel } @@ -1153,6 +1176,29 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(size).isEqualTo(PromptSize.LARGE) } + @Test + fun hint_for_talkback_guidance() = runGenericTest { + mSetFlagsRule.enableFlags(FLAG_BP_TALKBACK) + val hint by collectLastValue(viewModel.accessibilityHint) + + // Touches should fall outside of sensor area + whenever(udfpsUtils.getTouchInNativeCoordinates(any(), any(), any())) + .thenReturn(Point(0, 0)) + whenever(udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any())) + .thenReturn("Direction") + + viewModel.onAnnounceAccessibilityHint( + obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER), + true + ) + + if (testCase.modalities.hasUdfps) { + assertThat(hint?.isNotBlank()).isTrue() + } else { + assertThat(hint.isNullOrBlank()).isTrue() + } + } + /** Asserts that the selected buttons are visible now. */ private suspend fun TestScope.assertButtonsVisible( tryAgain: Boolean = false, @@ -1220,14 +1266,19 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa authenticatedModality = BiometricModality.Face, ), TestCase( - fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), + fingerprint = + fingerprintSensorPropertiesInternal( + strong = true, + sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON + ) + .first(), authenticatedModality = BiometricModality.Fingerprint, ), TestCase( fingerprint = fingerprintSensorPropertiesInternal( strong = true, - sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON + sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL ) .first(), authenticatedModality = BiometricModality.Fingerprint, @@ -1264,19 +1315,29 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa TestCase( face = faceSensorPropertiesInternal(strong = true).first(), fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), - authenticatedModality = BiometricModality.Fingerprint, + authenticatedModality = BiometricModality.Face, + confirmationRequested = true, ), TestCase( face = faceSensorPropertiesInternal(strong = true).first(), - fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), - authenticatedModality = BiometricModality.Face, + fingerprint = + fingerprintSensorPropertiesInternal( + strong = true, + sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON + ) + .first(), + authenticatedModality = BiometricModality.Fingerprint, confirmationRequested = true, ), TestCase( face = faceSensorPropertiesInternal(strong = true).first(), - fingerprint = fingerprintSensorPropertiesInternal(strong = true).first(), + fingerprint = + fingerprintSensorPropertiesInternal( + strong = true, + sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL + ) + .first(), authenticatedModality = BiometricModality.Fingerprint, - confirmationRequested = true, ), ) } @@ -1309,6 +1370,9 @@ internal data class TestCase( else -> false } + val modalities: BiometricModalities + get() = BiometricModalities(fingerprint, face) + val authenticatedByFingerprint: Boolean get() = authenticatedModality == BiometricModality.Fingerprint diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java index 220718027eee..07c980bb6656 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java @@ -21,14 +21,13 @@ import static com.android.systemui.controls.dagger.ControlsComponent.Visibility. import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ComponentName; -import android.content.Context; import android.content.res.Resources; import android.testing.AndroidTestingRunner; import android.view.View; @@ -48,6 +47,7 @@ import com.android.systemui.controls.management.ControlsListingController; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.shared.condition.Monitor; +import com.android.systemui.statusbar.policy.ConfigurationController; import org.junit.Before; import org.junit.Test; @@ -71,9 +71,6 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { private DreamOverlayStateController mDreamOverlayStateController; @Mock - private Context mContext; - - @Mock private Resources mResources; @Mock @@ -100,6 +97,9 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { @Mock private UiEventLogger mUiEventLogger; + @Mock + private ConfigurationController mConfigurationController; + @Captor private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor; @@ -109,7 +109,8 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { public void setup() { MockitoAnnotations.initMocks(this); - when(mContext.getString(anyInt())).thenReturn(""); + mContext.ensureTestableResources(); + when(mControlsComponent.getControlsController()).thenReturn( Optional.of(mControlsController)); when(mControlsComponent.getControlsListingController()).thenReturn( @@ -225,6 +226,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { mHomeControlsView, mActivityStarter, mContext, + mConfigurationController, mControlsComponent, mUiEventLogger); viewController.onViewAttached(); @@ -237,6 +239,24 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase { verify(mUiEventLogger).log(DreamOverlayUiEvent.DREAM_HOME_CONTROLS_TAPPED); } + @Test + public void testUnregistersConfigurationCallback() { + final DreamHomeControlsComplication.DreamHomeControlsChipViewController viewController = + new DreamHomeControlsComplication.DreamHomeControlsChipViewController( + mHomeControlsView, + mActivityStarter, + mContext, + mConfigurationController, + mControlsComponent, + mUiEventLogger); + viewController.onViewAttached(); + verify(mConfigurationController).addCallback(any()); + verify(mConfigurationController, never()).removeCallback(any()); + + viewController.onViewDetached(); + verify(mConfigurationController).removeCallback(any()); + } + private void setHaveFavorites(boolean value) { final List<StructureInfo> favorites = mock(List.class); when(favorites.isEmpty()).thenReturn(!value); diff --git a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt index e931384fd61e..65f68f9df3e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt @@ -22,6 +22,7 @@ import android.provider.Settings import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.LayoutInflater +import android.view.View import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -39,6 +40,8 @@ import com.android.systemui.util.time.FakeSystemClock import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.eq import org.mockito.Mockito.verify @@ -73,13 +76,20 @@ class ContrastDialogDelegateTest : SysuiTestCase() { if (Looper.myLooper() == null) Looper.prepare() mContrastDialogDelegate = - ContrastDialogDelegate( - sysuiDialogFactory, - mainExecutor, - mockUiModeManager, - mockUserTracker, - mockSecureSettings - ) + ContrastDialogDelegate( + sysuiDialogFactory, + mainExecutor, + mockUiModeManager, + mockUserTracker, + mockSecureSettings + ) + + mContrastDialogDelegate.createDialog() + val viewCaptor = ArgumentCaptor.forClass(View::class.java) + verify(sysuiDialog).setView(viewCaptor.capture()) + whenever(sysuiDialog.requireViewById(anyInt()) as View?).then { + viewCaptor.value.requireViewById(it.getArgument(0)) + } } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index ae5f625b1c8d..4ef18cf1a15f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -93,6 +93,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.flags.SystemPropertiesHelper; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.log.SessionTracker; import com.android.systemui.navigationbar.NavigationModeController; @@ -190,6 +191,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock ShadeInteractor mShadeInteractor; private @Mock ShadeWindowLogger mShadeWindowLogger; private @Mock SelectedUserInteractor mSelectedUserInteractor; + private @Mock KeyguardInteractor mKeyguardInteractor; private @Captor ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallback; private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback> @@ -1131,7 +1133,8 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { () -> mDreamingToLockscreenTransitionViewModel, mSystemPropertiesHelper, () -> mock(WindowManagerLockscreenVisibilityManager.class), - mSelectedUserInteractor); + mSelectedUserInteractor, + mKeyguardInteractor); mViewMediator.start(); mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt index 4ab8e28bc232..6eb95bddaf53 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt @@ -137,7 +137,6 @@ class KeyguardFaceAuthInteractorTest : SysuiTestCase() { mContext, testScope.backgroundScope, dispatcher, - dispatcher, faceAuthRepository, { PrimaryBouncerInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 687800714e05..459a74c82da4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -184,6 +184,13 @@ class KeyguardRootViewModelTest : SysuiTestCase() { } @Test + fun translationYInitialValueIsZero() = + testScope.runTest { + val translationY by collectLastValue(underTest.translationY) + assertThat(translationY).isEqualTo(0) + } + + @Test fun translationAndScaleFromBurnInNotDozing() = testScope.runTest { val translationX by collectLastValue(underTest.translationX) diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt index 119ffd28bebe..ebd34de463f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt @@ -28,7 +28,6 @@ import android.os.UserManager import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import com.android.dx.mockito.inline.extended.ExtendedMockito -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon @@ -40,6 +39,7 @@ import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.C import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEntryPoint import com.android.systemui.notetask.NoteTaskInfoResolver +import com.android.systemui.res.R import com.android.systemui.stylus.StylusManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -134,12 +134,9 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { // region lockScreenState @Test fun lockScreenState_stylusUsed_userUnlocked_isSelected_shouldEmitVisible() = runTest { - TestConfig() - .setStylusEverUsed(true) - .setUserUnlocked(true) - .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>()) - val underTest = createUnderTest() + TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(underTest) + val actual by collectLastValue(underTest.lockScreenState) assertThat(actual).isEqualTo(createLockScreenStateVisible()) @@ -148,10 +145,11 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { @Test fun lockScreenState_stylusUsed_userUnlocked_isSelected_noDefaultNotesAppSet_shouldEmitHidden() = runTest { + val underTest = createUnderTest() TestConfig() .setStylusEverUsed(true) .setUserUnlocked(true) - .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>()) + .setConfigSelections(underTest) whenever( roleManager.getRoleHoldersAsUser( eq(RoleManager.ROLE_NOTES), @@ -160,7 +158,6 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { ) .thenReturn(emptyList()) - val underTest = createUnderTest() val actual by collectLastValue(underTest.lockScreenState) assertThat(actual).isEqualTo(LockScreenState.Hidden) @@ -168,12 +165,9 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { @Test fun lockScreenState_stylusUnused_userUnlocked_isSelected_shouldEmitHidden() = runTest { - TestConfig() - .setStylusEverUsed(false) - .setUserUnlocked(true) - .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>()) - val underTest = createUnderTest() + TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections(underTest) + val actual by collectLastValue(underTest.lockScreenState) assertThat(actual).isEqualTo(LockScreenState.Hidden) @@ -181,25 +175,22 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { @Test fun lockScreenState_stylusUsed_userLocked_isSelected_shouldEmitHidden() = runTest { - TestConfig() - .setStylusEverUsed(true) - .setUserUnlocked(false) - .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>()) - val underTest = createUnderTest() + TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections(underTest) + val actual by collectLastValue(underTest.lockScreenState) assertThat(actual).isEqualTo(LockScreenState.Hidden) } @Test - fun lockScreenState_stylusUsed_userUnlocked_noSelected_shouldEmitVisible() = runTest { + fun lockScreenState_stylusUsed_userUnlocked_noSelected_shouldEmitHidden() = runTest { TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections() val underTest = createUnderTest() val actual by collectLastValue(underTest.lockScreenState) - assertThat(actual).isEqualTo(createLockScreenStateVisible()) + assertThat(actual).isEqualTo(LockScreenState.Hidden) } @Test @@ -223,13 +214,13 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun lockScreenState_stylusUsed_userUnlocked_customSelections_shouldEmitVisible() = runTest { + fun lockScreenState_stylusUsed_userUnlocked_customSelections_shouldEmitHidden() = runTest { TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(mock()) val underTest = createUnderTest() val actual by collectLastValue(underTest.lockScreenState) - assertThat(actual).isEqualTo(createLockScreenStateVisible()) + assertThat(actual).isEqualTo(LockScreenState.Hidden) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt index 0a8c0ab9817d..e4432f3038bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt @@ -31,18 +31,20 @@ import android.permission.PermissionGroupUsage import android.permission.PermissionManager import android.testing.AndroidTestingRunner import android.view.View +import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.animation.LaunchableView import com.android.systemui.appops.AppOpsController import com.android.systemui.plugins.ActivityStarter import com.android.systemui.privacy.logging.PrivacyLogger import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -56,12 +58,12 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -83,60 +85,48 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { private val TEST_INTENT = Intent("test_intent_action") } - @Mock - private lateinit var dialog: PrivacyDialogV2 - @Mock - private lateinit var permissionManager: PermissionManager - @Mock - private lateinit var packageManager: PackageManager - @Mock - private lateinit var privacyItemController: PrivacyItemController - @Mock - private lateinit var userTracker: UserTracker - @Mock - private lateinit var activityStarter: ActivityStarter - @Mock - private lateinit var privacyLogger: PrivacyLogger - @Mock - private lateinit var keyguardStateController: KeyguardStateController - @Mock - private lateinit var appOpsController: AppOpsController + @Mock private lateinit var dialog: PrivacyDialogV2 + @Mock private lateinit var permissionManager: PermissionManager + @Mock private lateinit var packageManager: PackageManager + @Mock private lateinit var privacyItemController: PrivacyItemController + @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var activityStarter: ActivityStarter + @Mock private lateinit var privacyLogger: PrivacyLogger + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock private lateinit var appOpsController: AppOpsController @Captor private lateinit var dialogDismissedCaptor: ArgumentCaptor<PrivacyDialogV2.OnDialogDismissed> - @Captor - private lateinit var activityStartedCaptor: ArgumentCaptor<ActivityStarter.Callback> - @Captor - private lateinit var intentCaptor: ArgumentCaptor<Intent> - @Mock - private lateinit var uiEventLogger: UiEventLogger - @Mock - private lateinit var dialogLaunchAnimator: DialogLaunchAnimator + @Captor private lateinit var activityStartedCaptor: ArgumentCaptor<ActivityStarter.Callback> + @Captor private lateinit var intentCaptor: ArgumentCaptor<Intent> + @Mock private lateinit var uiEventLogger: UiEventLogger + @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator private val backgroundExecutor = FakeExecutor(FakeSystemClock()) private val uiExecutor = FakeExecutor(FakeSystemClock()) private lateinit var controller: PrivacyDialogControllerV2 private var nextUid: Int = 0 - private val dialogProvider = object : PrivacyDialogControllerV2.DialogProvider { - var list: List<PrivacyDialogV2.PrivacyElement>? = null - var manageApp: ((String, Int, Intent) -> Unit)? = null - var closeApp: ((String, Int) -> Unit)? = null - var openPrivacyDashboard: (() -> Unit)? = null - - override fun makeDialog( - context: Context, - list: List<PrivacyDialogV2.PrivacyElement>, - manageApp: (String, Int, Intent) -> Unit, - closeApp: (String, Int) -> Unit, - openPrivacyDashboard: () -> Unit - ): PrivacyDialogV2 { - this.list = list - this.manageApp = manageApp - this.closeApp = closeApp - this.openPrivacyDashboard = openPrivacyDashboard - return dialog + private val dialogProvider = + object : PrivacyDialogControllerV2.DialogProvider { + var list: List<PrivacyDialogV2.PrivacyElement>? = null + var manageApp: ((String, Int, Intent) -> Unit)? = null + var closeApp: ((String, Int) -> Unit)? = null + var openPrivacyDashboard: (() -> Unit)? = null + + override fun makeDialog( + context: Context, + list: List<PrivacyDialogV2.PrivacyElement>, + manageApp: (String, Int, Intent) -> Unit, + closeApp: (String, Int) -> Unit, + openPrivacyDashboard: () -> Unit + ): PrivacyDialogV2 { + this.list = list + this.manageApp = manageApp + this.closeApp = closeApp + this.openPrivacyDashboard = openPrivacyDashboard + return dialog + } } - } @Before fun setUp() { @@ -144,7 +134,8 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { nextUid = 0 setUpDefaultMockResponses() - controller = PrivacyDialogControllerV2( + controller = + PrivacyDialogControllerV2( permissionManager, packageManager, privacyItemController, @@ -158,7 +149,7 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { uiEventLogger, dialogLaunchAnimator, dialogProvider - ) + ) } @After @@ -197,7 +188,7 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { verify(packageManager, never()).getApplicationInfoAsUser(anyString(), anyInt(), anyInt()) backgroundExecutor.runAllReady() verify(packageManager, atLeastOnce()) - .getApplicationInfoAsUser(anyString(), anyInt(), anyInt()) + .getApplicationInfoAsUser(anyString(), anyInt(), anyInt()) } @Test @@ -208,20 +199,25 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { controller.showDialog(context) exhaustExecutors() - verify(dialogLaunchAnimator, never()).showFromView(any(), any(), any(), anyBoolean()) + verify(dialogLaunchAnimator, never()).show(any(), any(), anyBoolean()) verify(dialog).show() } @Test fun testShowDialogShowsDialogWithView() { - val view = View(context) + val parent = LinearLayout(context) + val view = + object : View(context), LaunchableView { + override fun setShouldBlockVisibilityChanges(block: Boolean) {} + } + parent.addView(view) val usage = createMockPermGroupUsage() `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) controller.showDialog(context, view) exhaustExecutors() - verify(dialogLaunchAnimator).showFromView(dialog, view) + verify(dialogLaunchAnimator).show(eq(dialog), any(), anyBoolean()) verify(dialog, never()).show() } @@ -276,7 +272,8 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { @Test fun testSingleElementInList() { - val usage = createMockPermGroupUsage( + val usage = + createMockPermGroupUsage( packageName = TEST_PACKAGE_NAME, uid = generateUidForUser(USER_ID), permissionGroupName = PERM_CAMERA, @@ -285,7 +282,7 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { isPhoneCall = false, attributionTag = null, proxyLabel = TEST_PROXY_LABEL - ) + ) `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) controller.showDialog(context) @@ -304,33 +301,38 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { assertThat(list.get(0).isPhoneCall).isFalse() assertThat(list.get(0).isService).isFalse() assertThat(list.get(0).permGroupName).isEqualTo(PERM_CAMERA) - assertThat(isIntentEqual(list.get(0).navigationIntent!!, - controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID))) - .isTrue() + assertThat( + isIntentEqual( + list.get(0).navigationIntent!!, + controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID) + ) + ) + .isTrue() } } private fun isIntentEqual(actual: Intent, expected: Intent): Boolean { return actual.action == expected.action && - actual.getStringExtra(Intent.EXTRA_PACKAGE_NAME) == + actual.getStringExtra(Intent.EXTRA_PACKAGE_NAME) == expected.getStringExtra(Intent.EXTRA_PACKAGE_NAME) && - actual.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle == + actual.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle == expected.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle } @Test fun testTwoElementsDifferentType_sorted() { - val usage_camera = createMockPermGroupUsage( + val usage_camera = + createMockPermGroupUsage( packageName = "${TEST_PACKAGE_NAME}_camera", permissionGroupName = PERM_CAMERA - ) - val usage_microphone = createMockPermGroupUsage( + ) + val usage_microphone = + createMockPermGroupUsage( packageName = "${TEST_PACKAGE_NAME}_microphone", permissionGroupName = PERM_MICROPHONE - ) - `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn( - listOf(usage_microphone, usage_camera) - ) + ) + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())) + .thenReturn(listOf(usage_microphone, usage_camera)) controller.showDialog(context) exhaustExecutors() @@ -343,17 +345,12 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { @Test fun testTwoElementsSameType_oneActive() { - val usage_active = createMockPermGroupUsage( - packageName = "${TEST_PACKAGE_NAME}_active", - isActive = true - ) - val usage_recent = createMockPermGroupUsage( - packageName = "${TEST_PACKAGE_NAME}_recent", - isActive = false - ) - `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn( - listOf(usage_recent, usage_active) - ) + val usage_active = + createMockPermGroupUsage(packageName = "${TEST_PACKAGE_NAME}_active", isActive = true) + val usage_recent = + createMockPermGroupUsage(packageName = "${TEST_PACKAGE_NAME}_recent", isActive = false) + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())) + .thenReturn(listOf(usage_recent, usage_active)) controller.showDialog(context) exhaustExecutors() @@ -364,19 +361,20 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { @Test fun testTwoElementsSameType_twoActive() { - val usage_active = createMockPermGroupUsage( + val usage_active = + createMockPermGroupUsage( packageName = "${TEST_PACKAGE_NAME}_active", isActive = true, lastAccessTimeMillis = 0L - ) - val usage_active_moreRecent = createMockPermGroupUsage( + ) + val usage_active_moreRecent = + createMockPermGroupUsage( packageName = "${TEST_PACKAGE_NAME}_active_recent", isActive = true, lastAccessTimeMillis = 1L - ) - `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn( - listOf(usage_active, usage_active_moreRecent) - ) + ) + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())) + .thenReturn(listOf(usage_active, usage_active_moreRecent)) controller.showDialog(context) exhaustExecutors() assertThat(dialogProvider.list).hasSize(2) @@ -386,24 +384,26 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { @Test fun testManyElementsSameType_bothRecent() { - val usage_recent = createMockPermGroupUsage( + val usage_recent = + createMockPermGroupUsage( packageName = "${TEST_PACKAGE_NAME}_recent", isActive = false, lastAccessTimeMillis = 0L - ) - val usage_moreRecent = createMockPermGroupUsage( + ) + val usage_moreRecent = + createMockPermGroupUsage( packageName = "${TEST_PACKAGE_NAME}_moreRecent", isActive = false, lastAccessTimeMillis = 1L - ) - val usage_mostRecent = createMockPermGroupUsage( + ) + val usage_mostRecent = + createMockPermGroupUsage( packageName = "${TEST_PACKAGE_NAME}_mostRecent", isActive = false, lastAccessTimeMillis = 2L - ) - `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn( - listOf(usage_recent, usage_mostRecent, usage_moreRecent) - ) + ) + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())) + .thenReturn(listOf(usage_recent, usage_mostRecent, usage_moreRecent)) controller.showDialog(context) exhaustExecutors() @@ -414,19 +414,12 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { @Test fun testMicAndCameraDisabled() { - val usage_camera = createMockPermGroupUsage( - permissionGroupName = PERM_CAMERA - ) - val usage_microphone = createMockPermGroupUsage( - permissionGroupName = PERM_MICROPHONE - ) - val usage_location = createMockPermGroupUsage( - permissionGroupName = PERM_LOCATION - ) - - `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn( - listOf(usage_camera, usage_location, usage_microphone) - ) + val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA) + val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE) + val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION) + + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())) + .thenReturn(listOf(usage_camera, usage_location, usage_microphone)) `when`(privacyItemController.micCameraAvailable).thenReturn(false) controller.showDialog(context) @@ -438,45 +431,29 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { @Test fun testLocationDisabled() { - val usage_camera = createMockPermGroupUsage( - permissionGroupName = PERM_CAMERA - ) - val usage_microphone = createMockPermGroupUsage( - permissionGroupName = PERM_MICROPHONE - ) - val usage_location = createMockPermGroupUsage( - permissionGroupName = PERM_LOCATION - ) - - `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn( - listOf(usage_camera, usage_location, usage_microphone) - ) + val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA) + val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE) + val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION) + + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())) + .thenReturn(listOf(usage_camera, usage_location, usage_microphone)) `when`(privacyItemController.locationAvailable).thenReturn(false) controller.showDialog(context) exhaustExecutors() assertThat(dialogProvider.list).hasSize(2) - dialogProvider.list?.forEach { - assertThat(it.type).isNotEqualTo(PrivacyType.TYPE_LOCATION) - } + dialogProvider.list?.forEach { assertThat(it.type).isNotEqualTo(PrivacyType.TYPE_LOCATION) } } @Test fun testAllIndicatorsAvailable() { - val usage_camera = createMockPermGroupUsage( - permissionGroupName = PERM_CAMERA - ) - val usage_microphone = createMockPermGroupUsage( - permissionGroupName = PERM_MICROPHONE - ) - val usage_location = createMockPermGroupUsage( - permissionGroupName = PERM_LOCATION - ) - - `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn( - listOf(usage_camera, usage_location, usage_microphone) - ) + val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA) + val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE) + val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION) + + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())) + .thenReturn(listOf(usage_camera, usage_location, usage_microphone)) `when`(privacyItemController.micCameraAvailable).thenReturn(true) `when`(privacyItemController.locationAvailable).thenReturn(true) @@ -488,19 +465,12 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { @Test fun testNoIndicatorsAvailable() { - val usage_camera = createMockPermGroupUsage( - permissionGroupName = PERM_CAMERA - ) - val usage_microphone = createMockPermGroupUsage( - permissionGroupName = PERM_MICROPHONE - ) - val usage_location = createMockPermGroupUsage( - permissionGroupName = PERM_LOCATION - ) - - `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn( - listOf(usage_camera, usage_location, usage_microphone) - ) + val usage_camera = createMockPermGroupUsage(permissionGroupName = PERM_CAMERA) + val usage_microphone = createMockPermGroupUsage(permissionGroupName = PERM_MICROPHONE) + val usage_location = createMockPermGroupUsage(permissionGroupName = PERM_LOCATION) + + `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())) + .thenReturn(listOf(usage_camera, usage_location, usage_microphone)) `when`(privacyItemController.micCameraAvailable).thenReturn(false) `when`(privacyItemController.locationAvailable).thenReturn(false) @@ -512,11 +482,9 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { @Test fun testNotCurrentUser() { - val usage_other = createMockPermGroupUsage( - uid = generateUidForUser(ENT_USER_ID + 1) - ) + val usage_other = createMockPermGroupUsage(uid = generateUidForUser(ENT_USER_ID + 1)) `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())) - .thenReturn(listOf(usage_other)) + .thenReturn(listOf(usage_other)) controller.showDialog(context) exhaustExecutors() @@ -559,9 +527,7 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { // Calls happen in val usage = createMockPermGroupUsage(uid = SYSTEM_UID, isPhoneCall = true) `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) - `when`(userTracker.userProfiles).thenReturn(listOf( - UserInfo(ENT_USER_ID, "", 0) - )) + `when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(ENT_USER_ID, "", 0))) controller.showDialog(context) exhaustExecutors() @@ -577,8 +543,12 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { exhaustExecutors() dialogProvider.manageApp?.invoke(TEST_PACKAGE_NAME, USER_ID, TEST_INTENT) - verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS, - USER_ID, TEST_PACKAGE_NAME) + verify(uiEventLogger) + .log( + PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS, + USER_ID, + TEST_PACKAGE_NAME + ) } @Test @@ -589,8 +559,12 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { exhaustExecutors() dialogProvider.closeApp?.invoke(TEST_PACKAGE_NAME, USER_ID) - verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_CLOSE_APP, - USER_ID, TEST_PACKAGE_NAME) + verify(uiEventLogger) + .log( + PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_CLOSE_APP, + USER_ID, + TEST_PACKAGE_NAME + ) } @Test @@ -629,9 +603,13 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { exhaustExecutors() dialogProvider.list?.let { list -> - assertThat(isIntentEqual(list.get(0).navigationIntent!!, - controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID))) - .isTrue() + assertThat( + isIntentEqual( + list.get(0).navigationIntent!!, + controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID) + ) + ) + .isTrue() assertThat(list.get(0).isService).isFalse() } } @@ -648,45 +626,58 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { exhaustExecutors() dialogProvider.list?.let { list -> - assertThat(isIntentEqual(list.get(0).navigationIntent!!, - controller.getDefaultManageAppPermissionsIntent( - TEST_PACKAGE_NAME, ENT_USER_ID))) - .isTrue() + assertThat( + isIntentEqual( + list.get(0).navigationIntent!!, + controller.getDefaultManageAppPermissionsIntent( + TEST_PACKAGE_NAME, + ENT_USER_ID + ) + ) + ) + .isTrue() assertThat(list.get(0).isService).isFalse() } } @Test fun testDefaultIntentOnInvalidAttributionTag() { - val usage = createMockPermGroupUsage( + val usage = + createMockPermGroupUsage( attributionTag = "INVALID_ATTRIBUTION_TAG", proxyLabel = TEST_PROXY_LABEL - ) + ) `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) controller.showDialog(context) exhaustExecutors() dialogProvider.list?.let { list -> - assertThat(isIntentEqual(list.get(0).navigationIntent!!, - controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID))) - .isTrue() + assertThat( + isIntentEqual( + list.get(0).navigationIntent!!, + controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID) + ) + ) + .isTrue() assertThat(list.get(0).isService).isFalse() } } @Test fun testServiceIntentOnCorrectSubAttribution() { - val usage = createMockPermGroupUsage( + val usage = + createMockPermGroupUsage( attributionTag = TEST_ATTRIBUTION_TAG, attributionLabel = "TEST_LABEL" - ) + ) val activityInfo = createMockActivityInfo() val resolveInfo = createMockResolveInfo(activityInfo) `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) - `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())) - .thenAnswer { resolveInfo } + `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenAnswer { + resolveInfo + } controller.showDialog(context) exhaustExecutors() @@ -694,57 +685,61 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { val navigationIntent = list.get(0).navigationIntent!! assertThat(navigationIntent.action).isEqualTo(Intent.ACTION_MANAGE_PERMISSION_USAGE) assertThat(navigationIntent.getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME)) - .isEqualTo(PERM_CAMERA) + .isEqualTo(PERM_CAMERA) assertThat(navigationIntent.getStringArrayExtra(Intent.EXTRA_ATTRIBUTION_TAGS)) - .isEqualTo(arrayOf(TEST_ATTRIBUTION_TAG.toString())) + .isEqualTo(arrayOf(TEST_ATTRIBUTION_TAG.toString())) assertThat(navigationIntent.getBooleanExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, false)) - .isTrue() + .isTrue() assertThat(list.get(0).isService).isTrue() } } @Test fun testDefaultIntentOnMissingAttributionLabel() { - val usage = createMockPermGroupUsage( - attributionTag = TEST_ATTRIBUTION_TAG - ) + val usage = createMockPermGroupUsage(attributionTag = TEST_ATTRIBUTION_TAG) val activityInfo = createMockActivityInfo() val resolveInfo = createMockResolveInfo(activityInfo) `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) - `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())) - .thenAnswer { resolveInfo } + `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenAnswer { + resolveInfo + } controller.showDialog(context) exhaustExecutors() dialogProvider.list?.let { list -> - assertThat(isIntentEqual(list.get(0).navigationIntent!!, - controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID))) - .isTrue() + assertThat( + isIntentEqual( + list.get(0).navigationIntent!!, + controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID) + ) + ) + .isTrue() assertThat(list.get(0).isService).isFalse() } } @Test fun testDefaultIntentOnIncorrectPermission() { - val usage = createMockPermGroupUsage( - attributionTag = TEST_ATTRIBUTION_TAG - ) + val usage = createMockPermGroupUsage(attributionTag = TEST_ATTRIBUTION_TAG) - val activityInfo = createMockActivityInfo( - permission = "INCORRECT_PERMISSION" - ) + val activityInfo = createMockActivityInfo(permission = "INCORRECT_PERMISSION") val resolveInfo = createMockResolveInfo(activityInfo) `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage)) - `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())) - .thenAnswer { resolveInfo } + `when`(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenAnswer { + resolveInfo + } controller.showDialog(context) exhaustExecutors() dialogProvider.list?.let { list -> - assertThat(isIntentEqual(list.get(0).navigationIntent!!, - controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID))) - .isTrue() + assertThat( + isIntentEqual( + list.get(0).navigationIntent!!, + controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID) + ) + ) + .isTrue() assertThat(list.get(0).isService).isFalse() } } @@ -758,15 +753,18 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { `when`(appOpsController.isMicMuted).thenReturn(false) `when`(packageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) - .thenAnswer { FakeApplicationInfo(it.getArgument(0)) } + .thenAnswer { FakeApplicationInfo(it.getArgument(0)) } `when`(privacyItemController.locationAvailable).thenReturn(true) `when`(privacyItemController.micCameraAvailable).thenReturn(true) - `when`(userTracker.userProfiles).thenReturn(listOf( - UserInfo(USER_ID, "", 0), - UserInfo(ENT_USER_ID, "", UserInfo.FLAG_MANAGED_PROFILE) - )) + `when`(userTracker.userProfiles) + .thenReturn( + listOf( + UserInfo(USER_ID, "", 0), + UserInfo(ENT_USER_ID, "", UserInfo.FLAG_MANAGED_PROFILE) + ) + ) `when`(keyguardStateController.isUnlocked).thenReturn(true) } @@ -781,9 +779,7 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { return user * UserHandle.PER_USER_RANGE + nextUid++ } - private fun createMockResolveInfo( - activityInfo: ActivityInfo? = null - ): ResolveInfo { + private fun createMockResolveInfo(activityInfo: ActivityInfo? = null): ResolveInfo { val resolveInfo = mock(ResolveInfo::class.java) resolveInfo.activityInfo = activityInfo return resolveInfo @@ -822,4 +818,4 @@ class PrivacyDialogControllerV2Test : SysuiTestCase() { `when`(usage.proxyLabel).thenReturn(proxyLabel) return usage } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt index d8199c527cac..e9714dc524ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt @@ -23,6 +23,7 @@ import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -30,12 +31,19 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.res.R +import com.android.systemui.statusbar.phone.KeyguardDismissUtil +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq +import org.mockito.ArgumentMatchers.isA import org.mockito.Mock +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations /** @@ -53,15 +61,22 @@ class RecordIssueTileTest : SysuiTestCase() { @Mock private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var qsLogger: QSLogger + @Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock private lateinit var dialogLauncherAnimator: DialogLaunchAnimator + @Mock private lateinit var dialogFactory: SystemUIDialog.Factory + @Mock private lateinit var dialog: SystemUIDialog + private lateinit var testableLooper: TestableLooper private lateinit var tile: RecordIssueTile @Before fun setUp() { MockitoAnnotations.initMocks(this) whenever(host.context).thenReturn(mContext) + whenever(dialogFactory.create(any())).thenReturn(dialog) - val testableLooper = TestableLooper.get(this) + testableLooper = TestableLooper.get(this) tile = RecordIssueTile( host, @@ -72,7 +87,11 @@ class RecordIssueTileTest : SysuiTestCase() { metricsLogger, statusBarStateController, activityStarter, - qsLogger + qsLogger, + keyguardDismissUtil, + keyguardStateController, + dialogLauncherAnimator, + dialogFactory ) } @@ -119,4 +138,18 @@ class RecordIssueTileTest : SysuiTestCase() { assertThat(testState.state).isEqualTo(Tile.STATE_ACTIVE) } + + @Test + fun showPrompt_shouldUseKeyguardDismissUtil_ToShowDialog() { + tile.isRecording = false + tile.handleClick(null) + testableLooper.processAllMessages() + + verify(keyguardDismissUtil) + .executeWhenUnlocked( + isA(ActivityStarter.OnDismissAction::class.java), + eq(false), + eq(true) + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java index 6dc7a064af15..b24b8773d600 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java @@ -63,13 +63,13 @@ import com.android.internal.logging.UiEventLogger; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.wifi.WifiUtils; import com.android.settingslib.wifi.dpp.WifiDppIntentHelper; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.res.R; import com.android.systemui.statusbar.connectivity.AccessPointController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.LocationController; @@ -738,6 +738,44 @@ public class InternetDialogControllerTest extends SysuiTestCase { } @Test + public void onWifiScan_isWifiEnabledFalse_callbackOnWifiScanFalse() { + reset(mInternetDialogCallback); + when(mWifiStateWorker.isWifiEnabled()).thenReturn(false); + + mInternetDialogController.onWifiScan(true); + + verify(mInternetDialogCallback).onWifiScan(false); + } + + @Test + public void onWifiScan_isDeviceLockedTrue_callbackOnWifiScanFalse() { + reset(mInternetDialogCallback); + when(mKeyguardStateController.isUnlocked()).thenReturn(false); + + mInternetDialogController.onWifiScan(true); + + verify(mInternetDialogCallback).onWifiScan(false); + } + + @Test + public void onWifiScan_onWifiScanFalse_callbackOnWifiScanFalse() { + reset(mInternetDialogCallback); + + mInternetDialogController.onWifiScan(false); + + verify(mInternetDialogCallback).onWifiScan(false); + } + + @Test + public void onWifiScan_onWifiScanTrue_callbackOnWifiScanTrue() { + reset(mInternetDialogCallback); + + mInternetDialogController.onWifiScan(true); + + verify(mInternetDialogCallback).onWifiScan(true); + } + + @Test public void setMergedCarrierWifiEnabledIfNeed_carrierProvisionsEnabled_doNothing() { when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID)) .thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java index 039e58a64eb5..916bb79d97b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java @@ -6,12 +6,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -33,9 +31,9 @@ import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.internal.logging.UiEventLogger; import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogLaunchAnimator; +import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -48,7 +46,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; @@ -613,66 +610,21 @@ public class InternetDialogTest extends SysuiTestCase { } @Test - public void showProgressBar_wifiDisabled_hideProgressBar() { - Mockito.reset(mHandler); - when(mInternetDialogController.isWifiEnabled()).thenReturn(false); - - mInternetDialog.showProgressBar(); - - assertThat(mInternetDialog.mIsProgressBarVisible).isFalse(); - verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong()); - } - - @Test - public void showProgressBar_deviceLocked_hideProgressBar() { - Mockito.reset(mHandler); - when(mInternetDialogController.isDeviceLocked()).thenReturn(true); - - mInternetDialog.showProgressBar(); - - assertThat(mInternetDialog.mIsProgressBarVisible).isFalse(); - verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong()); - } - - @Test - public void showProgressBar_wifiEnabledWithWifiEntry_showProgressBarThenHide() { - Mockito.reset(mHandler); - when(mInternetDialogController.isWifiEnabled()).thenReturn(true); + public void onWifiScan_isScanTrue_setProgressBarVisibleTrue() { + mInternetDialog.mIsProgressBarVisible = false; - mInternetDialog.showProgressBar(); + mInternetDialog.onWifiScan(true); - // Show progress bar assertThat(mInternetDialog.mIsProgressBarVisible).isTrue(); - - ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(mHandler).postDelayed(runnableCaptor.capture(), - eq(InternetDialog.PROGRESS_DELAY_MS)); - runnableCaptor.getValue().run(); - - // Then hide progress bar - assertThat(mInternetDialog.mIsProgressBarVisible).isFalse(); } @Test - public void showProgressBar_wifiEnabledWithoutWifiEntries_showProgressBarThenHideSearch() { - Mockito.reset(mHandler); - when(mInternetDialogController.isWifiEnabled()).thenReturn(true); - mInternetDialog.mConnectedWifiEntry = null; - mInternetDialog.mWifiEntriesCount = 0; + public void onWifiScan_isScanFalse_setProgressBarVisibleFalse() { + mInternetDialog.mIsProgressBarVisible = true; - mInternetDialog.showProgressBar(); - - // Show progress bar - assertThat(mInternetDialog.mIsProgressBarVisible).isTrue(); + mInternetDialog.onWifiScan(false); - ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(mHandler).postDelayed(runnableCaptor.capture(), - eq(InternetDialog.PROGRESS_DELAY_MS)); - runnableCaptor.getValue().run(); - - // Then hide searching sub-title only - assertThat(mInternetDialog.mIsProgressBarVisible).isTrue(); - assertThat(mInternetDialog.mIsSearchingHidden).isTrue(); + assertThat(mInternetDialog.mIsProgressBarVisible).isFalse(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt new file mode 100644 index 000000000000..bbc59d03ac2b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recordissue + +import android.app.Dialog +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.widget.Button +import android.widget.Switch +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.model.SysUiState +import com.android.systemui.res.R +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.SystemUIDialogManager +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class RecordIssueDialogDelegateTest : SysuiTestCase() { + + private lateinit var dialog: SystemUIDialog + private lateinit var latch: CountDownLatch + + @Before + fun setup() { + val dialogFactory = + SystemUIDialog.Factory( + context, + mock<FeatureFlags>(), + mock<SystemUIDialogManager>(), + mock<SysUiState>().apply { + whenever(setFlag(anyInt(), anyBoolean())).thenReturn(this) + }, + mock<BroadcastDispatcher>(), + mock<DialogLaunchAnimator>() + ) + + latch = CountDownLatch(1) + dialog = RecordIssueDialogDelegate(dialogFactory) { latch.countDown() }.createDialog() + dialog.show() + } + + @After + fun teardown() { + dialog.dismiss() + } + + @Test + fun dialog_hasCorrectUiElements_afterCreation() { + dialog.requireViewById<Switch>(R.id.screenrecord_switch) + dialog.requireViewById<Button>(R.id.issue_type_button) + + assertThat(dialog.getButton(Dialog.BUTTON_POSITIVE).text) + .isEqualTo(context.getString(R.string.qs_record_issue_start)) + assertThat(dialog.getButton(Dialog.BUTTON_NEGATIVE).text) + .isEqualTo(context.getString(R.string.cancel)) + } + + @Test + fun onStarted_isCalled_afterStartButtonIsClicked() { + dialog.getButton(Dialog.BUTTON_POSITIVE).callOnClick() + latch.await(1L, TimeUnit.MILLISECONDS) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java index 0f33aaf20195..2c7b60660165 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java @@ -56,6 +56,7 @@ import com.google.common.util.concurrent.Futures; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -138,6 +139,7 @@ public final class AppClipsActivityTest extends SysuiTestCase { } @Test + @Ignore("b/315848285") public void screenshotDisplayed_userConsented_screenshotExportedSuccessfully() { ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> { assertThat(resultCode).isEqualTo(RESULT_OK); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 657f9127dc7e..e572dcca5a34 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -23,6 +23,8 @@ import static com.android.systemui.dump.LogBufferHelperKt.logcatLogBuffer; import static com.google.common.truth.Truth.assertThat; +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; @@ -37,8 +39,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static kotlinx.coroutines.flow.FlowKt.emptyFlow; - import android.annotation.IdRes; import android.content.ContentResolver; import android.content.res.Configuration; @@ -86,6 +86,7 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; import com.android.systemui.common.ui.view.LongPressHandlingView; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; import com.android.systemui.doze.DozeLog; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlagsClassic; @@ -402,6 +403,10 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mPowerInteractor = keyguardInteractorDeps.getPowerInteractor(); when(mKeyguardTransitionInteractor.isInTransitionToStateWhere(any())).thenReturn( StateFlowKt.MutableStateFlow(false)); + DeviceEntryUdfpsInteractor deviceEntryUdfpsInteractor = + mock(DeviceEntryUdfpsInteractor.class); + when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(emptyFlow()); + mShadeInteractor = new ShadeInteractorImpl( mTestScope.getBackgroundScope(), new FakeDeviceProvisioningRepository(), @@ -418,7 +423,9 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { new SharedNotificationContainerInteractor( new FakeConfigurationRepository(), mContext, - new ResourcesSplitShadeStateController() + new ResourcesSplitShadeStateController(), + mKeyguardInteractor, + deviceEntryUdfpsInteractor ), mShadeRepository ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 5ffbe65d2c50..9d8b21464585 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -23,6 +23,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static com.google.common.truth.Truth.assertThat; +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -54,6 +56,7 @@ import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepositor import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlagsClassic; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -235,6 +238,11 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mKeyguardSecurityModel, mSelectedUserInteractor, powerInteractor); + + DeviceEntryUdfpsInteractor deviceEntryUdfpsInteractor = + mock(DeviceEntryUdfpsInteractor.class); + when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(emptyFlow()); + mShadeInteractor = new ShadeInteractorImpl( mTestScope.getBackgroundScope(), new FakeDeviceProvisioningRepository(), @@ -251,7 +259,9 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { new SharedNotificationContainerInteractor( configurationRepository, mContext, - new ResourcesSplitShadeStateController()), + new ResourcesSplitShadeStateController(), + keyguardInteractor, + deviceEntryUdfpsInteractor), shadeRepository ) ); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java index e723d7d0367b..eb5633b70f61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher; import android.content.res.Resources; @@ -41,6 +42,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlagsClassic; import com.android.systemui.flags.FeatureFlags; @@ -275,6 +277,10 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { ResourcesSplitShadeStateController splitShadeStateController = new ResourcesSplitShadeStateController(); + DeviceEntryUdfpsInteractor deviceEntryUdfpsInteractor = + mock(DeviceEntryUdfpsInteractor.class); + when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(emptyFlow()); + mShadeInteractor = new ShadeInteractorImpl( mTestScope.getBackgroundScope(), deviceProvisioningRepository, @@ -291,7 +297,9 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase { new SharedNotificationContainerInteractor( configurationRepository, mContext, - splitShadeStateController), + splitShadeStateController, + keyguardInteractor, + deviceEntryUdfpsInteractor), mShadeRepository ) ); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt index dff91ddf559f..f25ce0aa5278 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepositor import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository @@ -53,6 +54,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSe import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository import com.android.systemui.util.mockito.mock +import kotlinx.coroutines.flow.emptyFlow import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -83,6 +85,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { FromPrimaryBouncerTransitionInteractor @Mock lateinit var interactionJankMonitor: InteractionJankMonitor @Mock lateinit var mockDarkAnimator: ObjectAnimator + @Mock lateinit var deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor private lateinit var controller: StatusBarStateControllerImpl private lateinit var uiEventLogger: UiEventLoggerFake @@ -164,6 +167,8 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { mock(), powerInteractor ) + + whenever(deviceEntryUdfpsInteractor.isUdfpsSupported).thenReturn(emptyFlow()) shadeInteractor = ShadeInteractorImpl( testScope.backgroundScope, @@ -181,7 +186,9 @@ class StatusBarStateControllerImplTest : SysuiTestCase() { SharedNotificationContainerInteractor( configurationRepository, mContext, - ResourcesSplitShadeStateController() + ResourcesSplitShadeStateController(), + keyguardInteractor, + deviceEntryUdfpsInteractor, ), shadeRepository, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt index 3fef1d9832f0..5bc75e8b84c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt @@ -148,6 +148,24 @@ class AccessPointControllerImplTest : SysuiTestCase() { } @Test + fun onWifiEntriesChanged_reasonIsScanResults_fireWifiScanCallbackFalse() { + controller.addAccessPointCallback(callback) + + controller.onWifiEntriesChanged(WifiPickerTracker.WIFI_ENTRIES_CHANGED_REASON_SCAN_RESULTS) + + verify(callback).onWifiScan(false) + } + + @Test + fun onScanRequested_fireWifiScanCallbackTrue() { + controller.addAccessPointCallback(callback) + + controller.onScanRequested() + + verify(callback).onWifiScan(true) + } + + @Test fun testOnNumSavedNetworksChangedDoesntTriggerCallback() { controller.addAccessPointCallback(callback) controller.onNumSavedNetworksChanged() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java index bfa03eed57a0..8cf64a5aa8fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java @@ -48,7 +48,6 @@ import android.os.SystemClock; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArrayMap; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -56,6 +55,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; +import com.android.systemui.log.LogAssertKt; import com.android.systemui.statusbar.NotificationInteractionTracker; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.notification.NotifPipelineFlags; @@ -76,6 +76,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener; import com.android.systemui.util.time.FakeSystemClock; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -129,10 +130,6 @@ public class ShadeListBuilderTest extends SysuiTestCase { private Map<String, Integer> mNextIdMap = new ArrayMap<>(); private int mNextRank = 0; - private Log.TerribleFailureHandler mOldWtfHandler = null; - private Log.TerribleFailure mLastWtf = null; - private int mWtfCount = 0; - @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -1756,20 +1753,19 @@ public class ShadeListBuilderTest extends SysuiTestCase { mListBuilder.addPreGroupFilter(filter); mListBuilder.addOnBeforeTransformGroupsListener(listener); - interceptWtfs(); - // WHEN we try to run the pipeline and the filter is invalidated exactly // MAX_CONSECUTIVE_REENTRANT_REBUILDS times, addNotif(0, PACKAGE_2); invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS); - dispatchBuild(); - runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); // THEN an exception is NOT thrown directly, but a WTF IS logged. - expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS); + LogAssertKt.assertLogsWtfs(() -> { + dispatchBuild(); + runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + }); } - @Test(expected = IllegalStateException.class) + @Test public void testOutOfOrderPreGroupFilterInvalidationThrowsAfterTooManyRuns() { // GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage, NotifFilter filter = new PackageFilter(PACKAGE_1); @@ -1778,20 +1774,20 @@ public class ShadeListBuilderTest extends SysuiTestCase { mListBuilder.addPreGroupFilter(filter); mListBuilder.addOnBeforeTransformGroupsListener(listener); - interceptWtfs(); - // WHEN we try to run the pipeline and the filter is invalidated more than // MAX_CONSECUTIVE_REENTRANT_REBUILDS times, + + // THEN an exception IS thrown. + addNotif(0, PACKAGE_2); invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1); - dispatchBuild(); - try { - runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); - } finally { - expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS); - } - // THEN an exception IS thrown. + LogAssertKt.assertLogsWtfs(() -> { + Assert.assertThrows(IllegalStateException.class, () -> { + dispatchBuild(); + runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + }); + }); } @Test @@ -1803,26 +1799,30 @@ public class ShadeListBuilderTest extends SysuiTestCase { mListBuilder.addPreGroupFilter(filter); mListBuilder.addOnBeforeTransformGroupsListener(listener); - interceptWtfs(); - // WHEN we try to run the pipeline and the filter is invalidated // MAX_CONSECUTIVE_REENTRANT_REBUILDS times, the pipeline runs for a non-reentrant reason, // and then the filter is invalidated MAX_CONSECUTIVE_REENTRANT_REBUILDS times again, + + // THEN an exception is NOT thrown, but WTFs ARE logged. + addNotif(0, PACKAGE_2); + invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS); - dispatchBuild(); - runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); - invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS); - // Note: dispatchBuild itself triggers a non-reentrant pipeline run. - dispatchBuild(); - runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + LogAssertKt.assertLogsWtfs(() -> { + dispatchBuild(); + runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + }); - // THEN an exception is NOT thrown, but WTFs ARE logged. - expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS * 2); + invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS); + LogAssertKt.assertLogsWtfs(() -> { + // Note: dispatchBuild itself triggers a non-reentrant pipeline run. + dispatchBuild(); + runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + }); } @Test - public void testOutOfOrderPrompterInvalidationDoesNotThrowBeforeTooManyRuns() { + public void testOutOfOrderPromoterInvalidationDoesNotThrowBeforeTooManyRuns() { // GIVEN a NotifPromoter that gets invalidated during the sorting stage, NotifPromoter promoter = new IdPromoter(47); CountingInvalidator invalidator = new CountingInvalidator(promoter); @@ -1830,22 +1830,22 @@ public class ShadeListBuilderTest extends SysuiTestCase { mListBuilder.addPromoter(promoter); mListBuilder.addOnBeforeSortListener(listener); - interceptWtfs(); - // WHEN we try to run the pipeline and the promoter is invalidated exactly // MAX_CONSECUTIVE_REENTRANT_REBUILDS times, - addNotif(0, PACKAGE_1); - invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS); - dispatchBuild(); - runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); // THEN an exception is NOT thrown directly, but a WTF IS logged. - expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS); + addNotif(0, PACKAGE_1); + invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS); + + LogAssertKt.assertLogsWtfs(() -> { + dispatchBuild(); + runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + }); } - @Test(expected = IllegalStateException.class) - public void testOutOfOrderPrompterInvalidationThrowsAfterTooManyRuns() { + @Test + public void testOutOfOrderPromoterInvalidationThrowsAfterTooManyRuns() { // GIVEN a NotifPromoter that gets invalidated during the sorting stage, NotifPromoter promoter = new IdPromoter(47); CountingInvalidator invalidator = new CountingInvalidator(promoter); @@ -1853,20 +1853,20 @@ public class ShadeListBuilderTest extends SysuiTestCase { mListBuilder.addPromoter(promoter); mListBuilder.addOnBeforeSortListener(listener); - interceptWtfs(); - // WHEN we try to run the pipeline and the promoter is invalidated more than // MAX_CONSECUTIVE_REENTRANT_REBUILDS times, + + // THEN an exception IS thrown. + addNotif(0, PACKAGE_1); invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1); - dispatchBuild(); - try { - runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); - } finally { - expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS); - } - // THEN an exception IS thrown. + LogAssertKt.assertLogsWtfs(() -> { + Assert.assertThrows(IllegalStateException.class, () -> { + dispatchBuild(); + runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + }); + }); } @Test @@ -1878,20 +1878,21 @@ public class ShadeListBuilderTest extends SysuiTestCase { mListBuilder.setComparators(singletonList(comparator)); mListBuilder.addOnBeforeRenderListListener(listener); - interceptWtfs(); - // WHEN we try to run the pipeline and the comparator is invalidated exactly // MAX_CONSECUTIVE_REENTRANT_REBUILDS times, + + // THEN an exception is NOT thrown directly, but a WTF IS logged. + addNotif(0, PACKAGE_2); invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS); - dispatchBuild(); - runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); - // THEN an exception is NOT thrown directly, but a WTF IS logged. - expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS); + LogAssertKt.assertLogsWtfs(() -> { + dispatchBuild(); + runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + }); } - @Test(expected = IllegalStateException.class) + @Test public void testOutOfOrderComparatorInvalidationThrowsAfterTooManyRuns() { // GIVEN a NotifComparator that gets invalidated during the finalizing stage, NotifComparator comparator = new HypeComparator(PACKAGE_1); @@ -1900,16 +1901,20 @@ public class ShadeListBuilderTest extends SysuiTestCase { mListBuilder.setComparators(singletonList(comparator)); mListBuilder.addOnBeforeRenderListListener(listener); - interceptWtfs(); - // WHEN we try to run the pipeline and the comparator is invalidated more than // MAX_CONSECUTIVE_REENTRANT_REBUILDS times, + + // THEN an exception IS thrown. + addNotif(0, PACKAGE_2); invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1); - dispatchBuild(); - runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); - // THEN an exception IS thrown. + LogAssertKt.assertLogsWtfs(() -> { + Assert.assertThrows(IllegalStateException.class, () -> { + dispatchBuild(); + runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + }); + }); } @Test @@ -1921,20 +1926,21 @@ public class ShadeListBuilderTest extends SysuiTestCase { mListBuilder.addFinalizeFilter(filter); mListBuilder.addOnBeforeRenderListListener(listener); - interceptWtfs(); - // WHEN we try to run the pipeline and the PreRenderFilter is invalidated exactly // MAX_CONSECUTIVE_REENTRANT_REBUILDS times, + + // THEN an exception is NOT thrown directly, but a WTF IS logged. + addNotif(0, PACKAGE_2); invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS); - dispatchBuild(); - runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); - // THEN an exception is NOT thrown directly, but a WTF IS logged. - expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS); + LogAssertKt.assertLogsWtfs(() -> { + dispatchBuild(); + runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + }); } - @Test(expected = IllegalStateException.class) + @Test public void testOutOfOrderPreRenderFilterInvalidationThrowsAfterTooManyRuns() { // GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage, NotifFilter filter = new PackageFilter(PACKAGE_1); @@ -1943,59 +1949,22 @@ public class ShadeListBuilderTest extends SysuiTestCase { mListBuilder.addFinalizeFilter(filter); mListBuilder.addOnBeforeRenderListListener(listener); - interceptWtfs(); - // WHEN we try to run the pipeline and the PreRenderFilter is invalidated more than // MAX_CONSECUTIVE_REENTRANT_REBUILDS times, - addNotif(0, PACKAGE_2); - invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1); - dispatchBuild(); - try { - runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); - } finally { - expectWtfs(MAX_CONSECUTIVE_REENTRANT_REBUILDS); - } // THEN an exception IS thrown. - } - - private void interceptWtfs() { - assertNull(mOldWtfHandler); - mLastWtf = null; - mWtfCount = 0; + addNotif(0, PACKAGE_2); + invalidator.setInvalidationCount(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 1); - mOldWtfHandler = Log.setWtfHandler((tag, e, system) -> { - Log.e("ShadeListBuilderTest", "Observed WTF: " + e); - mLastWtf = e; - mWtfCount++; + LogAssertKt.assertLogsWtfs(() -> { + Assert.assertThrows(IllegalStateException.class, () -> { + dispatchBuild(); + runWhileScheduledUpTo(MAX_CONSECUTIVE_REENTRANT_REBUILDS + 2); + }); }); } - private void expectNoWtfs() { - assertNull(expectWtfs(0)); - } - - private Log.TerribleFailure expectWtf() { - return expectWtfs(1); - } - - private Log.TerribleFailure expectWtfs(int expectedWtfCount) { - assertNotNull(mOldWtfHandler); - - Log.setWtfHandler(mOldWtfHandler); - mOldWtfHandler = null; - - Log.TerribleFailure wtf = mLastWtf; - int wtfCount = mWtfCount; - - mLastWtf = null; - mWtfCount = 0; - - assertEquals(expectedWtfCount, wtfCount); - return wtf; - } - @Test public void testStableOrdering() { mStabilityManager.setAllowEntryReordering(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt index a07b5705d171..327a07d6179f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorTest.kt @@ -20,57 +20,86 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository +import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository +import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.kosmos.testScope import com.android.systemui.res.R -import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class SharedNotificationContainerInteractorTest : SysuiTestCase() { - private lateinit var configurationRepository: FakeConfigurationRepository - private lateinit var underTest: SharedNotificationContainerInteractor - - @Before - fun setUp() { - configurationRepository = FakeConfigurationRepository() - underTest = - SharedNotificationContainerInteractor( - configurationRepository, - mContext, - ResourcesSplitShadeStateController() - ) - } + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val keyguardRepository = kosmos.fakeKeyguardRepository + private val configurationRepository = kosmos.fakeConfigurationRepository + private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository + private val underTest = kosmos.sharedNotificationContainerInteractor @Test - fun validateConfigValues() = runTest { - overrideResource(R.bool.config_use_split_notification_shade, true) - overrideResource(R.bool.config_use_large_screen_shade_header, false) - overrideResource(R.dimen.notification_panel_margin_horizontal, 0) - overrideResource(R.dimen.notification_panel_margin_bottom, 10) - overrideResource(R.dimen.notification_panel_margin_top, 10) - overrideResource(R.dimen.large_screen_shade_header_height, 0) - overrideResource(R.dimen.keyguard_split_shade_top_margin, 55) - - val dimens = collectLastValue(underTest.configurationBasedDimensions) - - configurationRepository.onAnyConfigurationChange() - runCurrent() - - val lastDimens = dimens()!! - - assertThat(lastDimens.useSplitShade).isTrue() - assertThat(lastDimens.useLargeScreenHeader).isFalse() - assertThat(lastDimens.marginHorizontal).isEqualTo(0) - assertThat(lastDimens.marginBottom).isGreaterThan(0) - assertThat(lastDimens.marginTop).isGreaterThan(0) - assertThat(lastDimens.marginTopLargeScreen).isEqualTo(0) - assertThat(lastDimens.keyguardSplitShadeTopMargin).isEqualTo(55) - } + fun validateConfigValues() = + testScope.runTest { + overrideResource(R.bool.config_use_split_notification_shade, true) + overrideResource(R.bool.config_use_large_screen_shade_header, false) + overrideResource(R.dimen.notification_panel_margin_horizontal, 0) + overrideResource(R.dimen.notification_panel_margin_bottom, 10) + overrideResource(R.dimen.notification_panel_margin_top, 10) + overrideResource(R.dimen.large_screen_shade_header_height, 0) + overrideResource(R.dimen.keyguard_split_shade_top_margin, 55) + + val dimens = collectLastValue(underTest.configurationBasedDimensions) + + configurationRepository.onAnyConfigurationChange() + runCurrent() + + val lastDimens = dimens()!! + + assertThat(lastDimens.useSplitShade).isTrue() + assertThat(lastDimens.useLargeScreenHeader).isFalse() + assertThat(lastDimens.marginHorizontal).isEqualTo(0) + assertThat(lastDimens.marginBottom).isGreaterThan(0) + assertThat(lastDimens.marginTop).isGreaterThan(0) + assertThat(lastDimens.marginTopLargeScreen).isEqualTo(0) + assertThat(lastDimens.keyguardSplitShadeTopMargin).isEqualTo(55) + } + + @Test + fun useExtraShelfSpaceIsTrueWithUdfps() = + testScope.runTest { + val useExtraShelfSpace by collectLastValue(underTest.useExtraShelfSpace) + + keyguardRepository.ambientIndicationVisible.value = true + fingerprintPropertyRepository.supportsUdfps() + + assertThat(useExtraShelfSpace).isEqualTo(true) + } + + @Test + fun useExtraShelfSpaceIsTrueWithRearFpsAndNoAmbientIndicationArea() = + testScope.runTest { + val useExtraShelfSpace by collectLastValue(underTest.useExtraShelfSpace) + + keyguardRepository.ambientIndicationVisible.value = false + fingerprintPropertyRepository.supportsRearFps() + + assertThat(useExtraShelfSpace).isEqualTo(true) + } + + @Test + fun useExtraShelfSpaceIsFalseWithRearFpsAndAmbientIndicationArea() = + testScope.runTest { + val useExtraShelfSpace by collectLastValue(underTest.useExtraShelfSpace) + + keyguardRepository.ambientIndicationVisible.value = true + fingerprintPropertyRepository.supportsRearFps() + + assertThat(useExtraShelfSpace).isEqualTo(false) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index f0205b3f5974..36a471238c8a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -332,8 +332,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { fun maxNotificationsOnLockscreen() = testScope.runTest { var notificationCount = 10 - val maxNotifications by - collectLastValue(underTest.getMaxNotifications { notificationCount }) + val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> notificationCount } + val maxNotifications by collectLastValue(underTest.getMaxNotifications(calculateSpace)) showLockscreen() @@ -355,8 +355,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { fun maxNotificationsOnLockscreen_DoesNotUpdateWhenUserInteracting() = testScope.runTest { var notificationCount = 10 - val maxNotifications by - collectLastValue(underTest.getMaxNotifications { notificationCount }) + val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> notificationCount } + val maxNotifications by collectLastValue(underTest.getMaxNotifications(calculateSpace)) showLockscreen() @@ -390,7 +390,8 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { @Test fun maxNotificationsOnShade() = testScope.runTest { - val maxNotifications by collectLastValue(underTest.getMaxNotifications { 10 }) + val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> 10 } + val maxNotifications by collectLastValue(underTest.getMaxNotifications(calculateSpace)) // Show lockscreen with shade expanded showLockscreenWithShadeExpanded() diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index 112368895888..b58a41c89a4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.theme; +import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; @@ -90,6 +91,9 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { private static final int USER_SYSTEM = UserHandle.USER_SYSTEM; private static final int USER_SECONDARY = 10; + private static final UserHandle MANAGED_USER_HANDLE = UserHandle.of(100); + private static final UserHandle PRIVATE_USER_HANDLE = UserHandle.of(101); + @Mock private JavaAdapter mJavaAdapter; @Mock @@ -174,6 +178,14 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { Integer.toHexString(mColorScheme.getSeed() | 0xff000000))); return overlay; } + + @VisibleForTesting + protected boolean isPrivateProfile(UserHandle userHandle) { + if (userHandle.getIdentifier() == PRIVATE_USER_HANDLE.getIdentifier()) { + return true; + } + return false; + } }; mWakefulnessLifecycle.dispatchFinishedWakingUp(); @@ -675,7 +687,8 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { @Test public void onProfileAdded_setsTheme() { mBroadcastReceiver.getValue().onReceive(null, - new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED)); + new Intent(Intent.ACTION_PROFILE_ADDED) + .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE)); verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); } @@ -684,7 +697,8 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { reset(mDeviceProvisionedController); when(mUserManager.isManagedProfile(anyInt())).thenReturn(false); mBroadcastReceiver.getValue().onReceive(null, - new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED)); + new Intent(Intent.ACTION_PROFILE_ADDED) + .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE)); verify(mThemeOverlayApplier) .applyCurrentUserOverlays(any(), any(), anyInt(), any()); } @@ -694,11 +708,25 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { reset(mDeviceProvisionedController); when(mUserManager.isManagedProfile(anyInt())).thenReturn(true); mBroadcastReceiver.getValue().onReceive(null, - new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED)); + new Intent(Intent.ACTION_PROFILE_ADDED) + .putExtra(Intent.EXTRA_USER, MANAGED_USER_HANDLE)); + verify(mThemeOverlayApplier, never()) + .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + } + + @Test + public void onPrivateProfileAdded_ignoresUntilStartComplete() { + mSetFlagsRule.enableFlags(FLAG_ALLOW_PRIVATE_PROFILE); + reset(mDeviceProvisionedController); + when(mUserManager.isManagedProfile(anyInt())).thenReturn(false); + mBroadcastReceiver.getValue().onReceive(null, + (new Intent(Intent.ACTION_PROFILE_ADDED)) + .putExtra(Intent.EXTRA_USER, PRIVATE_USER_HANDLE)); verify(mThemeOverlayApplier, never()) .applyCurrentUserOverlays(any(), any(), anyInt(), any()); } + @Test public void onWallpaperColorsChanged_firstEventBeforeUserSetup_shouldBeAccepted() { // By default, on setup() we make this controller return that the user finished setup diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 5b9b390eea2d..b217195000b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -29,6 +29,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.google.common.truth.Truth.assertThat; +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -97,6 +99,7 @@ import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepositor import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository; import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor; +import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.FakeFeatureFlagsClassic; @@ -465,6 +468,10 @@ public class BubblesTest extends SysuiTestCase { ResourcesSplitShadeStateController splitShadeStateController = new ResourcesSplitShadeStateController(); + DeviceEntryUdfpsInteractor deviceEntryUdfpsInteractor = + mock(DeviceEntryUdfpsInteractor.class); + when(deviceEntryUdfpsInteractor.isUdfpsSupported()).thenReturn(emptyFlow()); + mShadeInteractor = new ShadeInteractorImpl( mTestScope.getBackgroundScope(), @@ -481,7 +488,9 @@ public class BubblesTest extends SysuiTestCase { new SharedNotificationContainerInteractor( configurationRepository, mContext, - splitShadeStateController), + splitShadeStateController, + keyguardInteractor, + deviceEntryUdfpsInteractor), shadeRepository ) ); diff --git a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt new file mode 100644 index 000000000000..b88f302cdfdd --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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.graphics.drawable + +import android.graphics.Canvas +import android.graphics.ColorFilter +import android.graphics.PixelFormat + +/** + * Stub drawable that does nothing. It's to be used in tests as a mock drawable and checked for the + * same instance + */ +class TestStubDrawable : Drawable() { + + override fun draw(canvas: Canvas) = Unit + override fun setAlpha(alpha: Int) = Unit + override fun setColorFilter(colorFilter: ColorFilter?) = Unit + override fun getOpacity(): Int = PixelFormat.UNKNOWN + + override fun equals(other: Any?): Boolean = this === other +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorCorrectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorCorrectionRepository.kt new file mode 100644 index 000000000000..607a4f3e1c0f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorCorrectionRepository.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.data.repository + +import android.os.UserHandle +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class FakeColorCorrectionRepository : ColorCorrectionRepository { + private val userMap = mutableMapOf<Int, MutableStateFlow<Boolean>>() + + override fun isEnabled(userHandle: UserHandle): StateFlow<Boolean> { + return getFlow(userHandle.identifier) + } + + override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean { + getFlow(userHandle.identifier).value = isEnabled + return true + } + + /** initializes the flow if already not */ + private fun getFlow(userId: Int): MutableStateFlow<Boolean> { + return userMap.getOrPut(userId) { MutableStateFlow(false) } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 0e7c6625264c..4200f05ad64b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -41,6 +41,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { private val _deferKeyguardDone: MutableSharedFlow<KeyguardDone> = MutableSharedFlow() override val keyguardDone: Flow<KeyguardDone> = _deferKeyguardDone + private val _keyguardDoneAnimationsFinished: MutableSharedFlow<Unit> = + MutableSharedFlow(extraBufferCapacity = 1) + override val keyguardDoneAnimationsFinished: Flow<Unit> = _keyguardDoneAnimationsFinished + private val _clockShouldBeCentered = MutableStateFlow<Boolean>(true) override val clockShouldBeCentered: Flow<Boolean> = _clockShouldBeCentered @@ -121,6 +125,8 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { override val lastRootViewTapPosition: MutableStateFlow<Point?> = MutableStateFlow(null) + override val ambientIndicationVisible: MutableStateFlow<Boolean> = MutableStateFlow(false) + override fun setQuickSettingsVisible(isVisible: Boolean) { _isQuickSettingsVisible.value = isVisible } @@ -174,6 +180,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository { _deferKeyguardDone.emit(timing) } + override fun keyguardDoneAnimationsFinished() { + _keyguardDoneAnimationsFinished.tryEmit(Unit) + } + override fun setClockShouldBeCentered(shouldBeCentered: Boolean) { _clockShouldBeCentered.value = shouldBeCentered } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt index 10f9346aba14..6ccb3bc2812e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/LogAssert.kt @@ -59,6 +59,17 @@ fun assertLogsWtf( ): TerribleFailureLog = assertLogsWtf(message = message, allowMultiple = allowMultiple) { loggingRunnable.run() } +fun assertLogsWtfs( + message: String = "Expected Log.wtf to be called once or more", + loggingBlock: () -> Unit, +): TerribleFailureLog = assertLogsWtf(message, allowMultiple = true, loggingBlock) + +@JvmOverloads +fun assertLogsWtfs( + message: String = "Expected Log.wtf to be called once or more", + loggingRunnable: Runnable, +): TerribleFailureLog = assertLogsWtfs(message) { loggingRunnable.run() } + /** The data passed to [TerribleFailureHandler.onTerribleFailure] */ data class TerribleFailureLog( val tag: String, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt index 1cb2587e4e99..6332c1a8010d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt @@ -19,9 +19,14 @@ package com.android.systemui.qs import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.InstanceIdSequenceFake import com.android.systemui.kosmos.Kosmos +import com.android.systemui.plugins.qs.QSFactory +import com.android.systemui.qs.tiles.di.NewQSTileFactory val Kosmos.instanceIdSequenceFake: InstanceIdSequenceFake by Kosmos.Fixture { InstanceIdSequenceFake(0) } val Kosmos.uiEventLogger: UiEventLoggerFake by Kosmos.Fixture { UiEventLoggerFake() } val Kosmos.qsEventLogger: QsEventLoggerFake by Kosmos.Fixture { QsEventLoggerFake(uiEventLogger, instanceIdSequenceFake) } + +var Kosmos.newQSTileFactory by Kosmos.Fixture<NewQSTileFactory>() +var Kosmos.qsTileFactory by Kosmos.Fixture<QSFactory>() diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/CustomTileStatePersisterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/CustomTileStatePersisterKosmos.kt new file mode 100644 index 000000000000..f01e3aaa7089 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/CustomTileStatePersisterKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.external + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.customTileStatePersister: CustomTileStatePersister by + Kosmos.Fixture { fakeCustomTileStatePersister } +val Kosmos.fakeCustomTileStatePersister by Kosmos.Fixture { FakeCustomTileStatePersister() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt new file mode 100644 index 000000000000..f8ce707b0bb2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.external + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +/** Returns mocks */ +var Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by + Kosmos.Fixture { TileLifecycleManager.Factory { _, _ -> mock<TileLifecycleManager>() } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessorsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessorsKosmos.kt new file mode 100644 index 000000000000..d93dd8d42721 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessorsKosmos.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.data.model + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.pipeline.data.restoreprocessors.WorkTileRestoreProcessor + +val Kosmos.workTileRestoreProcessor by Kosmos.Fixture { WorkTileRestoreProcessor() } + +var Kosmos.restoreProcessors by + Kosmos.Fixture { + setOf( + workTileRestoreProcessor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt new file mode 100644 index 000000000000..009148266143 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeTileSpecRepository by Kosmos.Fixture { FakeTileSpecRepository() } +var Kosmos.tileSpecRepository: TileSpecRepository by Kosmos.Fixture { fakeTileSpecRepository } + +val Kosmos.fakeAutoAddRepository by Kosmos.Fixture { FakeAutoAddRepository() } +var Kosmos.autoAddRepository: AutoAddRepository by Kosmos.Fixture { fakeAutoAddRepository } + +val Kosmos.fakeRestoreRepository by Kosmos.Fixture { FakeQSSettingsRestoredRepository() } +var Kosmos.restoreRepository: QSSettingsRestoredRepository by + Kosmos.Fixture { fakeRestoreRepository } + +val Kosmos.fakeInstalledTilesRepository by + Kosmos.Fixture { FakeInstalledTilesComponentRepository() } +var Kosmos.installedTilesRepository: InstalledTilesComponentRepository by + Kosmos.Fixture { fakeInstalledTilesRepository } + +val Kosmos.fakeCustomTileAddedRepository by Kosmos.Fixture { FakeCustomTileAddedRepository() } +var Kosmos.customTileAddedRepository: CustomTileAddedRepository by + Kosmos.Fixture { fakeCustomTileAddedRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddablesKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddablesKosmos.kt new file mode 100644 index 000000000000..35f178b62a08 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddablesKosmos.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.domain.autoaddable + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.pipeline.data.model.workTileRestoreProcessor +import com.android.systemui.settings.userTracker + +val Kosmos.workTileAutoAddable by + Kosmos.Fixture { WorkTileAutoAddable(userTracker, workTileRestoreProcessor) } + +var Kosmos.autoAddables by Kosmos.Fixture { setOf(workTileAutoAddable) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt index ebdd6fd7aac0..bf8f4da34d5b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt @@ -39,14 +39,18 @@ class FakeAutoAddable( return getFlow(userId).asStateFlow().filterNotNull() } - suspend fun sendRemoveSignal(userId: Int) { + fun sendRemoveSignal(userId: Int) { getFlow(userId).value = AutoAddSignal.Remove(spec) } - suspend fun sendAddSignal(userId: Int, position: Int = POSITION_AT_END) { + fun sendAddSignal(userId: Int, position: Int = POSITION_AT_END) { getFlow(userId).value = AutoAddSignal.Add(spec, position) } + fun sendRemoveTrackingSignal(userId: Int) { + getFlow(userId).value = AutoAddSignal.RemoveTracking(spec) + } + override val description: String get() = "FakeAutoAddable($spec)" } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorKosmos.kt new file mode 100644 index 000000000000..5e8471c5575b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorKosmos.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.domain.interactor + +import com.android.systemui.dump.dumpManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.qs.pipeline.data.repository.autoAddRepository +import com.android.systemui.qs.pipeline.domain.autoaddable.autoAddables +import com.android.systemui.qs.pipeline.shared.logging.qsLogger + +val Kosmos.autoAddInteractor by + Kosmos.Fixture { + AutoAddInteractor( + autoAddables, + autoAddRepository, + dumpManager, + qsLogger, + applicationCoroutineScope, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt new file mode 100644 index 000000000000..67df563ec5b0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.qs.external.customTileStatePersister +import com.android.systemui.qs.external.tileLifecycleManagerFactory +import com.android.systemui.qs.newQSTileFactory +import com.android.systemui.qs.pipeline.data.repository.customTileAddedRepository +import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository +import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository +import com.android.systemui.qs.pipeline.shared.logging.qsLogger +import com.android.systemui.qs.pipeline.shared.pipelineFlagsRepository +import com.android.systemui.qs.qsTileFactory +import com.android.systemui.settings.userTracker +import com.android.systemui.user.data.repository.userRepository + +val Kosmos.currentTilesInteractor: CurrentTilesInteractor by + Kosmos.Fixture { + CurrentTilesInteractorImpl( + tileSpecRepository, + installedTilesRepository, + userRepository, + customTileStatePersister, + { newQSTileFactory }, + qsTileFactory, + customTileAddedRepository, + tileLifecycleManagerFactory, + userTracker, + testDispatcher, + testDispatcher, + applicationCoroutineScope, + qsLogger, + pipelineFlagsRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorKosmos.kt new file mode 100644 index 000000000000..55c23d41e548 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorKosmos.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.qs.pipeline.data.model.restoreProcessors +import com.android.systemui.qs.pipeline.data.repository.autoAddRepository +import com.android.systemui.qs.pipeline.data.repository.restoreRepository +import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository +import com.android.systemui.qs.pipeline.shared.logging.qsLogger + +val Kosmos.restoreReconciliationInteractor by + Kosmos.Fixture { + RestoreReconciliationInteractor( + tileSpecRepository, + autoAddRepository, + restoreRepository, + restoreProcessors, + qsLogger, + applicationCoroutineScope, + testDispatcher, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagRepositoryKosmos.kt new file mode 100644 index 000000000000..961545aba4e8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagRepositoryKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.shared + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.pipelineFlagsRepository by Kosmos.Fixture { QSPipelineFlagsRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLoggerKosmos.kt new file mode 100644 index 000000000000..7d52f5d8aa34 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLoggerKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.shared.logging + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +/** mock */ +var Kosmos.qsLogger: QSPipelineLogger by Kosmos.Fixture { mock<QSPipelineLogger>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/colorcorrection/ColorCorrectionTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/colorcorrection/ColorCorrectionTileKosmos.kt new file mode 100644 index 000000000000..0357036d907c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/colorcorrection/ColorCorrectionTileKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.colorcorrection + +import com.android.systemui.accessibility.qs.QSAccessibilityModule +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.qsEventLogger + +val Kosmos.qsColorCorrectionTileConfig by + Kosmos.Fixture { QSAccessibilityModule.provideColorCorrectionTileConfig(qsEventLogger) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt index 7494ccf32a2c..2ca338a3af9c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt @@ -72,6 +72,7 @@ class FakeUserTracker( onBeforeUserSwitching() onUserChanging() onUserChanged() + onProfileChanged() } fun onBeforeUserSwitching(userId: Int = _userId) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserTrackerKosmos.kt new file mode 100644 index 000000000000..ffa86ff03ab1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserTrackerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.settings + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeUserTracker by Kosmos.Fixture { FakeUserTracker() } +var Kosmos.userTracker: UserTracker by Kosmos.Fixture { fakeUserTracker } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt index c8013ef96fa7..862e52d7703f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt @@ -10,12 +10,12 @@ class FakeSmartspaceRepository( override val isSmartspaceRemoteViewsEnabled = smartspaceRemoteViewsEnabled - private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> = + private val _communalSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> = MutableStateFlow(emptyList()) - override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> = - _lockscreenSmartspaceTargets + override val communalSmartspaceTargets: Flow<List<SmartspaceTarget>> = + _communalSmartspaceTargets - fun setLockscreenSmartspaceTargets(targets: List<SmartspaceTarget>) { - _lockscreenSmartspaceTargets.value = targets + fun setCommunalSmartspaceTargets(targets: List<SmartspaceTarget>) { + _communalSmartspaceTargets.value = targets } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt index 3403227f6d27..13d577bde711 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.stack.domain.interactor import android.content.applicationContext import com.android.systemui.common.ui.data.repository.configurationRepository +import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.policy.splitShadeStateController @@ -27,5 +29,7 @@ val Kosmos.sharedNotificationContainerInteractor by configurationRepository = configurationRepository, context = applicationContext, splitShadeStateController = splitShadeStateController, + keyguardInteractor = keyguardInteractor, + deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, ) } diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java index b315f4a0f0c5..7fcef9c90812 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java @@ -76,6 +76,7 @@ import android.util.Size; import android.view.Surface; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl; import androidx.camera.extensions.impl.AutoPreviewExtenderImpl; import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl; @@ -94,6 +95,7 @@ import androidx.camera.extensions.impl.PreviewExtenderImpl; import androidx.camera.extensions.impl.PreviewExtenderImpl.ProcessorType; import androidx.camera.extensions.impl.PreviewImageProcessorImpl; import androidx.camera.extensions.impl.ProcessResultImpl; +import androidx.camera.extensions.impl.ProcessorImpl; import androidx.camera.extensions.impl.RequestUpdateProcessorImpl; import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl; @@ -101,6 +103,7 @@ import androidx.camera.extensions.impl.advanced.BeautyAdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.BokehAdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl; import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl; +import androidx.camera.extensions.impl.advanced.EyesFreeVideographyAdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.HdrAdvancedExtenderImpl; import androidx.camera.extensions.impl.advanced.ImageProcessorImpl; import androidx.camera.extensions.impl.advanced.ImageReaderOutputConfigImpl; @@ -112,6 +115,8 @@ import androidx.camera.extensions.impl.advanced.RequestProcessorImpl; import androidx.camera.extensions.impl.advanced.SessionProcessorImpl; import androidx.camera.extensions.impl.advanced.SurfaceOutputConfigImpl; +import com.android.internal.camera.flags.Flags; + import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -135,22 +140,28 @@ public class CameraExtensionsProxyService extends Service { private static final String RESULTS_VERSION_PREFIX = "1.3"; // Support for various latency improvements private static final String LATENCY_VERSION_PREFIX = "1.4"; - private static final String[] ADVANCED_VERSION_PREFIXES = {LATENCY_VERSION_PREFIX, - ADVANCED_VERSION_PREFIX, RESULTS_VERSION_PREFIX }; - private static final String[] SUPPORTED_VERSION_PREFIXES = {LATENCY_VERSION_PREFIX, - RESULTS_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, "1.1", NON_INIT_VERSION_PREFIX}; + private static final String EFV_VERSION_PREFIX = "1.5"; + private static final String[] ADVANCED_VERSION_PREFIXES = {EFV_VERSION_PREFIX, + LATENCY_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, RESULTS_VERSION_PREFIX }; + private static final String[] SUPPORTED_VERSION_PREFIXES = {EFV_VERSION_PREFIX, + LATENCY_VERSION_PREFIX, RESULTS_VERSION_PREFIX, ADVANCED_VERSION_PREFIX, "1.1", + NON_INIT_VERSION_PREFIX}; private static final boolean EXTENSIONS_PRESENT = checkForExtensions(); private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ? (new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null; private static final boolean ESTIMATED_LATENCY_API_SUPPORTED = checkForLatencyAPI(); private static final boolean LATENCY_IMPROVEMENTS_SUPPORTED = EXTENSIONS_PRESENT && - (EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX)); + (EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX) || + (EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX))); + private static final boolean EFV_SUPPORTED = EXTENSIONS_PRESENT && + (EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX)); private static final boolean ADVANCED_API_SUPPORTED = checkForAdvancedAPI(); private static final boolean INIT_API_SUPPORTED = EXTENSIONS_PRESENT && (!EXTENSIONS_VERSION.startsWith(NON_INIT_VERSION_PREFIX)); private static final boolean RESULT_API_SUPPORTED = EXTENSIONS_PRESENT && (EXTENSIONS_VERSION.startsWith(RESULTS_VERSION_PREFIX) || - EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX)); + EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX) || + EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX)); private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>(); private CameraManager mCameraManager; @@ -509,6 +520,167 @@ public class CameraExtensionsProxyService extends Service { */ public static Pair<PreviewExtenderImpl, ImageCaptureExtenderImpl> initializeExtension( int extensionType) { + if (Flags.concertMode()) { + if (extensionType == CameraExtensionCharacteristics.EXTENSION_EYES_FREE_VIDEOGRAPHY) { + // Basic extensions are deprecated starting with extension version 1.5 + return new Pair<>(new PreviewExtenderImpl() { + @Override + public boolean isExtensionAvailable(String cameraId, + CameraCharacteristics cameraCharacteristics) { + return false; + } + + @Override + public void init(String cameraId, CameraCharacteristics cameraCharacteristics) { + + } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl getCaptureStage() { + return null; + } + + @Override + public ProcessorType getProcessorType() { + return null; + } + + @Override + public ProcessorImpl getProcessor() { + return null; + } + + @Nullable + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + return null; + } + + @Override + public void onInit(String cameraId, CameraCharacteristics cameraCharacteristics, + Context context) { } + + @Override + public void onDeInit() { } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onPresetSession() { + return null; + } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onEnableSession() { + return null; + } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onDisableSession() { + return null; + } + + @Override + public int onSessionType() { + return 0; + } + }, new ImageCaptureExtenderImpl() { + @Override + public boolean isExtensionAvailable(String cameraId, + CameraCharacteristics cameraCharacteristics) { + return false; + } + + @Override + public void init(String cameraId, + CameraCharacteristics cameraCharacteristics) { } + + @Override + public CaptureProcessorImpl getCaptureProcessor() { + return null; + } + + @Override + public + List<androidx.camera.extensions.impl.CaptureStageImpl> getCaptureStages() { + return null; + } + + @Override + public int getMaxCaptureStage() { + return 0; + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedResolutions() { + return null; + } + + @Override + public List<Pair<Integer, Size[]>> getSupportedPostviewResolutions( + Size captureSize) { + return null; + } + + @Override + public Range<Long> getEstimatedCaptureLatencyRange( + Size captureOutputSize) { + return null; + } + + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + return null; + } + + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + return null; + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + return false; + } + + @Override + public Pair<Long, Long> getRealtimeCaptureLatency() { + return null; + } + + @Override + public boolean isPostviewAvailable() { + return false; + } + + @Override + public void onInit(String cameraId, + CameraCharacteristics cameraCharacteristics, Context context) { } + + @Override + public void onDeInit() { } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onPresetSession() { + return null; + } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onEnableSession() { + return null; + } + + @Override + public androidx.camera.extensions.impl.CaptureStageImpl onDisableSession() { + return null; + } + + @Override + public int onSessionType() { + return 0; + } + }); + } + } + switch (extensionType) { case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC: return new Pair<>(new AutoPreviewExtenderImpl(), @@ -533,6 +705,82 @@ public class CameraExtensionsProxyService extends Service { * @hide */ public static AdvancedExtenderImpl initializeAdvancedExtensionImpl(int extensionType) { + if (Flags.concertMode()) { + if (extensionType == CameraExtensionCharacteristics.EXTENSION_EYES_FREE_VIDEOGRAPHY) { + if (EFV_SUPPORTED) { + return new EyesFreeVideographyAdvancedExtenderImpl(); + } else { + return new AdvancedExtenderImpl() { + @Override + public boolean isExtensionAvailable(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + return false; + } + + @Override + public void init(String cameraId, + Map<String, CameraCharacteristics> characteristicsMap) { + + } + + @Override + public Range<Long> getEstimatedCaptureLatencyRange(String cameraId, + Size captureOutputSize, int imageFormat) { + return null; + } + + @Override + public Map<Integer, List<Size>> getSupportedPreviewOutputResolutions( + String cameraId) { + return null; + } + + @Override + public Map<Integer, List<Size>> getSupportedCaptureOutputResolutions( + String cameraId) { + return null; + } + + @Override + public Map<Integer, List<Size>> getSupportedPostviewResolutions( + Size captureSize) { + return null; + } + + @Override + public List<Size> getSupportedYuvAnalysisResolutions(String cameraId) { + return null; + } + + @Override + public SessionProcessorImpl createSessionProcessor() { + return null; + } + + @Override + public List<CaptureRequest.Key> getAvailableCaptureRequestKeys() { + return null; + } + + @Override + public List<CaptureResult.Key> getAvailableCaptureResultKeys() { + return null; + } + + @Override + public boolean isCaptureProcessProgressAvailable() { + return false; + } + + @Override + public boolean isPostviewAvailable() { + return false; + } + }; + } + } + } + switch (extensionType) { case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC: return new AutoAdvancedExtenderImpl(); diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index d175713eb92f..513c09587026 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -16,6 +16,8 @@ package android.platform.test.ravenwood; +import static org.junit.Assert.fail; + import android.platform.test.annotations.IgnoreUnderRavenwood; import org.junit.Assume; @@ -36,6 +38,15 @@ public class RavenwoodRule implements TestRule { private static final boolean IS_UNDER_RAVENWOOD = RavenwoodRuleImpl.isUnderRavenwood(); + /** + * When probing is enabled, all tests will be unconditionally run under Ravenwood to detect + * cases where a test is able to pass despite being marked as {@code IgnoreUnderRavenwood}. + * + * This is typically helpful for internal maintainers discovering tests that had previously + * been ignored, but now have enough Ravenwood-supported functionality to be enabled. + */ + private static final boolean ENABLE_PROBE_IGNORED = false; // DO NOT SUBMIT WITH TRUE + private static final int SYSTEM_UID = 1000; private static final int NOBODY_UID = 9999; private static final int FIRST_APPLICATION_UID = 10000; @@ -97,26 +108,76 @@ public class RavenwoodRule implements TestRule { return IS_UNDER_RAVENWOOD; } + /** + * Test if the given {@link Description} has been marked with an {@link IgnoreUnderRavenwood} + * annotation, either at the method or class level. + */ + private static boolean hasIgnoreUnderRavenwoodAnnotation(Description description) { + if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) { + return true; + } else if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) { + return true; + } else { + return false; + } + } + @Override public Statement apply(Statement base, Description description) { + if (ENABLE_PROBE_IGNORED) { + return applyProbeIgnored(base, description); + } else { + return applyDefault(base, description); + } + } + + /** + * Run the given {@link Statement} with no special treatment. + */ + private Statement applyDefault(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { - if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) { - Assume.assumeFalse(IS_UNDER_RAVENWOOD); - } - if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) { + if (hasIgnoreUnderRavenwoodAnnotation(description)) { Assume.assumeFalse(IS_UNDER_RAVENWOOD); } - if (IS_UNDER_RAVENWOOD) { - RavenwoodRuleImpl.init(RavenwoodRule.this); - } + + RavenwoodRuleImpl.init(RavenwoodRule.this); try { base.evaluate(); } finally { - if (IS_UNDER_RAVENWOOD) { - RavenwoodRuleImpl.reset(RavenwoodRule.this); + RavenwoodRuleImpl.reset(RavenwoodRule.this); + } + } + }; + } + + /** + * Run the given {@link Statement} with probing enabled. All tests will be unconditionally + * run under Ravenwood to detect cases where a test is able to pass despite being marked as + * {@code IgnoreUnderRavenwood}. + */ + private Statement applyProbeIgnored(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + RavenwoodRuleImpl.init(RavenwoodRule.this); + try { + base.evaluate(); + } catch (Throwable t) { + if (hasIgnoreUnderRavenwoodAnnotation(description)) { + // This failure is expected, so eat the exception and report the + // assumption failure that test authors expect + Assume.assumeFalse(IS_UNDER_RAVENWOOD); } + throw t; + } finally { + RavenwoodRuleImpl.reset(RavenwoodRule.this); + } + + if (hasIgnoreUnderRavenwoodAnnotation(description) && IS_UNDER_RAVENWOOD) { + fail("Test was annotated with IgnoreUnderRavenwood, but it actually " + + "passed under Ravenwood; consider removing the annotation"); } } }; diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java index fb71e9d1ac6f..0ff6a1ad846b 100644 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java +++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java @@ -22,12 +22,10 @@ public class RavenwoodRuleImpl { } public static void init(RavenwoodRule rule) { - // Must be provided by impl to reference runtime internals - throw new UnsupportedOperationException(); + // No-op when running on a real device } public static void reset(RavenwoodRule rule) { - // Must be provided by impl to reference runtime internals - throw new UnsupportedOperationException(); + // No-op when running on a real device } } diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt index 9fcabd67f9dd..13908f1732e1 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/ravenwood-annotation-allowed-classes.txt @@ -1,8 +1,16 @@ # Only classes listed here can use the Ravenwood annotations. com.android.internal.util.ArrayUtils +com.android.internal.os.BatteryStatsHistory +com.android.internal.os.BatteryStatsHistory$TraceDelegate +com.android.internal.os.BatteryStatsHistory$VarintParceler +com.android.internal.os.BatteryStatsHistoryIterator +com.android.internal.os.Clock com.android.internal.os.LongArrayMultiStateCounter com.android.internal.os.LongArrayMultiStateCounter$LongArrayContainer +com.android.internal.os.MonotonicClock +com.android.internal.os.PowerStats +com.android.internal.os.PowerStats$Descriptor android.util.AtomicFile android.util.DataUnit @@ -25,10 +33,15 @@ android.util.TimeUtils android.util.Xml android.os.BatteryConsumer +android.os.BatteryStats$HistoryItem +android.os.BatteryStats$HistoryStepDetails +android.os.BatteryStats$HistoryTag +android.os.BatteryStats$ProcessStateChange android.os.Binder android.os.Binder$IdentitySupplier android.os.Broadcaster android.os.BundleMerger +android.os.ConditionVariable android.os.FileUtils android.os.FileUtils$MemoryPipe android.os.Handler diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp index b8cf13b11534..e2488a51bba1 100644 --- a/services/accessibility/Android.bp +++ b/services/accessibility/Android.bp @@ -19,6 +19,9 @@ java_library_static { defaults: [ "platform_service_defaults", ], + lint: { + error_checks: ["MissingPermissionAnnotation"], + }, srcs: [ ":services.accessibility-sources", "//frameworks/base/packages/SettingsLib/RestrictedLockUtils:SettingsLibRestrictedLockUtilsSrc", diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 0696807b3c8c..1d73843260cb 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -118,6 +118,7 @@ import java.util.Set; * This class represents an accessibility client - either an AccessibilityService or a UiAutomation. * It is responsible for behavior common to both types of clients. */ +@SuppressWarnings("MissingPermissionAnnotation") abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServiceConnection.Stub implements ServiceConnection, IBinder.DeathRecipient, KeyEventDispatcher.KeyEventFilter, FingerprintGestureDispatcher.FingerprintGestureClient { @@ -209,6 +210,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final ComponentName mComponentName; int mGenericMotionEventSources; + int mObservedMotionEventSources; // the events pending events to be dispatched to this service final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<>(); @@ -397,6 +399,19 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ mNotificationTimeout = info.notificationTimeout; mIsDefault = (info.flags & DEFAULT) != 0; mGenericMotionEventSources = info.getMotionEventSources(); + if (android.view.accessibility.Flags.motionEventObserving()) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING) + == PackageManager.PERMISSION_GRANTED) { + mObservedMotionEventSources = info.getObservedMotionEventSources(); + } else { + Slog.e( + LOG_TAG, + "Observing motion events requires" + + " android.Manifest.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING."); + mObservedMotionEventSources = 0; + } + } if (supportsFlagForNotImportantViews(info)) { if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) { @@ -1599,7 +1614,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int displayId = displays[i].getDisplayId(); onDisplayRemoved(displayId); } - if (Flags.cleanupA11yOverlays()) { + if (com.android.server.accessibility.Flags.cleanupA11yOverlays()) { detachAllOverlays(); } } @@ -1919,6 +1934,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ return (mGenericMotionEventSources & eventSourceWithoutClass) != 0; } + /** * Called by the invocation handler to notify the service that the * state of magnification has changed. diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 6cac6a47c77b..abcd8e2a2d7e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -57,6 +57,7 @@ import java.util.StringJoiner; * * NOTE: This class has to be created and poked only from the main thread. */ +@SuppressWarnings("MissingPermissionAnnotation") class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation { private static final String TAG = AccessibilityInputFilter.class.getSimpleName(); @@ -198,6 +199,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo // State tracking for generic MotionEvents is display-agnostic so we only need one. private GenericMotionEventStreamState mGenericMotionEventStreamState; private int mCombinedGenericMotionEventSources = 0; + private int mCombinedMotionEventObservedSources = 0; private EventStreamState mKeyboardStreamState; @@ -525,16 +527,33 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } if ((mEnabledFeatures & FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS) != 0) { - addFirstEventHandler(displayId, new BaseEventStreamTransformation() { - @Override - public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, - int policyFlags) { - if (!anyServiceWantsGenericMotionEvent(rawEvent) - || !mAms.sendMotionEventToListeningServices(rawEvent)) { - super.onMotionEvent(event, rawEvent, policyFlags); - } - } - }); + addFirstEventHandler( + displayId, + new BaseEventStreamTransformation() { + @Override + public void onMotionEvent( + MotionEvent event, MotionEvent rawEvent, int policyFlags) { + boolean passAlongEvent = true; + if (anyServiceWantsGenericMotionEvent(event)) { + // Some service wants this event, so try to deliver it to at least + // one service. + if (mAms.sendMotionEventToListeningServices(event)) { + // A service accepted this event, so prevent it from passing + // down the stream by default. + passAlongEvent = false; + } + // However, if a service is observing these events instead of + // consuming them then ensure + // it is always passed along to the next stage of the event stream. + if (anyServiceWantsToObserveMotionEvent(event)) { + passAlongEvent = true; + } + } + if (passAlongEvent) { + super.onMotionEvent(event, rawEvent, policyFlags); + } + } + }); } if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0 @@ -542,15 +561,14 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo || ((mEnabledFeatures & FLAG_FEATURE_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP) != 0) || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) { final MagnificationGestureHandler magnificationGestureHandler = - createMagnificationGestureHandler(displayId, - displayContext); + createMagnificationGestureHandler(displayId, displayContext); addFirstEventHandler(displayId, magnificationGestureHandler); mMagnificationGestureHandler.put(displayId, magnificationGestureHandler); } if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { - MotionEventInjector injector = new MotionEventInjector( - mContext.getMainLooper(), mAms.getTraceManager()); + MotionEventInjector injector = + new MotionEventInjector(mContext.getMainLooper(), mAms.getTraceManager()); addFirstEventHandler(displayId, injector); mMotionEventInjectors.put(displayId, injector); } @@ -923,6 +941,20 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } } + private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) { + // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing + // touch exploration. + if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) + && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { + return false; + } + final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; + return (mCombinedGenericMotionEventSources + & mCombinedMotionEventObservedSources + & eventSourceWithoutClass) + != 0; + } + private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) { // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing // touch exploration. @@ -938,6 +970,10 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo mCombinedGenericMotionEventSources = sources; } + public void setCombinedMotionEventObservedSources(int sources) { + mCombinedMotionEventObservedSources = sources; + } + /** * Keeps state of streams of events from all keyboard devices. */ diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 440e99632c86..3d8d7b738233 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -195,6 +195,7 @@ import java.util.function.Predicate; * event dispatch for {@link AccessibilityEvent}s generated across all processes * on the device. Events are dispatched to {@link AccessibilityService}s. */ +@SuppressWarnings("MissingPermissionAnnotation") public class AccessibilityManagerService extends IAccessibilityManager.Stub implements AbstractAccessibilityServiceConnection.SystemSupport, AccessibilityUserState.ServiceInfoChangeListener, @@ -2825,8 +2826,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS; } int combinedGenericMotionEventSources = 0; + int combinedMotionEventObservedSources = 0; for (AccessibilityServiceConnection connection : userState.mBoundServices) { combinedGenericMotionEventSources |= connection.mGenericMotionEventSources; + combinedMotionEventObservedSources |= connection.mObservedMotionEventSources; } if (combinedGenericMotionEventSources != 0) { flags |= AccessibilityInputFilter.FLAG_FEATURE_INTERCEPT_GENERIC_MOTION_EVENTS; @@ -2845,6 +2848,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags); mInputFilter.setCombinedGenericMotionEventSources( combinedGenericMotionEventSources); + mInputFilter.setCombinedMotionEventObservedSources( + combinedMotionEventObservedSources); } else { if (mHasInputFilter) { mHasInputFilter = false; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 40ca694dddd8..5ebe16115a4b 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -65,6 +65,7 @@ import java.util.Set; * passed to the service it represents as soon it is bound. It also serves as the * connection for the service. */ +@SuppressWarnings("MissingPermissionAnnotation") class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection { private static final String LOG_TAG = "AccessibilityServiceConnection"; diff --git a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java index a525e7c64bc3..b119d7d117cd 100644 --- a/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java +++ b/services/accessibility/java/com/android/server/accessibility/ActionReplacingCallback.java @@ -34,6 +34,7 @@ import java.util.List; * If we are stripping and/or replacing the actions from a window, we need to intercept the * nodes heading back to the service and swap out the actions. */ +@SuppressWarnings("MissingPermissionAnnotation") public class ActionReplacingCallback extends IAccessibilityInteractionConnectionCallback.Stub { private static final boolean DEBUG = false; private static final String LOG_TAG = "ActionReplacingCallback"; diff --git a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java index c9ec16edc54e..e10e87c51d59 100644 --- a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java +++ b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java @@ -33,6 +33,7 @@ import java.util.List; /** * Encapsulate fingerprint gesture logic */ +@SuppressWarnings("MissingPermissionAnnotation") public class FingerprintGestureDispatcher extends IFingerprintClientActiveCallback.Stub implements Handler.Callback{ private static final int MSG_REGISTER = 1; diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java index ab01fc324ed2..6aa4702ec7d5 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java @@ -64,6 +64,7 @@ import java.util.Set; * * TODO(241429275): Initialize this when a proxy is registered. */ +@SuppressWarnings("MissingPermissionAnnotation") public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection { private static final String LOG_TAG = "ProxyAccessibilityServiceConnection"; diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index 53c629a9ed2d..f69104db7c10 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -245,6 +245,7 @@ class UiAutomationManager { } } + @SuppressWarnings("MissingPermissionAnnotation") private class UiAutomationService extends AbstractAccessibilityServiceConnection { private final Handler mMainHandler; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java index e6af54bd937c..e11c36a91830 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java @@ -922,6 +922,7 @@ public class MagnificationConnectionManager implements disableWindowMagnification(displayId, true); } + @SuppressWarnings("MissingPermissionAnnotation") private class ConnectionCallback extends IMagnificationConnectionCallback.Stub implements IBinder.DeathRecipient { private boolean mExpiredDeathRecipient = false; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java index c63784a79ffd..db5b3133169a 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionWrapper.java @@ -246,6 +246,7 @@ class MagnificationConnectionWrapper { return new RemoteAnimationCallback(callback, trace); } + @SuppressWarnings("MissingPermissionAnnotation") private static class RemoteAnimationCallback extends IRemoteMagnificationAnimationCallback.Stub { private final MagnificationAnimationCallback mCallback; diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig index ab678d93c665..b5130a1c4cfc 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -27,3 +27,10 @@ flag { description: "Mitigation for view state reset to empty causing no save dialog to show issue" bug: "297976948" } + +flag { + name: "include_invisible_view_group_in_assist_structure" + namespace: "autofill" + description: "Mitigation for autofill providers miscalculating view visibility" + bug: "291795358" +} diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index 23e7ce68c1d0..9fdf5c2d0fc1 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -92,9 +92,10 @@ class CompanionDeviceShellCommand extends ShellCommand { int userId = getNextIntArgRequired(); String packageName = getNextArgRequired(); String address = getNextArgRequired(); + String deviceProfile = getNextArg(); final MacAddress macAddress = MacAddress.fromString(address); mService.createNewAssociation(userId, packageName, macAddress, - null, null, false); + null, deviceProfile, false); } break; @@ -350,7 +351,7 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" Print this help text."); pw.println(" list USER_ID"); pw.println(" List all Associations for a user."); - pw.println(" associate USER_ID PACKAGE MAC_ADDRESS"); + pw.println(" associate USER_ID PACKAGE MAC_ADDRESS [DEVICE_PROFILE]"); pw.println(" Create a new Association."); pw.println(" disassociate USER_ID PACKAGE MAC_ADDRESS"); pw.println(" Remove an existing Association."); diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 8dc6537d7e80..0d5cdcbe484c 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -439,6 +439,12 @@ public class VirtualDeviceManagerService extends SystemService { if (associationInfo == null) { throw new IllegalArgumentException("No association with ID " + associationId); } + if (!VIRTUAL_DEVICE_COMPANION_DEVICE_PROFILES + .contains(associationInfo.getDeviceProfile()) + && Flags.persistentDeviceIdApi()) { + throw new IllegalArgumentException("Unsupported CDM Association device profile " + + associationInfo.getDeviceProfile() + " for virtual device creation."); + } Objects.requireNonNull(params); Objects.requireNonNull(activityListener); Objects.requireNonNull(soundEffectListener); diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 34787a390d48..145303df7b0b 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -554,6 +554,10 @@ final class ContentCapturePerUserService if (mMaster.debug) Slog.d(mTag, "onActivityEvent(): no remote service"); return; } + if (mRemoteService.getServiceInterface() == null) { + if (mMaster.debug) Slog.d(mTag, "onActivityEvent(): remote service is dead or unbound"); + return; + } final ActivityEvent event = new ActivityEvent(activityId, componentName, type); if (mMaster.verbose) Slog.v(mTag, "onActivityEvent(): " + event); diff --git a/services/core/Android.bp b/services/core/Android.bp index 20a3b9ada85d..a0ccbf3acf0a 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -154,6 +154,7 @@ java_library_static { static_libs: [ "android.frameworks.location.altitude-V1-java", // AIDL + "android.frameworks.vibrator-V1-java", // AIDL "android.hardware.authsecret-V1.0-java", "android.hardware.authsecret-V1-java", "android.hardware.boot-V1.0-java", // HIDL @@ -165,7 +166,7 @@ java_library_static { "android.hardware.health-V1.0-java", // HIDL "android.hardware.health-V2.0-java", // HIDL "android.hardware.health-V2.1-java", // HIDL - "android.hardware.health-V2-java", // AIDL + "android.hardware.health-V3-java", // AIDL "android.hardware.health-translate-java", "android.hardware.light-V1-java", "android.hardware.security.rkp-V3-java", diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 09e79860b45c..136692eb90d5 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -1426,14 +1426,19 @@ public abstract class PackageManagerInternal { /** * Checks if package is quarantined for a specific user. + * + * @throws PackageManager.NameNotFoundException if the package is not found */ - public abstract boolean isPackageQuarantined(@NonNull String packageName, - @UserIdInt int userId); + public abstract boolean isPackageQuarantined(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException; /** * Checks if package is stopped for a specific user. + * + * @throws PackageManager.NameNotFoundException if the package is not found */ - public abstract boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId); + public abstract boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException; /** * Sends the PACKAGE_RESTARTED broadcast. diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 33726d17da62..5a44ac803cb4 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -974,6 +974,7 @@ public final class BatteryService extends SystemService { unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw); } break; case "get": { + final int opts = parseOptions(shell); final String key = shell.getNextArg(); if (key == null) { pw.println("No property specified"); @@ -1007,11 +1008,17 @@ public final class BatteryService extends SystemService { break; case "current_now": if (batteryServiceSupportCurrentAdbCommand()) { + if ((opts & OPTION_FORCE_UPDATE) != 0) { + updateHealthInfo(); + } pw.println(mHealthInfo.batteryCurrentMicroamps); } break; case "current_average": if (batteryServiceSupportCurrentAdbCommand()) { + if ((opts & OPTION_FORCE_UPDATE) != 0) { + updateHealthInfo(); + } pw.println(mHealthInfo.batteryCurrentAverageMicroamps); } break; @@ -1125,6 +1132,14 @@ public final class BatteryService extends SystemService { return 0; } + private void updateHealthInfo() { + try { + mHealthServiceWrapper.scheduleUpdate(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to update health service data.", e); + } + } + private void setChargerAcOnline(boolean online, boolean forceUpdate) { if (!mUpdatesStopped) { copyV1Battery(mLastHealthInfo, mHealthInfo); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 71916843fe0b..df8f17ac9d7c 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -5175,6 +5175,8 @@ public final class ActiveServices { return null; } + final long startTimeNs = SystemClock.elapsedRealtimeNanos(); + if (DEBUG_SERVICE) { Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent + " fg=" + r.fgRequired); } @@ -5333,9 +5335,14 @@ public final class ActiveServices { bringDownServiceLocked(r, enqueueOomAdj); return msg; } + mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app, r, + hostingRecord, true); if (isolated) { r.isolationHostProc = app; } + } else { + mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app, r, + hostingRecord, false); } if (r.fgRequired) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ac173f3b5b7a..2ee39c577977 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -421,6 +421,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.os.TimeoutRecord; import com.android.internal.os.TransferPipe; import com.android.internal.os.Zygote; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.policy.AttributeCache; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; @@ -468,7 +469,6 @@ import com.android.server.pm.UserManagerInternal; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.SELinuxUtil; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.snapshot.PackageDataSnapshot; import com.android.server.power.stats.BatteryStatsImpl; import com.android.server.sdksandbox.SdkSandboxManagerLocal; @@ -1103,9 +1103,51 @@ public class ActivityManagerService extends IActivityManager.Stub private final ActivityMetricsLaunchObserver mActivityLaunchObserver = new ActivityMetricsLaunchObserver() { + + @Override + public void onIntentStarted(@NonNull Intent intent, long timestampNanos) { + synchronized (this) { + mProcessList.getAppStartInfoTracker().onIntentStarted(intent, timestampNanos); + } + } + @Override - public void onActivityLaunched(long id, ComponentName name, int temperature) { + public void onIntentFailed(long id) { + mProcessList.getAppStartInfoTracker().onIntentFailed(id); + } + + @Override + public void onActivityLaunched(long id, ComponentName name, int temperature, int userId) { mAppProfiler.onActivityLaunched(); + synchronized (ActivityManagerService.this) { + ProcessRecord record = null; + try { + record = getProcessRecordLocked(name.getPackageName(), mContext + .getPackageManager().getPackageUidAsUser(name.getPackageName(), 0, + userId)); + } catch (NameNotFoundException nnfe) { + // Ignore, record will be lost. + } + mProcessList.getAppStartInfoTracker().onActivityLaunched(id, name, temperature, + record); + } + } + + @Override + public void onActivityLaunchCancelled(long id) { + mProcessList.getAppStartInfoTracker().onActivityLaunchCancelled(id); + } + + @Override + public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos, + int launchMode) { + mProcessList.getAppStartInfoTracker().onActivityLaunchFinished(id, name, + timestampNanos, launchMode); + } + + @Override + public void onReportFullyDrawn(long id, long timestampNanos) { + mProcessList.getAppStartInfoTracker().onReportFullyDrawn(id, timestampNanos); } }; @@ -4488,13 +4530,13 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") private void attachApplicationLocked(@NonNull IApplicationThread thread, int pid, int callingUid, long startSeq) { - // Find the application record that is being attached... either via // the pid if we are running in multiple processes, or just pull the // next app record if we are emulating process with anonymous threads. ProcessRecord app; long startTime = SystemClock.uptimeMillis(); long bindApplicationTimeMillis; + long bindApplicationTimeNanos; if (pid != MY_PID && pid >= 0) { synchronized (mPidsSelfLocked) { app = mPidsSelfLocked.get(pid); @@ -4698,6 +4740,7 @@ public class ActivityManagerService extends IActivityManager.Stub checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); bindApplicationTimeMillis = SystemClock.uptimeMillis(); + bindApplicationTimeNanos = SystemClock.elapsedRealtimeNanos(); mAtmInternal.preBindApplication(app.getWindowProcessController()); final ActiveInstrumentation instr2 = app.getActiveInstrumentation(); if (mPlatformCompat != null) { @@ -4754,6 +4797,8 @@ public class ActivityManagerService extends IActivityManager.Stub } app.setBindApplicationTime(bindApplicationTimeMillis); + mProcessList.getAppStartInfoTracker() + .reportBindApplicationTimeNanos(app, bindApplicationTimeNanos); // Make app active after binding application or client may be running requests (e.g // starting activities) before it is ready. @@ -9799,12 +9844,12 @@ public class ActivityManagerService extends IActivityManager.Stub final int uid = enforceDumpPermissionForPackage(packageName, userId, callingUid, "getHistoricalProcessStartReasons"); if (uid != INVALID_UID) { - mProcessList.mAppStartInfoTracker.getStartInfo( + mProcessList.getAppStartInfoTracker().getStartInfo( packageName, userId, callingPid, maxNum, results); } } else { // If no package name is given, use the caller's uid as the filter uid. - mProcessList.mAppStartInfoTracker.getStartInfo( + mProcessList.getAppStartInfoTracker().getStartInfo( packageName, callingUid, callingPid, maxNum, results); } return new ParceledListSlice<ApplicationStartInfo>(results); @@ -9822,7 +9867,7 @@ public class ActivityManagerService extends IActivityManager.Stub } final int callingUid = Binder.getCallingUid(); - mProcessList.mAppStartInfoTracker.addStartInfoCompleteListener(listener, callingUid); + mProcessList.getAppStartInfoTracker().addStartInfoCompleteListener(listener, callingUid); } @@ -9836,7 +9881,7 @@ public class ActivityManagerService extends IActivityManager.Stub } final int callingUid = Binder.getCallingUid(); - mProcessList.mAppStartInfoTracker.clearStartInfoCompleteListener(callingUid, true); + mProcessList.getAppStartInfoTracker().clearStartInfoCompleteListener(callingUid, true); } @Override @@ -10138,7 +10183,7 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(); if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); - mProcessList.mAppStartInfoTracker.dumpHistoryProcessStartInfo(pw, dumpPackage); + mProcessList.getAppStartInfoTracker().dumpHistoryProcessStartInfo(pw, dumpPackage); pw.println("-------------------------------------------------------------------------------"); mProcessList.mAppExitInfoTracker.dumpHistoryProcessExitInfo(pw, dumpPackage); } @@ -10541,7 +10586,7 @@ public class ActivityManagerService extends IActivityManager.Stub dumpPackage = args[opti]; opti++; } - mProcessList.mAppStartInfoTracker.dumpHistoryProcessStartInfo(pw, dumpPackage); + mProcessList.getAppStartInfoTracker().dumpHistoryProcessStartInfo(pw, dumpPackage); } else if ("exit-info".equals(cmd)) { if (opti < args.length) { dumpPackage = args[opti]; @@ -13831,6 +13876,7 @@ public class ActivityManagerService extends IActivityManager.Stub // activity manager to announce its creation. public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId, @BackupDestination int backupDestination) { + long startTimeNs = SystemClock.elapsedRealtimeNanos(); if (DEBUG_BACKUP) { Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode + " targetUserId=" + targetUserId + " callingUid = " + Binder.getCallingUid() @@ -13906,15 +13952,20 @@ public class ActivityManagerService extends IActivityManager.Stub ? new ComponentName(app.packageName, app.backupAgentName) : new ComponentName("android", "FullBackupAgent"); - // startProcessLocked() returns existing proc's record if it's already running - ProcessRecord proc = startProcessLocked(app.processName, app, - false, 0, - new HostingRecord(HostingRecord.HOSTING_TYPE_BACKUP, hostingName), - ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false); + ProcessRecord proc = getProcessRecordLocked(app.processName, app.uid); + boolean isProcessStarted = proc != null; + if (!isProcessStarted) { + proc = startProcessLocked(app.processName, app, + false, 0, + new HostingRecord(HostingRecord.HOSTING_TYPE_BACKUP, hostingName), + ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS, false, false); + } if (proc == null) { Slog.e(TAG, "Unable to start backup agent process " + r); return false; } + mProcessList.getAppStartInfoTracker().handleProcessBackupStart(startTimeNs, proc, r, + !isProcessStarted); // If the app is a regular app (uid >= 10000) and not the system server or phone // process, etc, then mark it as being in full backup so that certain calls to the @@ -18741,8 +18792,12 @@ public class ActivityManagerService extends IActivityManager.Stub // If the process is known as top app, set a hint so when the process is // started, the top priority can be applied immediately to avoid cpu being // preempted by other processes before attaching the process of top app. - startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */, - new HostingRecord(hostingType, hostingName, isTop), + final long startTimeNs = SystemClock.elapsedRealtimeNanos(); + HostingRecord hostingRecord = + new HostingRecord(hostingType, hostingName, isTop); + ProcessRecord rec = getProcessRecordLocked(processName, info.uid); + ProcessRecord app = startProcessLocked(processName, info, knownToBeDead, + 0 /* intentFlags */, hostingRecord, ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */, false /* isolated */); } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index f3b2ef34ad6d..ae0cd65b2770 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -1362,7 +1362,7 @@ final class ActivityManagerShellCommand extends ShellCommand { } userId = user.id; } - mInternal.mProcessList.mAppStartInfoTracker + mInternal.mProcessList.getAppStartInfoTracker() .clearHistoryProcessStartInfo(packageName, userId); return 0; } diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java index edca74fae0e4..82e554e67b7e 100644 --- a/services/core/java/com/android/server/am/AppStartInfoTracker.java +++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java @@ -22,11 +22,12 @@ import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import android.app.ActivityOptions; +import android.annotation.NonNull; import android.app.ApplicationStartInfo; import android.app.Flags; import android.app.IApplicationStartInfoCompleteListener; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -138,6 +139,15 @@ public final class AppStartInfoTracker { /** The path to the historical proc start info file, persisted in the storage. */ @VisibleForTesting File mProcStartInfoFile; + + /** + * Temporary list of records that have not been completed. + * + * Key is timestamp of launch from {@link #ActivityMetricsLaunchObserver}. + */ + @GuardedBy("mLock") + private ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>(); + AppStartInfoTracker() { mCallbacks = new SparseArray<>(); mData = new ProcessMap<AppStartInfoContainer>(); @@ -174,68 +184,99 @@ public final class AppStartInfoTracker { }); } - void handleProcessColdStarted(long startTimeNs, HostingRecord hostingRecord, - ProcessRecord app) { + void onIntentStarted(@NonNull Intent intent, long timestampNanos) { synchronized (mLock) { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); - addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); - start.addStartupTimestamp( - ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs); - start.addStartupTimestamp( - ApplicationStartInfo.START_TIMESTAMP_FORK, app.getStartElapsedTime()); - start.setStartType(ApplicationStartInfo.START_TYPE_COLD); - start.setReason(ApplicationStartInfo.START_REASON_OTHER); - addStartInfoLocked(start); + start.setIntent(intent); + start.setStartType(ApplicationStartInfo.START_TYPE_UNSET); + start.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_LAUNCH, timestampNanos); + if (intent != null && intent.getCategories() != null + && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { + start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER); + } else { + start.setReason(ApplicationStartInfo.START_REASON_START_ACTIVITY); + } + mInProgRecords.put(timestampNanos, start); } } - public void handleProcessActivityWarmOrHotStarted(long startTimeNs, - ActivityOptions activityOptions, Intent intent) { + void onIntentFailed(long id) { synchronized (mLock) { if (!mEnabled) { return; } - ApplicationStartInfo start = new ApplicationStartInfo(); - start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); - start.addStartupTimestamp( - ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs); - start.setIntent(intent); - start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER); - if (activityOptions != null) { - start.setProcessName(activityOptions.getPackageName()); + if (!mInProgRecords.containsKey(id)) { + return; } - start.setStartType(ApplicationStartInfo.START_TYPE_WARM); - if (intent != null && intent.getCategories() != null - && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { - start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER); + mInProgRecords.get(id).setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR); + mInProgRecords.remove(id); + } + } + + void onActivityLaunched(long id, ComponentName name, long temperature, ProcessRecord app) { + synchronized (mLock) { + if (!mEnabled) { + return; + } + if (!mInProgRecords.containsKey(id)) { + return; + } + if (app != null) { + ApplicationStartInfo info = mInProgRecords.get(id); + info.setStartType((int) temperature); + addBaseFieldsFromProcessRecord(info, app); + addStartInfoLocked(info); } else { - start.setReason(ApplicationStartInfo.START_REASON_START_ACTIVITY); + mInProgRecords.remove(id); } - addStartInfoLocked(start); } } - public void handleProcessActivityStartedFromRecents(long startTimeNs, - ActivityOptions activityOptions) { + void onActivityLaunchCancelled(long id) { synchronized (mLock) { if (!mEnabled) { return; } - ApplicationStartInfo start = new ApplicationStartInfo(); - start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); - start.addStartupTimestamp( - ApplicationStartInfo.START_TIMESTAMP_LAUNCH, startTimeNs); - if (activityOptions != null) { - start.setIntent(activityOptions.getResultData()); - start.setProcessName(activityOptions.getPackageName()); + if (!mInProgRecords.containsKey(id)) { + return; } - start.setReason(ApplicationStartInfo.START_REASON_LAUNCHER_RECENTS); - start.setStartType(ApplicationStartInfo.START_TYPE_WARM); - addStartInfoLocked(start); + ApplicationStartInfo info = mInProgRecords.get(id); + info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR); + mInProgRecords.remove(id); + } + } + + void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos, + int launchMode) { + synchronized (mLock) { + if (!mEnabled) { + return; + } + if (!mInProgRecords.containsKey(id)) { + return; + } + ApplicationStartInfo info = mInProgRecords.get(id); + info.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN); + info.setLaunchMode(launchMode); + } + } + + void onReportFullyDrawn(long id, long timestampNanos) { + synchronized (mLock) { + if (!mEnabled) { + return; + } + if (!mInProgRecords.containsKey(id)) { + return; + } + ApplicationStartInfo info = mInProgRecords.get(id); + info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN, + timestampNanos); + mInProgRecords.remove(id); } } @@ -347,7 +388,8 @@ public final class AppStartInfoTracker { ApplicationStartInfo.START_TIMESTAMP_APPLICATION_ONCREATE); } - void reportBindApplicationTimeNanos(ProcessRecord app, long timeNs) { + /** Report a bind application timestamp to add to {@link ApplicationStartInfo}. */ + public void reportBindApplicationTimeNanos(ProcessRecord app, long timeNs) { addTimestampToStart(app, timeNs, ApplicationStartInfo.START_TIMESTAMP_BIND_APPLICATION); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index f8f3d82556fa..ace2cfd8a307 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -33,6 +33,7 @@ import android.annotation.EnforcePermission; import android.annotation.NonNull; import android.annotation.RequiresNoPermission; import android.annotation.SuppressLint; +import android.app.AlarmManager; import android.app.StatsManager; import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothActivityEnergyInfo; @@ -427,13 +428,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, aggregatedPowerStatsConfig); mPowerStatsAggregator = new PowerStatsAggregator(aggregatedPowerStatsConfig, mStats.getHistory()); - final long aggregatedPowerStatsSpanDuration = context.getResources().getInteger( - com.android.internal.R.integer.config_aggregatedPowerStatsSpanDuration); - final long powerStatsAggregationPeriod = context.getResources().getInteger( - com.android.internal.R.integer.config_powerStatsAggregationPeriod); - mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator, - aggregatedPowerStatsSpanDuration, powerStatsAggregationPeriod, mPowerStatsStore, - Clock.SYSTEM_CLOCK, mMonotonicClock, mHandler, mStats); + mPowerStatsScheduler = createPowerStatsScheduler(mContext); PowerStatsExporter powerStatsExporter = new PowerStatsExporter(mPowerStatsStore, mPowerStatsAggregator); mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, @@ -445,6 +440,23 @@ public final class BatteryStatsService extends IBatteryStats.Stub mConfigFile = new AtomicFile(new File(systemDir, "battery_usage_stats_config")); } + private PowerStatsScheduler createPowerStatsScheduler(Context context) { + final long aggregatedPowerStatsSpanDuration = context.getResources().getInteger( + com.android.internal.R.integer.config_aggregatedPowerStatsSpanDuration); + final long powerStatsAggregationPeriod = context.getResources().getInteger( + com.android.internal.R.integer.config_powerStatsAggregationPeriod); + PowerStatsScheduler.AlarmScheduler alarmScheduler = + (triggerAtMillis, tag, onAlarmListener, aHandler) -> { + AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class); + alarmManager.set(AlarmManager.ELAPSED_REALTIME, triggerAtMillis, tag, + onAlarmListener, aHandler); + }; + return new PowerStatsScheduler(mStats::schedulePowerStatsSampleCollection, + mPowerStatsAggregator, aggregatedPowerStatsSpanDuration, + powerStatsAggregationPeriod, mPowerStatsStore, alarmScheduler, Clock.SYSTEM_CLOCK, + mMonotonicClock, () -> mStats.getHistory().getStartTime(), mHandler); + } + private AggregatedPowerStatsConfig getAggregatedPowerStatsConfig() { AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig(); config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU) diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 903cb7bcfaed..982076dd7c9a 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -30,7 +30,6 @@ import android.widget.WidgetFlags; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import java.util.ArrayList; import java.util.HashMap; @@ -164,12 +163,6 @@ final class CoreSettingsObserver extends ContentObserver { WidgetFlags.MAGNIFIER_ASPECT_RATIO_DEFAULT)); sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>( - DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION, - SystemUiDeviceConfigFlags.KEY_REMOTEVIEWS_ADAPTER_CONVERSION, boolean.class, - SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT)); - - sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>( TextFlags.NAMESPACE, TextFlags.ENABLE_NEW_CONTEXT_MENU, TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU, boolean.class, TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT)); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 4ff34b1d7faa..3156e9da0ae9 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -498,7 +498,7 @@ public final class ProcessList { /** Manages the {@link android.app.ApplicationStartInfo} records. */ @GuardedBy("mAppStartInfoTracker") - final AppStartInfoTracker mAppStartInfoTracker = new AppStartInfoTracker(); + private final AppStartInfoTracker mAppStartInfoTracker = new AppStartInfoTracker(); /** * The currently running SDK sandbox processes for a uid. @@ -1523,6 +1523,10 @@ public final class ProcessList { return mCachedRestoreLevel; } + AppStartInfoTracker getAppStartInfoTracker() { + return mAppStartInfoTracker; + } + /** * Set the out-of-memory badness adjustment for a process. * If {@code pid <= 0}, this method will be a no-op. @@ -2572,6 +2576,7 @@ public final class ProcessList { boolean isSdkSandbox, int sdkSandboxUid, String sdkSandboxClientAppPackage, String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) { long startTime = SystemClock.uptimeMillis(); + final long startTimeNs = SystemClock.elapsedRealtimeNanos(); ProcessRecord app; if (!isolated) { app = getProcessRecordLocked(processName, info.uid); diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index b1825380bbdc..32d5cf587e0c 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -152,7 +152,6 @@ public final class GameManagerService extends IGameManagerService.Stub { private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME = "game_mode_intervention.list"; - private final Context mContext; private final Object mLock = new Object(); private final Object mDeviceConfigLock = new Object(); @@ -184,6 +183,7 @@ public final class GameManagerService extends IGameManagerService.Stub { @GuardedBy("mUidObserverLock") private final Set<Integer> mForegroundGameUids = new HashSet<>(); private final GameManagerServiceSystemPropertiesWrapper mSysProps; + private float mGameDefaultFrameRateValue; @VisibleForTesting static class Injector { @@ -1559,6 +1559,10 @@ public final class GameManagerService extends IGameManagerService.Stub { mPowerManagerInternal.setPowerMode(Mode.GAME_LOADING, false); Slog.v(TAG, "Game power mode OFF (game manager service start/restart)"); mPowerManagerInternal.setPowerMode(Mode.GAME, false); + + mGameDefaultFrameRateValue = (float) mSysProps.getInt( + PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE, 60); + Slog.v(TAG, "Game Default Frame Rate : " + mGameDefaultFrameRateValue); } private void sendUserMessage(int userId, int what, String eventForLog, int delayMillis) { @@ -2217,8 +2221,7 @@ public final class GameManagerService extends IGameManagerService.Stub { } if (gameDefaultFrameRate()) { gameDefaultFrameRate = isGameDefaultFrameRateEnabled - ? (float) mSysProps.getInt( - PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE, 0) : 0.0f; + ? mGameDefaultFrameRateValue : 0.0f; } return gameDefaultFrameRate; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 44cb1367928d..290bb7e92c69 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -5106,7 +5106,7 @@ public class AudioService extends IAudioService.Stub private void setMasterMuteInternalNoCallerCheck( boolean mute, int flags, int userId, String eventSource) { if (DEBUG_VOL) { - Log.d(TAG, TextUtils.formatSimple("Master mute %s, %d, user=%d from %s", + Log.d(TAG, TextUtils.formatSimple("Master mute %s, flags 0x%x, userId=%d from %s", mute, flags, userId, eventSource)); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 5084b602bff2..578d9dc2aede 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -722,6 +722,7 @@ public class FaceService extends SystemService { if (Flags.faceVhalFeature() && Utils.isVirtualEnabled(getContext())) { if (virtualAt != -1) { //only virtual instance should be returned + Slog.i(TAG, "virtual hal is used"); return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt))); } else { Slog.e(TAG, "Could not find virtual interface while it is enabled"); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 5ce0c8b384ef..769554315b6e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -1057,6 +1057,7 @@ public class FingerprintService extends SystemService { if (Utils.isVirtualEnabled(getContext())) { if (virtualAt != -1) { //only virtual instance should be returned + Slog.i(TAG, "virtual hal is used"); return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt))); } else { Slog.e(TAG, "Could not find virtual interface while it is enabled"); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 1e5e147749a2..df179a9b6d65 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -1294,8 +1294,8 @@ public class SyncManager { if (android.content.pm.Flags.stayStopped()) { try { return mPackageManagerInternal.isPackageStopped(packageName, userId); - } catch (IllegalArgumentException e) { - Log.d(TAG, "Couldn't determine stopped state for unknown package: " + packageName); + } catch (NameNotFoundException e) { + Log.e(TAG, "Couldn't determine stopped state for unknown package: " + packageName); } } return false; diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index dff14b5fbdd0..6ec6a123a4e7 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -327,7 +327,7 @@ public final class DeviceStateManagerService extends SystemService { Optional<DeviceState> getOverrideState() { synchronized (mLock) { if (mActiveOverride.isPresent()) { - return getStateLocked(mActiveOverride.get().getRequestedState()); + return getStateLocked(mActiveOverride.get().getRequestedStateIdentifier()); } return Optional.empty(); } @@ -342,7 +342,7 @@ public final class DeviceStateManagerService extends SystemService { Optional<DeviceState> getOverrideBaseState() { synchronized (mLock) { if (mActiveBaseStateOverride.isPresent()) { - return getStateLocked(mActiveBaseStateOverride.get().getRequestedState()); + return getStateLocked(mActiveBaseStateOverride.get().getRequestedStateIdentifier()); } return Optional.empty(); } @@ -499,6 +499,7 @@ public final class DeviceStateManagerService extends SystemService { * @return {@code true} if the pending state has changed as a result of this call, {@code false} * otherwise. */ + @GuardedBy("mLock") private boolean updatePendingStateLocked() { if (mPendingState.isPresent()) { // Have pending state, can not configure a new state until the state is committed. @@ -507,7 +508,8 @@ public final class DeviceStateManagerService extends SystemService { final DeviceState stateToConfigure; if (mActiveOverride.isPresent()) { - stateToConfigure = getStateLocked(mActiveOverride.get().getRequestedState()).get(); + stateToConfigure = getStateLocked( + mActiveOverride.get().getRequestedStateIdentifier()).get(); } else if (mBaseState.isPresent() && isSupportedStateLocked(mBaseState.get().getIdentifier())) { // Base state could have recently become unsupported after a change in supported states. @@ -599,7 +601,7 @@ public final class DeviceStateManagerService extends SystemService { // requested state is committed. OverrideRequest activeRequest = mActiveOverride.orElse(null); if (activeRequest != null - && activeRequest.getRequestedState() == newState.getIdentifier()) { + && activeRequest.getRequestedStateIdentifier() == newState.getIdentifier()) { ProcessRecord processRecord = mProcessRecords.get(activeRequest.getPid()); if (processRecord != null) { processRecord.notifyRequestActiveAsync(activeRequest.getToken()); @@ -666,21 +668,21 @@ public final class DeviceStateManagerService extends SystemService { case STATUS_ACTIVE: mActiveOverride = Optional.of(request); mDeviceStateNotificationController.showStateActiveNotificationIfNeeded( - request.getRequestedState(), request.getUid()); + request.getRequestedStateIdentifier(), request.getUid()); break; case STATUS_CANCELED: if (mActiveOverride.isPresent() && mActiveOverride.get() == request) { mActiveOverride = Optional.empty(); mDeviceStateNotificationController.cancelNotification( - request.getRequestedState()); + request.getRequestedStateIdentifier()); if ((flags & FLAG_THERMAL_CRITICAL) == FLAG_THERMAL_CRITICAL) { mDeviceStateNotificationController .showThermalCriticalNotificationIfNeeded( - request.getRequestedState()); + request.getRequestedStateIdentifier()); } else if ((flags & FLAG_POWER_SAVE_ENABLED) == FLAG_POWER_SAVE_ENABLED) { mDeviceStateNotificationController .showPowerSaveNotificationIfNeeded( - request.getRequestedState()); + request.getRequestedStateIdentifier()); } } break; @@ -723,7 +725,7 @@ public final class DeviceStateManagerService extends SystemService { */ @GuardedBy("mLock") private void enableBaseStateRequestLocked(OverrideRequest request) { - setBaseState(request.getRequestedState()); + setBaseState(request.getRequestedStateIdentifier()); mActiveBaseStateOverride = Optional.of(request); ProcessRecord processRecord = mProcessRecords.get(request.getPid()); processRecord.notifyRequestActiveAsync(request.getToken()); @@ -762,6 +764,11 @@ public final class DeviceStateManagerService extends SystemService { synchronized (mLock) { mProcessRecords.remove(processRecord.mPid); mOverrideRequestController.handleProcessDied(processRecord.mPid); + + if (shouldCancelOverrideRequestWhenRequesterNotOnTop()) { + OverrideRequest request = mActiveOverride.get(); + mOverrideRequestController.cancelRequest(request); + } } } @@ -787,7 +794,7 @@ public final class DeviceStateManagerService extends SystemService { } OverrideRequest request = new OverrideRequest(token, callingPid, callingUid, - state, flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay if (!hasControlDeviceStatePermission && mRearDisplayState != null @@ -848,7 +855,7 @@ public final class DeviceStateManagerService extends SystemService { } OverrideRequest request = new OverrideRequest(token, callingPid, callingUid, - state, flags, OVERRIDE_REQUEST_TYPE_BASE_STATE); + deviceState.get(), flags, OVERRIDE_REQUEST_TYPE_BASE_STATE); mOverrideRequestController.addBaseStateRequest(request); } } @@ -1318,7 +1325,7 @@ public final class DeviceStateManagerService extends SystemService { if (mActiveOverride.isEmpty()) { return false; } - int identifier = mActiveOverride.get().getRequestedState(); + int identifier = mActiveOverride.get().getRequestedStateIdentifier(); DeviceState deviceState = mDeviceStates.get(identifier); return deviceState.hasFlag(DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP); } diff --git a/services/core/java/com/android/server/devicestate/OverrideRequest.java b/services/core/java/com/android/server/devicestate/OverrideRequest.java index 74cf184e826e..20485c1ac102 100644 --- a/services/core/java/com/android/server/devicestate/OverrideRequest.java +++ b/services/core/java/com/android/server/devicestate/OverrideRequest.java @@ -17,6 +17,7 @@ package com.android.server.devicestate; import android.annotation.IntDef; +import android.annotation.NonNull; import android.hardware.devicestate.DeviceStateRequest; import android.os.IBinder; @@ -32,7 +33,8 @@ final class OverrideRequest { private final IBinder mToken; private final int mPid; private final int mUid; - private final int mRequestedState; + @NonNull + private final DeviceState mRequestedState; @DeviceStateRequest.RequestFlags private final int mFlags; @OverrideRequestType @@ -69,7 +71,7 @@ final class OverrideRequest { @Retention(RetentionPolicy.SOURCE) public @interface OverrideRequestType {} - OverrideRequest(IBinder token, int pid, int uid, int requestedState, + OverrideRequest(IBinder token, int pid, int uid, @NonNull DeviceState requestedState, @DeviceStateRequest.RequestFlags int flags, @OverrideRequestType int requestType) { mToken = token; mPid = pid; @@ -91,10 +93,15 @@ final class OverrideRequest { return mUid; } - int getRequestedState() { + @NonNull + DeviceState getRequestedDeviceState() { return mRequestedState; } + int getRequestedStateIdentifier() { + return mRequestedState.getIdentifier(); + } + @DeviceStateRequest.RequestFlags int getFlags() { return mFlags; diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java index 46f0bc0d9805..f5f2fa8cabdc 100644 --- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java +++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java @@ -204,6 +204,12 @@ final class OverrideRequestController { } if (mRequest != null && mRequest.getPid() == pid) { + if (mRequest.getRequestedDeviceState().hasFlag( + DeviceState.FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP)) { + cancelCurrentRequestLocked(); + return; + } + if (mStickyRequestsAllowed) { // Do not cancel the requests now because sticky requests are allowed. These // requests will be cancelled on a call to cancelStickyRequests(). @@ -219,7 +225,7 @@ final class OverrideRequestController { * listener of all changes to request status as a result of this change. */ void handleBaseStateChanged(int state) { - if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedState()) { + if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedStateIdentifier()) { cancelBaseStateOverrideRequest(); } if (mRequest == null) { @@ -246,11 +252,12 @@ final class OverrideRequestController { flags |= isThermalCritical ? FLAG_THERMAL_CRITICAL : 0; flags |= isPowerSaveEnabled ? FLAG_POWER_SAVE_ENABLED : 0; if (mBaseStateRequest != null && !contains(newSupportedStates, - mBaseStateRequest.getRequestedState())) { + mBaseStateRequest.getRequestedStateIdentifier())) { cancelCurrentBaseStateRequestLocked(flags); } - if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) { + if (mRequest != null && !contains(newSupportedStates, + mRequest.getRequestedStateIdentifier())) { cancelCurrentRequestLocked(flags); } } @@ -262,7 +269,7 @@ final class OverrideRequestController { pw.println("Override Request active: " + requestActive); if (requestActive) { pw.println("Request: mPid=" + overrideRequest.getPid() - + ", mRequestedState=" + overrideRequest.getRequestedState() + + ", mRequestedState=" + overrideRequest.getRequestedStateIdentifier() + ", mFlags=" + overrideRequest.getFlags() + ", mStatus=" + statusToString(STATUS_ACTIVE)); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 168715713f8d..5831b29437dc 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -194,10 +194,14 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() { @Override public void onComplete(int result) { - if (result != HdmiControlManager.RESULT_SUCCESS) { + if (!mService.getLocalActiveSource().isValid() + && result != HdmiControlManager.RESULT_SUCCESS) { mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource( getDeviceInfo().getLogicalAddress(), getDeviceInfo().getPhysicalAddress())); + updateActiveSource(getDeviceInfo().getLogicalAddress(), + getDeviceInfo().getPhysicalAddress(), + "RequestActiveSourceAction#finishWithCallback()"); } } })); @@ -257,6 +261,7 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { if (isAlreadyActiveSource(targetDevice, targetAddress, callback)) { return; } + removeAction(RequestActiveSourceAction.class); if (targetAddress == Constants.ADDR_INTERNAL) { handleSelectInternalSource(); // Switching to internal source is always successful even when CEC control is disabled. diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index f526dbe9c66d..4089a81dfc20 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -215,10 +215,20 @@ public abstract class InputMethodManagerInternal { /** * Switch the keyboard layout in response to a keyboard shortcut. * - * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the - * previous subtype + * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the + * previous subtype + * @param displayId the display to which the keyboard layout switch shortcut is + * dispatched. Note that there is no guarantee that an IME is + * associated with this display. This is more or less than a hint for + * cases when no IME is running for the given targetWindowToken. There + * is a longstanding discussion whether we should allow users to + * rotate keyboard layout even when there is no edit field, and this + * displayID would be helpful for such a situation. + * @param targetWindowToken the window token to which other keys are being sent while handling + * this shortcut. */ - public abstract void switchKeyboardLayout(int direction); + public abstract void onSwitchKeyboardLayoutShortcut(int direction, int displayId, + IBinder targetWindowToken); /** * Returns true if any InputConnection is currently active. @@ -314,7 +324,8 @@ public abstract class InputMethodManagerInternal { } @Override - public void switchKeyboardLayout(int direction) { + public void onSwitchKeyboardLayoutShortcut(int direction, int displayId, + IBinder targetWindowToken) { } @Override diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 30e9f5bd2a14..c440a6461de2 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -5763,7 +5763,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public void switchKeyboardLayout(int direction) { + public void onSwitchKeyboardLayoutShortcut(int direction, int displayId, + IBinder targetWindowToken) { synchronized (ImfLock.class) { switchKeyboardLayoutLocked(direction); } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index ec858ee296e1..f2dcba45e312 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -59,6 +59,7 @@ import android.util.apk.SourceStampVerifier; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; @@ -67,7 +68,6 @@ import com.android.server.integrity.model.IntegrityCheckResult; import com.android.server.integrity.model.RuleMetadata; import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.parsing.PackageParser2; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import java.io.ByteArrayInputStream; import java.io.File; diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index bab3cbea108e..9c27c22dfd00 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -18,6 +18,7 @@ package com.android.server.location.contexthub; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.contexthub.HostEndpointInfo; +import android.hardware.contexthub.MessageDeliveryStatus; import android.hardware.contexthub.NanSessionRequest; import android.hardware.contexthub.V1_0.ContextHub; import android.hardware.contexthub.V1_0.ContextHubMsg; @@ -467,6 +468,11 @@ public abstract class IContextHubWrapper { // TODO(271471342): Implement } + public void handleMessageDeliveryStatus(char hostEndPointId, + MessageDeliveryStatus messageDeliveryStatus) { + // TODO(b/312417087): Implement reliable message support + } + public byte[] getUuid() { return UUID; } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 42c2548264d4..0c2eee5aebbe 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -209,7 +209,7 @@ import javax.crypto.spec.GCMParameterSpec; * <li>Protect each user's data using their SP. For example, use the SP to encrypt/decrypt the * user's credential-encrypted (CE) key for file-based encryption (FBE).</li> * - * <li>Generate, protect, and use profile passwords for managed profiles.</li> + * <li>Generate, protect, and use unified profile passwords.</li> * * <li>Support unlocking the SP by alternative means: resume-on-reboot (reboot escrow) for easier * OTA updates, and escrow tokens when set up by the Device Policy Controller (DPC).</li> @@ -287,7 +287,7 @@ public class LockSettingsService extends ILockSettings.Stub { private final java.security.KeyStore mJavaKeyStore; private final RecoverableKeyStoreManager mRecoverableKeyStoreManager; - private ManagedProfilePasswordCache mManagedProfilePasswordCache; + private final UnifiedProfilePasswordCache mUnifiedProfilePasswordCache; private final RebootEscrowManager mRebootEscrowManager; @@ -404,7 +404,8 @@ public class LockSettingsService extends ILockSettings.Stub { for (int i = 0; i < newPasswordChars.length; i++) { newPassword[i] = (byte) newPasswordChars[i]; } - LockscreenCredential credential = LockscreenCredential.createManagedPassword(newPassword); + LockscreenCredential credential = + LockscreenCredential.createUnifiedProfilePassword(newPassword); Arrays.fill(newPasswordChars, '\u0000'); Arrays.fill(newPassword, (byte) 0); Arrays.fill(randomLockSeed, (byte) 0); @@ -424,7 +425,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (!isCredentialSharableWithParent(profileUserId)) { return; } - // Do not tie profile when work challenge is enabled + // Do not tie profile when separate challenge is enabled if (getSeparateProfileChallengeEnabledInternal(profileUserId)) { return; } @@ -462,7 +463,7 @@ public class LockSettingsService extends ILockSettings.Stub { setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId, /* isLockTiedToParent= */ true); tieProfileLockToParent(profileUserId, parent.id, unifiedProfilePassword); - mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, + mUnifiedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword, parentSid); } } @@ -620,9 +621,9 @@ public class LockSettingsService extends ILockSettings.Stub { } } - public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache( + public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache( java.security.KeyStore ks) { - return new ManagedProfilePasswordCache(ks); + return new UnifiedProfilePasswordCache(ks); } public boolean isHeadlessSystemUserMode() { @@ -665,7 +666,7 @@ public class LockSettingsService extends ILockSettings.Stub { mGatekeeperPasswords = new LongSparseArray<>(); mSpManager = injector.getSyntheticPasswordManager(mStorage); - mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache(mJavaKeyStore); + mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mJavaKeyStore); mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler); mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(), @@ -689,8 +690,8 @@ public class LockSettingsService extends ILockSettings.Stub { } /** - * If the account is credential-encrypted, show notification requesting the user to unlock the - * device. + * If the user is a managed profile whose credential-encrypted storage is locked, show a + * notification requesting the user to unlock the device. */ private void maybeShowEncryptionNotificationForUser(@UserIdInt int userId, String reason) { final UserInfo user = mUserManager.getUserInfo(userId); @@ -846,7 +847,7 @@ public class LockSettingsService extends ILockSettings.Stub { mHandler.post(new Runnable() { @Override public void run() { - // Hide notification first, as tie managed profile lock takes time + // Hide notification first, as tie profile lock takes time hideEncryptionNotification(new UserHandle(userId)); if (isCredentialSharableWithParent(userId)) { @@ -1458,13 +1459,13 @@ public class LockSettingsService extends ILockSettings.Stub { cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv)); decryptionResult = cipher.doFinal(encryptedPassword); - LockscreenCredential credential = LockscreenCredential.createManagedPassword( + LockscreenCredential credential = LockscreenCredential.createUnifiedProfilePassword( decryptionResult); Arrays.fill(decryptionResult, (byte) 0); try { long parentSid = getGateKeeperService().getSecureUserId( mUserManager.getProfileParent(userId).id); - mManagedProfilePasswordCache.storePassword(userId, credential, parentSid); + mUnifiedProfilePasswordCache.storePassword(userId, credential, parentSid); } catch (RemoteException e) { Slogf.w(TAG, "Failed to talk to GateKeeper service", e); } @@ -1550,7 +1551,7 @@ public class LockSettingsService extends ILockSettings.Stub { // so it goes into the cache getDecryptedPasswordForTiedProfile(profile.id); } catch (GeneralSecurityException | IOException e) { - Slog.d(TAG, "Cache work profile password failed", e); + Slog.d(TAG, "Cache unified profile password failed", e); } } } @@ -1604,19 +1605,19 @@ public class LockSettingsService extends ILockSettings.Stub { } /** - * Synchronize all profile's work challenge of the given user if it's unified: tie or clear them + * Synchronize all profile's challenge of the given user if it's unified: tie or clear them * depending on the parent user's secure state. * - * When clearing tied work challenges, a pre-computed password table for profiles are required, - * since changing password for profiles requires existing password, and existing passwords can - * only be computed before the parent user's password is cleared. + * When clearing tied challenges, a pre-computed password table for profiles are required, since + * changing password for profiles requires existing password, and existing passwords can only be + * computed before the parent user's password is cleared. * * Strictly this is a recursive function, since setLockCredentialInternal ends up calling this * method again on profiles. However the recursion is guaranteed to terminate as this method * terminates when the user is a profile that shares lock credentials with parent. * (e.g. managed and clone profile). */ - private void synchronizeUnifiedWorkChallengeForProfiles(int userId, + private void synchronizeUnifiedChallengeForProfiles(int userId, Map<Integer, LockscreenCredential> profilePasswordMap) { if (isCredentialSharableWithParent(userId)) { return; @@ -1635,7 +1636,7 @@ public class LockSettingsService extends ILockSettings.Stub { tieProfileLockIfNecessary(profileUserId, LockscreenCredential.createNone()); } else { - // We use cached work profile password computed before clearing the parent's + // We use cached profile password computed before clearing the parent's // credential, otherwise they get lost if (profilePasswordMap != null && profilePasswordMap.containsKey(profileUserId)) { @@ -1777,7 +1778,7 @@ public class LockSettingsService extends ILockSettings.Stub { notifyPasswordChanged(credential, userId); } if (isCredentialSharableWithParent(userId)) { - // Make sure the profile doesn't get locked straight after setting work challenge. + // Make sure the profile doesn't get locked straight after setting challenge. setDeviceUnlockedForUser(userId); } notifySeparateProfileChallengeChanged(userId); @@ -2368,7 +2369,7 @@ public class LockSettingsService extends ILockSettings.Stub { } try { - // Unlock work profile, and work profile with unified lock must use password only + // Unlock profile with unified lock return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId), userId, null /* progressCallback */, flags); } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException @@ -2492,7 +2493,7 @@ public class LockSettingsService extends ILockSettings.Stub { mStrongAuth.removeUser(userId); AndroidKeyStoreMaintenance.onUserRemoved(userId); - mManagedProfilePasswordCache.removePassword(userId); + mUnifiedProfilePasswordCache.removePassword(userId); gateKeeperClearSecureUserId(userId); removeKeystoreProfileKey(userId); @@ -2982,7 +2983,7 @@ public class LockSettingsService extends ILockSettings.Stub { credential, sp, userId); final Map<Integer, LockscreenCredential> profilePasswords; if (!credential.isNone()) { - // not needed by synchronizeUnifiedWorkChallengeForProfiles() + // not needed by synchronizeUnifiedChallengeForProfiles() profilePasswords = null; if (!mSpManager.hasSidForUser(userId)) { @@ -2993,8 +2994,8 @@ public class LockSettingsService extends ILockSettings.Stub { } } } else { - // Cache all profile password if they use unified work challenge. This will later be - // used to clear the profile's password in synchronizeUnifiedWorkChallengeForProfiles() + // Cache all profile password if they use unified challenge. This will later be used to + // clear the profile's password in synchronizeUnifiedChallengeForProfiles(). profilePasswords = getDecryptedPasswordsForAllTiedProfiles(userId); mSpManager.clearSidForUser(userId); @@ -3010,10 +3011,10 @@ public class LockSettingsService extends ILockSettings.Stub { } setCurrentLskfBasedProtectorId(newProtectorId, userId); LockPatternUtils.invalidateCredentialTypeCache(); - synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords); + synchronizeUnifiedChallengeForProfiles(userId, profilePasswords); setUserPasswordMetrics(credential, userId); - mManagedProfilePasswordCache.removePassword(userId); + mUnifiedProfilePasswordCache.removePassword(userId); if (savedCredentialType != CREDENTIAL_TYPE_NONE) { mSpManager.destroyAllWeakTokenBasedProtectors(userId); } @@ -3114,7 +3115,7 @@ public class LockSettingsService extends ILockSettings.Stub { try { currentCredential = getDecryptedPasswordForTiedProfile(userId); } catch (Exception e) { - Slog.e(TAG, "Failed to get work profile credential", e); + Slog.e(TAG, "Failed to get unified profile password", e); return null; } } @@ -3284,7 +3285,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean tryUnlockWithCachedUnifiedChallenge(int userId) { checkPasswordReadPermission(); - try (LockscreenCredential cred = mManagedProfilePasswordCache.retrievePassword(userId)) { + try (LockscreenCredential cred = mUnifiedProfilePasswordCache.retrievePassword(userId)) { if (cred == null) { return false; } @@ -3296,7 +3297,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void removeCachedUnifiedChallenge(int userId) { checkWritePermission(); - mManagedProfilePasswordCache.removePassword(userId); + mUnifiedProfilePasswordCache.removePassword(userId); } static String timestampToString(long timestamp) { diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index 1e8b387fc189..6d123ccebc7c 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -501,10 +501,10 @@ class LockSettingsStorage { final UserInfo parentInfo = um.getProfileParent(userId); if (parentInfo == null) { - // This user owns its lock settings files - safe to delete them + // Delete files specific to non-profile users. deleteFile(getRebootEscrowFile(userId)); } else { - // Managed profile + // Delete files specific to profile users. removeChildProfileLock(userId); } diff --git a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java b/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java index 1298fe8f07a4..21caf76d30d0 100644 --- a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java +++ b/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java @@ -43,30 +43,31 @@ import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; /** - * Caches *unified* work challenge for managed profiles. The cached credential is encrypted using - * a keystore key auth-bound to the parent user's lockscreen credential, similar to how unified - * work challenge is normally secured. - * - * <p> The cache is filled whenever the managed profile's unified challenge is created or derived - * (as part of the parent user's credential verification flow). It's removed when the profile is - * deleted or a (separate) lockscreen credential is explicitly set on the profile. There is also - * an ADB command to evict the cache "cmd lock_settings remove-cache --user X", to assist - * development and testing. - - * <p> The encrypted credential is stored in-memory only so the cache does not persist across - * reboots. + * An in-memory cache for unified profile passwords. A "unified profile password" is the random + * password that the system automatically generates and manages for each profile that uses a unified + * challenge and where the parent user has a secure lock screen. + * <p> + * Each password in this cache is encrypted by a Keystore key that is auth-bound to the parent user. + * This is very similar to how the password is protected on-disk, but the in-memory cache uses a + * much longer timeout on the keys: 7 days instead of 30 seconds. This enables use cases like + * unpausing work apps without requiring authentication as frequently. + * <p> + * Unified profile passwords are cached when they are created, or when they are decrypted as part of + * the parent user's LSKF verification flow. They are removed when the profile is deleted or when a + * separate challenge is explicitly set on the profile. There is also an ADB command to evict a + * cached password, "locksettings remove-cache --user X", to assist development and testing. */ @VisibleForTesting // public visibility is needed for Mockito -public class ManagedProfilePasswordCache { +public class UnifiedProfilePasswordCache { - private static final String TAG = "ManagedProfilePasswordCache"; + private static final String TAG = "UnifiedProfilePasswordCache"; private static final int KEY_LENGTH = 256; private static final int CACHE_TIMEOUT_SECONDS = (int) TimeUnit.DAYS.toSeconds(7); private final SparseArray<byte[]> mEncryptedPasswords = new SparseArray<>(); private final KeyStore mKeyStore; - public ManagedProfilePasswordCache(KeyStore keyStore) { + public UnifiedProfilePasswordCache(KeyStore keyStore) { mKeyStore = keyStore; } @@ -151,7 +152,8 @@ public class ManagedProfilePasswordCache { Slog.d(TAG, "Cannot decrypt", e); return null; } - LockscreenCredential result = LockscreenCredential.createManagedPassword(credential); + LockscreenCredential result = + LockscreenCredential.createUnifiedProfilePassword(credential); Arrays.fill(credential, (byte) 0); return result; } diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java index 173c452fd8cb..850449595d74 100644 --- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java +++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java @@ -330,8 +330,11 @@ import java.util.Objects; TextUtils.isEmpty(address) ? null : mBluetoothRouteController.getRouteIdForBluetoothAddress(address); - return createMediaRoute2Info( - routeId, audioDeviceInfo.getType(), audioDeviceInfo.getProductName(), address); + // We use the name from the port instead AudioDeviceInfo#getProductName because the latter + // replaces empty names with the name of the device (example: Pixel 8). In that case we want + // to derive a name ourselves from the type instead. + String deviceName = audioDeviceInfo.getPort().name(); + return createMediaRoute2Info(routeId, audioDeviceInfo.getType(), deviceName, address); } /** @@ -339,8 +342,8 @@ import java.util.Objects; * * @param routeId A route id, or null to use an id pre-defined for the given {@code type}. * @param audioDeviceInfoType The type as obtained from {@link AudioDeviceInfo#getType}. - * @param productName The product name as obtained from {@link - * AudioDeviceInfo#getProductName()}, or null to use a predefined name for the given {@code + * @param deviceName A human readable name to populate the route's {@link + * MediaRoute2Info#getName name}, or null to use a predefined name for the given {@code * type}. * @param address The type as obtained from {@link AudioDeviceInfo#getAddress()} or {@link * BluetoothDevice#getAddress()}. @@ -350,7 +353,7 @@ import java.util.Objects; private MediaRoute2Info createMediaRoute2Info( @Nullable String routeId, int audioDeviceInfoType, - @Nullable CharSequence productName, + @Nullable CharSequence deviceName, @Nullable String address) { SystemRouteInfo systemRouteInfo = AUDIO_DEVICE_INFO_TYPE_TO_ROUTE_INFO.get(audioDeviceInfoType); @@ -359,7 +362,7 @@ import java.util.Objects; // earpiece. return null; } - CharSequence humanReadableName = productName; + CharSequence humanReadableName = deviceName; if (TextUtils.isEmpty(humanReadableName)) { humanReadableName = mContext.getResources().getText(systemRouteInfo.mNameResource); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a62d8b8bc032..66a974080a43 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -5406,6 +5406,7 @@ public class NotificationManagerService extends SystemService { private void validateAutomaticZenRule(AutomaticZenRule rule) { Objects.requireNonNull(rule, "automaticZenRule is null"); Objects.requireNonNull(rule.getName(), "Name is null"); + rule.validate(); if (rule.getOwner() == null && rule.getConfigurationActivity() == null) { throw new NullPointerException( @@ -5463,6 +5464,7 @@ public class NotificationManagerService extends SystemService { public void setAutomaticZenRuleState(String id, Condition condition) { Objects.requireNonNull(id, "id is null"); Objects.requireNonNull(condition, "Condition is null"); + condition.validate(); enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState"); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 7ec94c315798..3f8b5952a1bc 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -627,6 +627,7 @@ public class ZenModeHelper { try { ApplicationInfo applicationInfo = mPm.getApplicationInfo(pkg, 0); rule.name = applicationInfo.loadLabel(mPm).toString(); + rule.iconResName = drawableResIdToResName(pkg, applicationInfo.icon); } catch (PackageManager.NameNotFoundException e) { // Should not happen, since it's the app calling us (?) Log.w(TAG, "Package not found for creating implicit zen rule"); @@ -634,6 +635,9 @@ public class ZenModeHelper { } }); + rule.type = AutomaticZenRule.TYPE_OTHER; + rule.triggerDescription = mContext.getString(R.string.zen_mode_implicit_trigger_description, + rule.name); rule.condition = null; rule.conditionId = new Uri.Builder() .scheme(Condition.SCHEME) diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig index dcac8c98d19f..da017453ed8b 100644 --- a/services/core/java/com/android/server/notification/flags.aconfig +++ b/services/core/java/com/android/server/notification/flags.aconfig @@ -20,3 +20,17 @@ flag { description: "This flag controls the refactoring of NMS to NotificationAttentionHelper" bug: "291907312" } + +flag { + name: "cross_app_polite_notifications" + namespace: "systemui" + description: "This flag controls the cross-app effect of polite notifications" + bug: "270456865" +} + +flag { + name: "vibrate_while_unlocked" + namespace: "systemui" + description: "This flag controls the vibrate while unlocked setting of polite notifications" + bug: "270456865" +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java index f985b5b64e21..b9b09fb0e84c 100644 --- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java +++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java @@ -754,9 +754,9 @@ public class PersistentDataBlockService extends SystemService { } }; - private PersistentDataBlockManagerInternal mInternalService = - new PersistentDataBlockManagerInternal() { + private InternalService mInternalService = new InternalService(); + private class InternalService implements PersistentDataBlockManagerInternal { @Override public void setFrpCredentialHandle(byte[] handle) { writeInternal(handle, getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE); diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java index 7b35589ae682..7f58e75e0287 100644 --- a/services/core/java/com/android/server/pm/BroadcastHelper.java +++ b/services/core/java/com/android/server/pm/BroadcastHelper.java @@ -837,13 +837,11 @@ public final class BroadcastHelper { } final String removedPackage = packageRemovedInfo.mRemovedPackage; - final int removedAppId = packageRemovedInfo.mRemovedAppId; - final int uid = packageRemovedInfo.mUid; final String installerPackageName = packageRemovedInfo.mInstallerPackageName; final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList; Bundle extras = new Bundle(2); - extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid); + extras.putInt(Intent.EXTRA_UID, packageRemovedInfo.mUid); extras.putBoolean(Intent.EXTRA_REPLACING, true); sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras, 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null); @@ -888,8 +886,6 @@ public final class BroadcastHelper { boolean removedBySystem, boolean isArchived) { final String removedPackage = packageRemovedInfo.mRemovedPackage; - final int removedAppId = packageRemovedInfo.mRemovedAppId; - final int uid = packageRemovedInfo.mUid; final String installerPackageName = packageRemovedInfo.mInstallerPackageName; final int[] broadcastUserIds = packageRemovedInfo.mBroadcastUsers; final int[] instantUserIds = packageRemovedInfo.mInstantUserIds; @@ -902,8 +898,7 @@ public final class BroadcastHelper { final boolean isStaticSharedLib = packageRemovedInfo.mIsStaticSharedLib; Bundle extras = new Bundle(); - final int removedUid = removedAppId >= 0 ? removedAppId : uid; - extras.putInt(Intent.EXTRA_UID, removedUid); + extras.putInt(Intent.EXTRA_UID, packageRemovedInfo.mUid); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved); extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, isRemovedPackageSystemUpdate); extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp); @@ -940,10 +935,10 @@ public final class BroadcastHelper { sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, null, broadcastUserIds, instantUserIds, broadcastAllowList, null); - packageSender.notifyPackageRemoved(removedPackage, removedUid); + packageSender.notifyPackageRemoved(removedPackage, packageRemovedInfo.mUid); } } - if (removedAppId >= 0) { + if (packageRemovedInfo.mIsAppIdRemoved) { // If a system app's updates are uninstalled the UID is not actually removed. Some // services need to know the package name affected. if (isReplace) { diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index bd725ed49f3c..27f4e11c53ad 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -508,12 +508,15 @@ public interface Computer extends PackageDataSnapshot { boolean getApplicationHiddenSettingAsUser(@NonNull String packageName, @UserIdInt int userId); - boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId); + boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException; - boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId); + boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException; /** Check if the package is in a stopped state for a given user. */ - boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId); + boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException; boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, @UserIdInt int userId); diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 2dc3fb7464fd..abfd5715810e 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -4980,31 +4980,34 @@ public class ComputerEngine implements Computer { } } - private PackageUserStateInternal getUserStageOrDefaultForUser(@NonNull String packageName, - int userId) { + private PackageUserStateInternal getUserStateOrDefaultForUser(@NonNull String packageName, + int userId) throws PackageManager.NameNotFoundException { final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "when asking about packages for user " + userId); final PackageStateInternal ps = mSettings.getPackage(packageName); if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) { - throw new IllegalArgumentException("Unknown target package: " + packageName); + throw new PackageManager.NameNotFoundException(packageName); } return ps.getUserStateOrDefault(userId); } @Override - public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId) { - return getUserStageOrDefaultForUser(packageName, userId).isSuspended(); + public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId) + throws PackageManager.NameNotFoundException { + return getUserStateOrDefaultForUser(packageName, userId).isSuspended(); } @Override - public boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) { - return getUserStageOrDefaultForUser(packageName, userId).isQuarantined(); + public boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { + return getUserStateOrDefaultForUser(packageName, userId).isQuarantined(); } @Override - public boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) { - return getUserStageOrDefaultForUser(packageName, userId).isStopped(); + public boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { + return getUserStateOrDefaultForUser(packageName, userId).isStopped(); } @Override diff --git a/services/core/java/com/android/server/pm/DeletePackageAction.java b/services/core/java/com/android/server/pm/DeletePackageAction.java index 8ef6601f7684..31544d5308fb 100644 --- a/services/core/java/com/android/server/pm/DeletePackageAction.java +++ b/services/core/java/com/android/server/pm/DeletePackageAction.java @@ -16,17 +16,19 @@ package com.android.server.pm; +import android.annotation.NonNull; import android.os.UserHandle; final class DeletePackageAction { public final PackageSetting mDeletingPs; public final PackageSetting mDisabledPs; + @NonNull public final PackageRemovedInfo mRemovedInfo; public final int mFlags; public final UserHandle mUser; DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs, - PackageRemovedInfo removedInfo, int flags, UserHandle user) { + @NonNull PackageRemovedInfo removedInfo, int flags, UserHandle user) { mDeletingPs = deletingPs; mDisabledPs = disabledPs; mRemovedInfo = removedInfo; diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 93836266d1f4..aa7f0d3c668a 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -370,7 +370,7 @@ final class DeletePackageHelper { @GuardedBy("mPm.mInstallLock") public boolean deletePackageLIF(@NonNull String packageName, UserHandle user, boolean deleteCodeAndResources, @NonNull int[] allUserHandles, int flags, - PackageRemovedInfo outInfo, boolean writeSettings) { + @NonNull PackageRemovedInfo outInfo, boolean writeSettings) { final DeletePackageAction action; synchronized (mPm.mLock) { final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName); @@ -410,8 +410,8 @@ final class DeletePackageHelper { * deleted, {@code null} otherwise. */ @Nullable - public static DeletePackageAction mayDeletePackageLocked( - PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs, + public static DeletePackageAction mayDeletePackageLocked(@NonNull PackageRemovedInfo outInfo, + PackageSetting ps, @Nullable PackageSetting disabledPs, int flags, UserHandle user) { if (ps == null) { return null; @@ -460,12 +460,18 @@ final class DeletePackageHelper { } final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier(); - if (outInfo != null) { - // Remember which users are affected, before the installed states are modified - outInfo.mRemovedUsers = (systemApp || userId == UserHandle.USER_ALL) - ? ps.queryUsersInstalledOrHasData(allUserHandles) - : new int[]{userId}; - } + // Remember which users are affected, before the installed states are modified + outInfo.mRemovedUsers = (systemApp || userId == UserHandle.USER_ALL) + ? ps.queryUsersInstalledOrHasData(allUserHandles) + : new int[]{userId}; + outInfo.populateBroadcastUsers(ps); + outInfo.mDataRemoved = (flags & PackageManager.DELETE_KEEP_DATA) == 0; + outInfo.mRemovedPackage = ps.getPackageName(); + outInfo.mInstallerPackageName = ps.getInstallSource().mInstallerPackageName; + outInfo.mIsStaticSharedLib = + ps.getPkg() != null && ps.getPkg().getStaticSharedLibraryName() != null; + outInfo.mIsExternal = ps.isExternalStorage(); + outInfo.mRemovedPackageVersionCode = ps.getVersionCode(); if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0) && userId != UserHandle.USER_ALL) { @@ -503,7 +509,11 @@ final class DeletePackageHelper { } } if (clearPackageStateAndReturn) { - mRemovePackageHelper.clearPackageStateForUserLIF(ps, userId, outInfo, flags); + mRemovePackageHelper.clearPackageStateForUserLIF(ps, userId, flags); + // Legacy behavior to report appId as UID here. + // The final broadcasts will contain a per-user UID. + outInfo.mUid = ps.getAppId(); + outInfo.mIsAppIdRemoved = true; mPm.scheduleWritePackageRestrictions(user); return; } @@ -529,12 +539,8 @@ final class DeletePackageHelper { // If the package removed had SUSPEND_APPS, unset any restrictions that might have been in // place for all affected users. - int[] affectedUserIds = (outInfo != null) ? outInfo.mRemovedUsers : null; - if (affectedUserIds == null) { - affectedUserIds = mPm.resolveUserIds(userId); - } final Computer snapshot = mPm.snapshotComputer(); - for (final int affectedUserId : affectedUserIds) { + for (final int affectedUserId : outInfo.mRemovedUsers) { if (hadSuspendAppsPermission.get(affectedUserId)) { mPm.unsuspendForSuspendingPackage(snapshot, packageName, affectedUserId); mPm.removeAllDistractingPackageRestrictions(snapshot, affectedUserId); @@ -542,24 +548,21 @@ final class DeletePackageHelper { } // Take a note whether we deleted the package for all users - if (outInfo != null) { - synchronized (mPm.mLock) { - outInfo.mRemovedForAllUsers = mPm.mPackages.get(ps.getPackageName()) == null; - } + synchronized (mPm.mLock) { + outInfo.mRemovedForAllUsers = mPm.mPackages.get(ps.getPackageName()) == null; } } @GuardedBy("mPm.mInstallLock") private void deleteInstalledPackageLIF(PackageSetting ps, boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles, - PackageRemovedInfo outInfo, boolean writeSettings) { + @NonNull PackageRemovedInfo outInfo, boolean writeSettings) { synchronized (mPm.mLock) { - if (outInfo != null) { - outInfo.mUid = ps.getAppId(); - outInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList( - mPm.snapshotComputer(), ps, allUserHandles, - mPm.mSettings.getPackagesLocked()); - } + // Since the package is being deleted in all users, report appId as the uid + outInfo.mUid = ps.getAppId(); + outInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList( + mPm.snapshotComputer(), ps, allUserHandles, + mPm.mSettings.getPackagesLocked()); } // Delete package data from internal structures and also remove data if flag is set @@ -567,7 +570,7 @@ final class DeletePackageHelper { ps, allUserHandles, outInfo, flags, writeSettings); // Delete application code and resources only for parent packages - if (deleteCodeAndResources && (outInfo != null)) { + if (deleteCodeAndResources) { outInfo.mArgs = new InstallArgs( ps.getPathString(), getAppDexInstructionSets( ps.getPrimaryCpuAbiLegacy(), ps.getSecondaryCpuAbiLegacy())); @@ -639,7 +642,7 @@ final class DeletePackageHelper { int flags = action.mFlags; final PackageSetting deletedPs = action.mDeletingPs; final PackageRemovedInfo outInfo = action.mRemovedInfo; - final boolean applyUserRestrictions = outInfo != null && (outInfo.mOrigUsers != null); + final boolean applyUserRestrictions = outInfo.mOrigUsers != null; final AndroidPackage deletedPkg = deletedPs.getPkg(); // Confirm if the system package has been updated // An updated system app can be deleted. This will also have to restore @@ -662,10 +665,8 @@ final class DeletePackageHelper { } } - if (outInfo != null) { - // Delete the updated package - outInfo.mIsRemovedPackageSystemUpdate = true; - } + // Delete the updated package + outInfo.mIsRemovedPackageSystemUpdate = true; if (disabledPs.getVersionCode() < deletedPs.getVersionCode() || disabledPs.getAppId() != deletedPs.getAppId()) { diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java index 24a33f1949fa..e3bbd2d8d17e 100644 --- a/services/core/java/com/android/server/pm/IPackageManagerBase.java +++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java @@ -951,20 +951,32 @@ public abstract class IPackageManagerBase extends IPackageManager.Stub { @Deprecated public final boolean isPackageSuspendedForUser(@NonNull String packageName, @UserIdInt int userId) { - return snapshot().isPackageSuspendedForUser(packageName, userId); + try { + return snapshot().isPackageSuspendedForUser(packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException("Unknown target package: " + packageName); + } } @Override @Deprecated public final boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId) { - return snapshot().isPackageQuarantinedForUser(packageName, userId); + try { + return snapshot().isPackageQuarantinedForUser(packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException("Unknown target package: " + packageName); + } } @Override public final boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId) { - return snapshot().isPackageStoppedForUser(packageName, userId); + try { + return snapshot().isPackageStoppedForUser(packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException("Unknown target package: " + packageName); + } } @Override diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java index 5c4447eb99a4..3b9f9c804e27 100644 --- a/services/core/java/com/android/server/pm/InitAppsHelper.java +++ b/services/core/java/com/android/server/pm/InitAppsHelper.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME; import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME; import static com.android.server.pm.PackageManagerService.SCAN_AS_APK_IN_APEX; @@ -30,7 +31,6 @@ import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX; import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN; import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS; import static com.android.server.pm.PackageManagerService.TAG; -import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX; import android.annotation.NonNull; import android.annotation.Nullable; @@ -46,12 +46,12 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.om.OverlayConfig; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.server.EventLogTags; import com.android.server.pm.parsing.PackageCacher; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.utils.WatchedArrayMap; import java.io.File; diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 83a6f10f0e2a..0fa7aa5473b2 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -154,12 +154,16 @@ import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.F2fsUtils; +import com.android.internal.pm.parsing.PackageParserException; +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedPermission; import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.security.VerityUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; @@ -181,8 +185,6 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.SharedLibraryWrapper; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.rollback.RollbackManagerInternal; import com.android.server.security.FileIntegrityService; import com.android.server.utils.WatchedArrayMap; @@ -1165,7 +1167,7 @@ final class InstallPackageHelper { parseFlags); archivedPackage = request.getPackageLite().getArchivedPackage(); } - } catch (PackageManagerException e) { + } catch (PackageManagerException | PackageParserException e) { throw new PrepareFailure("Failed parse during installPackageLI", e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -2450,9 +2452,9 @@ final class InstallPackageHelper { // Hardcode previousAppId to 0 to disable any data migration (http://b/221088088) mAppDataHelper.prepareAppDataPostCommitLIF(ps, 0, installRequest.getNewUsers()); if (installRequest.isClearCodeCache()) { - mAppDataHelper.clearAppDataLeafLIF(packageName, ps.getVolumeUuid(), - UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE - | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + mAppDataHelper.clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL, + FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL + | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); } if (installRequest.isInstallReplace() && pkg != null) { mDexManager.notifyPackageUpdated(packageName, @@ -2910,7 +2912,8 @@ final class InstallPackageHelper { info.mInstallerPackageName = request.getInstallerPackageName(); info.mRemovedUsers = firstUserIds; info.mBroadcastUsers = firstUserIds; - info.mRemovedAppId = request.getAppId(); + info.mUid = request.getAppId(); + info.mIsAppIdRemoved = true; info.mRemovedPackageVersionCode = request.getPkg().getLongVersionCode(); info.mRemovedForAllUsers = true; @@ -3856,7 +3859,7 @@ final class InstallPackageHelper { synchronized (mPm.mLock) { platformPackage = mPm.getPlatformPackage(); - var isSystemApp = AndroidPackageUtils.isSystem(parsedPackage); + var isSystemApp = AndroidPackageLegacyUtils.isSystem(parsedPackage); final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr( AndroidPackageUtils.getRealPackageOrNull(parsedPackage, isSystemApp)); realPkgName = ScanPackageUtils.getRealPackageName(parsedPackage, renamedPkgName, @@ -4126,7 +4129,7 @@ final class InstallPackageHelper { null /* request */)) { mDeletePackageHelper.deletePackageLIF( parsedPackage.getPackageName(), null, true, - mPm.mUserManager.getUserIds(), 0, null, false); + mPm.mUserManager.getUserIds(), 0, new PackageRemovedInfo(), false); } } else if (newPkgVersionGreater || newSharedUserSetting) { // The application on /system is newer than the application on /data. @@ -4574,7 +4577,7 @@ final class InstallPackageHelper { private void assertPackageWithSharedUserIdIsPrivileged(AndroidPackage pkg) throws PackageManagerException { - if (!AndroidPackageUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null)) { + if (!AndroidPackageLegacyUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null)) { SharedUserSetting sharedUserSetting = null; try { synchronized (mPm.mLock) { @@ -4612,7 +4615,7 @@ final class InstallPackageHelper { final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0) && ScanPackageUtils.getVendorPartitionVersion() < 28; if (((scanFlags & SCAN_AS_PRIVILEGED) == 0) - && !AndroidPackageUtils.isPrivileged(pkg) + && !AndroidPackageLegacyUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null) && !skipVendorPrivilegeScan) { SharedUserSetting sharedUserSetting = null; diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 5494bd9808c8..ee780d99b6b6 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -51,12 +51,12 @@ import android.util.ExceptionUtils; import android.util.Slog; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.ArrayUtils; import com.android.server.art.model.DexoptResult; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import java.io.File; import java.util.ArrayList; @@ -818,7 +818,8 @@ final class InstallRequest { public void setRemovedAppId(int appId) { if (mRemovedInfo != null) { - mRemovedInfo.mRemovedAppId = appId; + mRemovedInfo.mUid = appId; + mRemovedInfo.mIsAppIdRemoved = true; } } diff --git a/services/core/java/com/android/server/pm/KnownPackages.java b/services/core/java/com/android/server/pm/KnownPackages.java index 154709a62095..83831ca61aa1 100644 --- a/services/core/java/com/android/server/pm/KnownPackages.java +++ b/services/core/java/com/android/server/pm/KnownPackages.java @@ -77,6 +77,8 @@ public final class KnownPackages { // Please note the numbers should be continuous. public static final int LAST_KNOWN_PACKAGE = PACKAGE_WEARABLE_SENSING; + static final String SYSTEM_PACKAGE_NAME = "android"; + private final DefaultAppProvider mDefaultAppProvider; private final String mRequiredInstallerPackage; private final String mRequiredUninstallerPackage; @@ -186,7 +188,7 @@ public final class KnownPackages { case PACKAGE_SETUP_WIZARD: return snapshot.filterOnlySystemPackages(mSetupWizardPackage); case PACKAGE_SYSTEM: - return new String[]{"android"}; + return new String[]{SYSTEM_PACKAGE_NAME}; case PACKAGE_VERIFIER: return snapshot.filterOnlySystemPackages(mRequiredVerifierPackages); case PACKAGE_SYSTEM_TEXT_CLASSIFIER: diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 58c1c0564f95..ba66377beb8a 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -1575,6 +1575,30 @@ public class LauncherAppsService extends SystemService { } @Override + public List<String> getPreInstalledSystemPackages(UserHandle user) { + // Only system launchers, which have access to recents should have access to this API. + // TODO(b/303803157): Update access control for this API to default Launcher app. + if (!mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) { + throw new SecurityException("Caller is not the recents app"); + } + if (!canAccessProfile(user.getIdentifier(), + "Can't access preinstalled packages for another user")) { + return null; + } + final long identity = Binder.clearCallingIdentity(); + try { + String userType = mUm.getUserInfo(user.getIdentifier()).userType; + Set<String> preInstalledPackages = mUm.getPreInstallableSystemPackages(userType); + if (preInstalledPackages == null) { + return new ArrayList<>(); + } + return List.copyOf(preInstalledPackages); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void startActivityAsUser(IApplicationThread caller, String callingPackage, String callingFeatureId, ComponentName component, Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException { diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index e3bab3f243c3..6b05edf7c25a 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -323,6 +323,7 @@ public class PackageArchiver { PackageStateInternal ps = getPackageState(packageName, snapshot, Binder.getCallingUid(), userId); verifyNotSystemApp(ps.getFlags()); + verifyInstalled(ps, userId); String responsibleInstallerPackage = getResponsibleInstallerPackage(ps); verifyInstaller(responsibleInstallerPackage, userId); ApplicationInfo installerInfo = snapshot.getApplicationInfo( @@ -476,6 +477,14 @@ public class PackageArchiver { } } + private void verifyInstalled(PackageStateInternal ps, int userId) + throws PackageManager.NameNotFoundException { + if (!ps.getUserStateOrDefault(userId).isInstalled()) { + throw new PackageManager.NameNotFoundException( + TextUtils.formatSimple("%s is not installed.", ps.getPackageName())); + } + } + /** * Returns true if the app is archivable. */ @@ -519,11 +528,11 @@ public class PackageArchiver { /** * Returns true if user has opted the app out of archiving through system settings. */ - // TODO(b/304256918) Switch this to a separate OP code for archiving. private boolean isAppOptedOutOfArchiving(String packageName, int uid) { return Binder.withCleanCallingIdentity(() -> - getAppOpsManager().checkOp(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, - uid, packageName) == MODE_IGNORED); + getAppOpsManager().checkOpNoThrow( + AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, uid, packageName) + == MODE_IGNORED); } private void verifyOptOutStatus(String packageName, int uid) @@ -676,23 +685,51 @@ public class PackageArchiver { PackageStateInternal ps; try { ps = getPackageState(packageName, snapshot, callingUid, userId); - snapshot.enforceCrossUserPermission(callingUid, userId, true, false, - "getArchivedAppIcon"); - verifyArchived(ps, userId); } catch (PackageManager.NameNotFoundException e) { - throw new ParcelableException(e); + Slog.e(TAG, TextUtils.formatSimple("Package %s couldn't be found.", packageName), e); + return null; } - List<ArchiveActivityInfo> activityInfos = ps.getUserStateOrDefault( - userId).getArchiveState().getActivityInfos(); - if (activityInfos.size() == 0) { + ArchiveState archiveState = getAnyArchiveState(ps, userId); + if (archiveState == null || archiveState.getActivityInfos().size() == 0) { return null; } // TODO(b/298452477) Handle monochrome icons. // In the rare case the archived app defined more than two launcher activities, we choose // the first one arbitrarily. - return includeCloudOverlay(decodeIcon(activityInfos.get(0))); + return includeCloudOverlay(decodeIcon(archiveState.getActivityInfos().get(0))); + } + + /** + * This method first checks the ArchiveState for the provided userId and then tries to fallback + * to other users if the current user is not archived. + * + * <p> This fallback behaviour is required for archived apps to fit into the multi-user world + * where APKs are shared across users. E.g. current ways of fetching icons for apps that are + * only installed on the work profile also work when executed on the personal profile if you're + * using {@link PackageManager#MATCH_UNINSTALLED_PACKAGES}. Resource fetching from APKs is for + * the most part userId-agnostic, which we need to mimic here in order for existing methods + * like {@link PackageManager#getApplicationIcon} to continue working. + * + * @return {@link ArchiveState} for {@code userId} if present. If not present, false back to an + * arbitrary userId. If no user is archived, returns null. + */ + @Nullable + private ArchiveState getAnyArchiveState(PackageStateInternal ps, int userId) { + PackageUserStateInternal userState = ps.getUserStateOrDefault(userId); + if (isArchived(userState)) { + return userState.getArchiveState(); + } + + for (int i = 0; i < ps.getUserStates().size(); i++) { + userState = ps.getUserStates().valueAt(i); + if (isArchived(userState)) { + return userState.getArchiveState(); + } + } + + return null; } @VisibleForTesting diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 47d1df5df1c0..4adb60c34c52 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -168,6 +168,7 @@ import com.android.internal.content.InstallLocationUtils; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.os.SomeArgs; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.security.VerityUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; @@ -181,7 +182,6 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.dex.DexManager; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import libcore.io.IoUtils; import libcore.util.EmptyArray; diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index 1e7d0437d99e..c737b45ae885 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -762,13 +762,14 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { } @Override - public boolean isPackageQuarantined(@NonNull String packageName, - @UserIdInt int userId) { + public boolean isPackageQuarantined(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { return snapshot().isPackageQuarantinedForUser(packageName, userId); } @Override - public boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId) { + public boolean isPackageStopped(@NonNull String packageName, @UserIdInt int userId) + throws PackageManager.NameNotFoundException { return snapshot().isPackageStoppedForUser(packageName, userId); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2880f84c6445..56365b676618 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -185,6 +185,7 @@ import com.android.internal.pm.parsing.pkg.AndroidPackageInternal; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.pm.pkg.component.ParsedMainComponent; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.telephony.CarrierAppUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; @@ -238,7 +239,6 @@ import com.android.server.pm.pkg.SharedUserApi; import com.android.server.pm.pkg.mutate.PackageStateMutator; import com.android.server.pm.pkg.mutate.PackageStateWrite; import com.android.server.pm.pkg.mutate.PackageUserStateWrite; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.resolution.ComponentResolver; import com.android.server.pm.resolution.ComponentResolverApi; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; @@ -2013,6 +2013,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService public boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); } + + @Override + public Set<String> getHiddenApiWhitelistedApps() { + return SystemConfig.getInstance().getHiddenApiWhitelistedApps(); + } + + @Override + public Set<String> getInstallConstraintsAllowlist() { + return SystemConfig.getInstance().getInstallConstraintsAllowlist(); + } }; // CHECKSTYLE:ON IndentationCheck @@ -3044,6 +3054,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } + @NonNull int[] resolveUserIds(int userId) { return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId }; } @@ -5241,14 +5252,18 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String getSuspendingPackage(String packageName, int userId) { - final int callingUid = Binder.getCallingUid(); - final Computer snapshot = snapshot(); - // This will do visibility checks as well. - if (!snapshot.isPackageSuspendedForUser(packageName, userId)) { + try { + final int callingUid = Binder.getCallingUid(); + final Computer snapshot = snapshot(); + // This will do visibility checks as well. + if (!snapshot.isPackageSuspendedForUser(packageName, userId)) { + return null; + } + return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId, + callingUid); + } catch (PackageManager.NameNotFoundException e) { return null; } - return mSuspendPackageHelper.getSuspendingPackage(snapshot, packageName, userId, - callingUid); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 215e9528a35e..322557b9e496 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2872,16 +2872,16 @@ class PackageManagerShellCommand extends ShellCommand { UserHandle.USER_NULL, "runGrantRevokePermission")); List<PackageInfo> packageInfos; + PackageManager pm = mContext.createContextAsUser(translatedUser, 0).getPackageManager(); if (pkg == null) { - packageInfos = mContext.getPackageManager().getInstalledPackages( - PackageManager.GET_PERMISSIONS); + packageInfos = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS); } else { try { - packageInfos = Collections.singletonList( - mContext.getPackageManager().getPackageInfo(pkg, - PackageManager.GET_PERMISSIONS)); + packageInfos = Collections.singletonList(pm.getPackageInfo(pkg, + PackageManager.GET_PERMISSIONS)); } catch (NameNotFoundException e) { getErrPrintWriter().println("Error: package not found"); + getOutPrintWriter().println("Failure [package not found]"); return 1; } } diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java index 7ee1772adead..881b0b398f9a 100644 --- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java +++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java @@ -25,7 +25,7 @@ final class PackageRemovedInfo { String mRemovedPackage; String mInstallerPackageName; int mUid = -1; - int mRemovedAppId = -1; + boolean mIsAppIdRemoved = false; int[] mOrigUsers; int[] mRemovedUsers = null; int[] mBroadcastUsers = null; diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index bb0017c80d6d..b6de0e5c030f 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -20,19 +20,23 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIB import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY; +import static com.android.server.pm.PackageManagerService.SCAN_AS_APEX; import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; +import static com.android.server.pm.PackageManagerService.TAG; import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; import android.content.pm.SigningDetails; +import android.os.Build; import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Log; +import android.util.Slog; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.utils.WatchedLongSparseArray; import java.util.ArrayList; @@ -49,6 +53,8 @@ import java.util.Map; * as install) led to the request. */ final class ReconcilePackageUtils { + private static final boolean ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE = Build.IS_DEBUGGABLE || true; + public static List<ReconciledPackage> reconcilePackages( List<InstallRequest> installRequests, Map<String, AndroidPackage> allPackages, @@ -90,6 +96,8 @@ final class ReconcilePackageUtils { } } + final AndroidPackage systemPackage = allPackages.get(KnownPackages.SYSTEM_PACKAGE_NAME); + for (InstallRequest installRequest : installRequests) { final String installPackageName = installRequest.getParsedPackage().getPackageName(); final List<SharedLibraryInfo> allowedSharedLibInfos = @@ -133,6 +141,9 @@ final class ReconcilePackageUtils { if (parsedPackage != null) { signingDetails = parsedPackage.getSigningDetails(); } + final boolean isSystemPackage = + ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0); + final boolean isApex = (scanFlags & SCAN_AS_APEX) != 0; SharedUserSetting sharedUserSetting = settings.getSharedUserSettingLPr( signatureCheckPs); if (ksms.shouldCheckUpgradeKeySetLocked( @@ -141,7 +152,7 @@ final class ReconcilePackageUtils { // We just determined the app is signed correctly, so bring // over the latest parsed certs. } else { - if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) { + if (!isSystemPackage) { throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + parsedPackage.getPackageName() + " upgrade keys do not match the previously installed" @@ -168,9 +179,23 @@ final class ReconcilePackageUtils { removeAppKeySetData = true; } + if (!isSystemPackage && !isApex && signingDetails != null + && systemPackage != null && systemPackage.getSigningDetails() != null + && systemPackage.getSigningDetails().checkCapability( + signingDetails, + SigningDetails.CertCapabilities.PERMISSION)) { + Slog.d(TAG, "Non-preload app associated with system signature: " + + signatureCheckPs.getPackageName()); + if (!ALLOW_NON_PRELOADS_SYSTEM_SIGNATURE) { + throw new ReconcileFailure( + INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, + "Non-preload app associated with system signature: " + + signatureCheckPs.getPackageName()); + } + } + // if this is is a sharedUser, check to see if the new package is signed by a - // newer - // signing certificate than the existing one, and if so, copy over the new + // newer signing certificate than the existing one, and if so, copy over the new // details if (sharedUserSetting != null) { // Attempt to merge the existing lineage for the shared SigningDetails with @@ -203,7 +228,7 @@ final class ReconcilePackageUtils { } } } catch (PackageManagerException e) { - if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) { + if (!isSystemPackage) { throw new ReconcileFailure(e); } signingDetails = parsedPackage.getSigningDetails(); diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java index 52b31319cc19..fefab3ba15b9 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -44,12 +44,12 @@ import android.util.Slog; import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.util.ArrayUtils; import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.parsing.PackageCacher; -import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; @@ -167,7 +167,7 @@ final class RemovePackageHelper { if (removedPackage != null) { // TODO: Use PackageState for isSystem cleanPackageDataStructuresLILPw(removedPackage, - AndroidPackageUtils.isSystem(removedPackage), chatty); + AndroidPackageLegacyUtils.isSystem(removedPackage), chatty); } } } @@ -252,8 +252,7 @@ final class RemovePackageHelper { } } - public void clearPackageStateForUserLIF(PackageSetting ps, int userId, - PackageRemovedInfo outInfo, int flags) { + public void clearPackageStateForUserLIF(PackageSetting ps, int userId, int flags) { final AndroidPackage pkg; final SharedUserSetting sus; synchronized (mPm.mLock) { @@ -287,25 +286,12 @@ final class RemovePackageHelper { } mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), ps, pkg, sharedUserPkgs, userId); - - if (outInfo != null) { - if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { - outInfo.mDataRemoved = true; - } - outInfo.mRemovedPackage = ps.getPackageName(); - outInfo.mInstallerPackageName = ps.getInstallSource().mInstallerPackageName; - outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibraryName() != null; - outInfo.mRemovedAppId = ps.getAppId(); - outInfo.mBroadcastUsers = outInfo.mRemovedUsers; - outInfo.mIsExternal = ps.isExternalStorage(); - outInfo.mRemovedPackageVersionCode = ps.getVersionCode(); - } } // Called to clean up disabled system packages public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles) { synchronized (mPm.mInstallLock) { - removePackageDataLIF(deletedPs, allUserHandles, /* outInfo= */ null, + removePackageDataLIF(deletedPs, allUserHandles, new PackageRemovedInfo(), /* flags= */ 0, /* writeSettings= */ false); } } @@ -318,20 +304,11 @@ final class RemovePackageHelper { */ @GuardedBy("mPm.mInstallLock") public void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles, - PackageRemovedInfo outInfo, int flags, boolean writeSettings) { + @NonNull PackageRemovedInfo outInfo, int flags, boolean writeSettings) { String packageName = deletedPs.getPackageName(); if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs); // Retrieve object to delete permissions for shared user later on final AndroidPackage deletedPkg = deletedPs.getPkg(); - if (outInfo != null) { - outInfo.mRemovedPackage = packageName; - outInfo.mInstallerPackageName = deletedPs.getInstallSource().mInstallerPackageName; - outInfo.mIsStaticSharedLib = deletedPkg != null - && deletedPkg.getStaticSharedLibraryName() != null; - outInfo.populateBroadcastUsers(deletedPs); - outInfo.mIsExternal = deletedPs.isExternalStorage(); - outInfo.mRemovedPackageVersionCode = deletedPs.getVersionCode(); - } removePackageLI(deletedPs.getPackageName(), (flags & PackageManager.DELETE_CHATTY) != 0); if (!deletedPs.isSystem()) { @@ -355,9 +332,6 @@ final class RemovePackageHelper { mAppDataHelper.destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); mAppDataHelper.destroyAppProfilesLIF(resolvedPkg.getPackageName()); - if (outInfo != null) { - outInfo.mDataRemoved = true; - } } int removedAppId = -1; @@ -373,9 +347,7 @@ final class RemovePackageHelper { mPm.mAppsFilter.removePackage(snapshot, snapshot.getPackageStateInternal(packageName)); removedAppId = mPm.mSettings.removePackageLPw(packageName); - if (outInfo != null) { - outInfo.mRemovedAppId = removedAppId; - } + outInfo.mIsAppIdRemoved = true; if (!mPm.mSettings.isDisabledSystemPackageLPr(packageName)) { // If we don't have a disabled system package to reinstall, the package is // really gone and its permission state should be removed. @@ -403,8 +375,8 @@ final class RemovePackageHelper { mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL); }); } - } else if (!deletedPs.isSystem() && outInfo != null && !outInfo.mIsUpdate - && outInfo.mRemovedUsers != null && !outInfo.mIsExternal) { + } else if (!deletedPs.isSystem() && !outInfo.mIsUpdate + && outInfo.mRemovedUsers != null && !deletedPs.isExternalStorage()) { // For non-system uninstalls with DELETE_KEEP_DATA, set the installed state to false // for affected users. This does not apply to app updates where the old apk is replaced // but the old data remains. @@ -424,7 +396,7 @@ final class RemovePackageHelper { // make sure to preserve per-user installed state if this removal was just // a downgrade of a system app to the factory package boolean installedStateChanged = false; - if (outInfo != null && outInfo.mOrigUsers != null && deletedPs.isSystem()) { + if (outInfo.mOrigUsers != null && deletedPs.isSystem()) { if (DEBUG_REMOVE) { Slog.d(TAG, "Propagating install state across downgrade"); } diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java index 31a63e07b66c..5c6d61e3eaf9 100644 --- a/services/core/java/com/android/server/pm/ScanPackageUtils.java +++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java @@ -74,11 +74,13 @@ import android.util.jar.StrictJarFile; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.component.ParsedProcess; import com.android.internal.pm.pkg.component.ParsedProvider; import com.android.internal.pm.pkg.component.ParsedService; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.ArrayUtils; import com.android.server.SystemConfig; import com.android.server.pm.parsing.PackageInfoUtils; @@ -86,8 +88,6 @@ import com.android.server.pm.parsing.library.PackageBackwardCompatibility; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateUtils; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.utils.WatchedArraySet; import dalvik.system.VMRuntime; diff --git a/services/core/java/com/android/server/pm/ScanRequest.java b/services/core/java/com/android/server/pm/ScanRequest.java index 37cf30bd63fe..41e2a3f3d318 100644 --- a/services/core/java/com/android/server/pm/ScanRequest.java +++ b/services/core/java/com/android/server/pm/ScanRequest.java @@ -22,8 +22,8 @@ import android.os.UserHandle; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; /** A package to be scanned */ @VisibleForTesting diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 75d88da059b5..cfbaae3d0f30 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -89,6 +89,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedPermission; @@ -106,7 +107,6 @@ import com.android.server.LocalServices; import com.android.server.backup.PreferredActivityBackupHelper; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.parsing.PackageInfoUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.permission.LegacyPermissionSettings; import com.android.server.pm.permission.LegacyPermissionState; diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java index 94495bf462f2..ec8af2ecd070 100644 --- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java +++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java @@ -731,7 +731,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable ? PackageManager.DELETE_KEEP_DATA : 0; synchronized (mPm.mInstallLock) { mDeletePackageHelper.deletePackageLIF(pkg.getPackageName(), null, true, - mPm.mUserManager.getUserIds(), flags, null, + mPm.mUserManager.getUserIds(), flags, new PackageRemovedInfo(), true); } } diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java index dddc6b0fbb7a..5c0a15a28285 100644 --- a/services/core/java/com/android/server/pm/SharedUserSetting.java +++ b/services/core/java/com/android/server/pm/SharedUserSetting.java @@ -25,14 +25,14 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.proto.ProtoOutputStream; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedProcess; +import com.android.internal.pm.pkg.component.ParsedProcessImpl; import com.android.internal.util.ArrayUtils; import com.android.server.pm.permission.LegacyPermissionState; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.SharedUserApi; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.component.ParsedProcessImpl; import com.android.server.utils.SnapshotCache; import com.android.server.utils.Watchable; import com.android.server.utils.WatchedArraySet; diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index 70aa19ae9cef..7d87d1b27da0 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -47,11 +47,11 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.policy.AttributeCache; import com.android.internal.util.IndentingPrintWriter; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import java.io.File; import java.io.PrintWriter; @@ -256,13 +256,12 @@ public final class StorageEventHelper extends StorageEventListener { final AndroidPackage pkg = ps.getPkg(); final int deleteFlags = PackageManager.DELETE_KEEP_DATA; - final PackageRemovedInfo outInfo = new PackageRemovedInfo(); try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.getPackageName(), UserHandle.USER_ALL, deleteFlags, "unloadPrivatePackagesInner", ApplicationExitInfo.REASON_OTHER)) { if (mDeletePackageHelper.deletePackageLIF(ps.getPackageName(), null, false, - userIds, deleteFlags, outInfo, false)) { + userIds, deleteFlags, new PackageRemovedInfo(), false)) { unloaded.add(pkg); } else { Slog.w(TAG, "Failed to unload " + ps.getPath()); diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index fe8c12c8e232..c2a960a95394 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -418,11 +418,24 @@ public final class SuspendPackageHelper { } String suspendingPackage = null; + String suspendedBySystem = null; + String qasPackage = null; for (int i = 0; i < userState.getSuspendParams().size(); i++) { suspendingPackage = userState.getSuspendParams().keyAt(i); + var suspendParams = userState.getSuspendParams().valueAt(i); if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) { - return suspendingPackage; + suspendedBySystem = suspendingPackage; } + if (suspendParams.isQuarantined() && qasPackage == null) { + qasPackage = suspendingPackage; + } + } + // Precedence: quarantined, then system, then suspending. + if (qasPackage != null) { + return qasPackage; + } + if (suspendedBySystem != null) { + return suspendedBySystem; } return suspendingPackage; } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index a7b52f4e7a58..c48eccf2aac5 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -532,9 +532,10 @@ public class UserManagerService extends IUserManager.Stub { } final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT, android.content.IntentSender.class); final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL); + final String callingPackage = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); // Call setQuietModeEnabled on bg thread to avoid ANR BackgroundThread.getHandler().post(() -> - setQuietModeEnabled(userId, false, target, /* callingPackage */ null)); + setQuietModeEnabled(userId, false, target, callingPackage)); } }; @@ -1410,7 +1411,7 @@ public class UserManagerService extends IUserManager.Stub { if (onlyIfCredentialNotRequired) { return false; } - showConfirmCredentialToDisableQuietMode(userId, target); + showConfirmCredentialToDisableQuietMode(userId, target, callingPackage); return false; } } @@ -1434,7 +1435,7 @@ public class UserManagerService extends IUserManager.Stub { if (onlyIfCredentialNotRequired) { return false; } - showConfirmCredentialToDisableQuietMode(userId, target); + showConfirmCredentialToDisableQuietMode(userId, target, callingPackage); return false; } setQuietModeEnabled(userId, false /* enableQuietMode */, target, callingPackage); @@ -1604,7 +1605,7 @@ public class UserManagerService extends IUserManager.Stub { * Show confirm credential screen to unlock user in order to turn off quiet mode. */ private void showConfirmCredentialToDisableQuietMode( - @UserIdInt int userId, @Nullable IntentSender target) { + @UserIdInt int userId, @Nullable IntentSender target, @Nullable String callingPackage) { if (android.app.admin.flags.Flags.quietModeCredentialBugFix()) { // TODO (b/308121702) It may be brittle to rely on user states to check profile state int state; @@ -1635,6 +1636,7 @@ public class UserManagerService extends IUserManager.Stub { } callBackIntent.putExtra(Intent.EXTRA_USER_ID, userId); callBackIntent.setPackage(mContext.getPackageName()); + callBackIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackage); callBackIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); final PendingIntent pendingIntent = PendingIntent.getBroadcast( mContext, diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index 7386301bdd6e..14db70e5f72e 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -306,7 +306,6 @@ public final class UserTypeFactory { .setDarkThemeBadgeColors( R.color.white) .setDefaultRestrictions(getDefaultProfileRestrictions()) - .setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings()) .setDefaultUserProperties(new UserProperties.Builder() .setStartWithParent(true) .setCredentialShareableWithParent(true) diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java index 459e2cf7f5c0..79c9c8ec8b0b 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java +++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java @@ -28,9 +28,9 @@ import android.system.StructStat; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.ApexManager; -import com.android.server.pm.parsing.pkg.PackageImpl; import libcore.io.IoUtils; diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index b23dbee5e973..fa54f0ba18cd 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -52,6 +52,9 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; +import com.android.internal.pm.parsing.pkg.PackageImpl; +import com.android.internal.pm.pkg.component.ComponentParseUtils; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedAttribution; import com.android.internal.pm.pkg.component.ParsedComponent; @@ -63,11 +66,12 @@ import com.android.internal.pm.pkg.component.ParsedProcess; import com.android.internal.pm.pkg.component.ParsedProvider; import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.pm.pkg.component.ParsedUsesPermission; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.ArrayUtils; import com.android.server.SystemConfig; import com.android.server.pm.PackageArchiver; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.PackageStateUnserialized; @@ -75,9 +79,6 @@ import com.android.server.pm.pkg.PackageUserState; import com.android.server.pm.pkg.PackageUserStateInternal; import com.android.server.pm.pkg.PackageUserStateUtils; import com.android.server.pm.pkg.SELinuxUtil; -import com.android.server.pm.pkg.component.ComponentParseUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; import java.io.File; import java.util.ArrayList; @@ -273,8 +274,8 @@ public class PackageInfoUtils { final ActivityInfo[] res = new ActivityInfo[N]; for (int i = 0; i < N; i++) { final ParsedActivity a = pkg.getActivities().get(i); - if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), a, - aflags)) { + if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), + a.isEnabled(), a.isDirectBootAware(), a.getName(), aflags)) { if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals( a.getName())) { continue; @@ -293,8 +294,8 @@ public class PackageInfoUtils { final ActivityInfo[] res = new ActivityInfo[size]; for (int i = 0; i < size; i++) { final ParsedActivity a = pkg.getReceivers().get(i); - if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), a, - flags)) { + if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), + a.isEnabled(), a.isDirectBootAware(), a.getName(), flags)) { res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo, userId, pkgSetting); } @@ -309,8 +310,8 @@ public class PackageInfoUtils { final ServiceInfo[] res = new ServiceInfo[size]; for (int i = 0; i < size; i++) { final ParsedService s = pkg.getServices().get(i); - if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), s, - flags)) { + if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), + s.isEnabled(), s.isDirectBootAware(), s.getName(), flags)) { res[num++] = generateServiceInfo(pkg, s, flags, state, applicationInfo, userId, pkgSetting); } @@ -326,8 +327,8 @@ public class PackageInfoUtils { for (int i = 0; i < size; i++) { final ParsedProvider pr = pkg.getProviders() .get(i); - if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), pr, - flags)) { + if (PackageUserStateUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), + pr.isEnabled(), pr.isDirectBootAware(), pr.getName(), flags)) { res[num++] = generateProviderInfo(pkg, pr, flags, state, applicationInfo, userId, pkgSetting); } @@ -923,7 +924,7 @@ public class PackageInfoUtils { | flag(pkg.isExtraLargeScreensSupported(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) - | flag(AndroidPackageUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM) + | flag(AndroidPackageLegacyUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM) | flag(pkg.isFactoryTest(), ApplicationInfo.FLAG_FACTORY_TEST); return appInfoFlags(pkgWithoutStateFlags, pkgSetting); @@ -964,12 +965,12 @@ public class PackageInfoUtils { | flag(pkg.isSaveStateDisallowed(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING) - | flag(AndroidPackageUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) - | flag(AndroidPackageUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) - | flag(AndroidPackageUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM) - | flag(AndroidPackageUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR) - | flag(AndroidPackageUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT) - | flag(AndroidPackageUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM) + | flag(AndroidPackageLegacyUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) + | flag(AndroidPackageLegacyUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) + | flag(AndroidPackageLegacyUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM) + | flag(AndroidPackageLegacyUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR) + | flag(AndroidPackageLegacyUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT) + | flag(AndroidPackageLegacyUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM) | flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY); Boolean resizeableActivity = pkg.getResizeableActivity(); diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java index 1c751e07bbbe..b6a08a5a546f 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java +++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java @@ -35,17 +35,19 @@ import android.util.DisplayMetrics; import android.util.Slog; import com.android.internal.compat.IPlatformCompat; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.internal.pm.pkg.parsing.ParsingUtils; import com.android.internal.util.ArrayUtils; +import com.android.server.SystemConfig; import com.android.server.pm.PackageManagerException; import com.android.server.pm.PackageManagerService; -import com.android.server.pm.parsing.pkg.PackageImpl; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingUtils; import java.io.File; import java.util.List; +import java.util.Set; /** * The v2 of package parsing for use when parsing is initiated in the server and must @@ -88,6 +90,16 @@ public class PackageParser2 implements AutoCloseable { // behavior. return false; } + + @Override + public Set<String> getHiddenApiWhitelistedApps() { + return SystemConfig.getInstance().getHiddenApiWhitelistedApps(); + } + + @Override + public Set<String> getInstallConstraintsAllowlist() { + return SystemConfig.getInstance().getInstallConstraintsAllowlist(); + } }); } @@ -221,7 +233,7 @@ public class PackageParser2 implements AutoCloseable { @NonNull String baseCodePath, @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp) { return PackageImpl.forParsing(packageName, baseCodePath, codePath, manifestArray, - isCoreApp); + isCoreApp, Callback.this); } /** diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java index 61be6e1036f2..1b7c7ad94dc9 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java @@ -29,7 +29,9 @@ import android.content.pm.parsing.result.ParseTypeImpl; import android.os.incremental.IncrementalManager; import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.pm.parsing.PackageParserException; import com.android.internal.pm.parsing.pkg.AndroidPackageHidden; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedInstrumentation; import com.android.internal.pm.pkg.component.ParsedProvider; @@ -37,7 +39,6 @@ import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.pm.pkg.parsing.ParsingPackageHidden; import com.android.internal.util.ArrayUtils; import com.android.server.SystemConfig; -import com.android.server.pm.PackageManagerException; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; @@ -134,10 +135,10 @@ public class AndroidPackageUtils { /** * Validate the dex metadata files installed for the given package. * - * @throws PackageManagerException in case of errors. + * @throws PackageParserException in case of errors. */ public static void validatePackageDexMetadata(AndroidPackage pkg) - throws PackageManagerException { + throws PackageParserException { Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values(); String packageName = pkg.getPackageName(); long versionCode = pkg.getLongVersionCode(); @@ -146,7 +147,7 @@ public class AndroidPackageUtils { final ParseResult result = DexMetadataHelper.validateDexMetadataFile( input.reset(), dexMetadata, packageName, versionCode); if (result.isError()) { - throw new PackageManagerException( + throw new PackageParserException( result.getErrorCode(), result.getErrorMessage(), result.getException()); } } @@ -314,60 +315,4 @@ public class AndroidPackageUtils { info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode(); info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor(); } - - /** - * @deprecated Use {@link PackageState#isSystem} - */ - @Deprecated - public static boolean isSystem(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isSystem(); - } - - /** - * @deprecated Use {@link PackageState#isSystemExt} - */ - @Deprecated - public static boolean isSystemExt(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isSystemExt(); - } - - /** - * @deprecated Use {@link PackageState#isPrivileged} - */ - @Deprecated - public static boolean isPrivileged(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isPrivileged(); - } - - /** - * @deprecated Use {@link PackageState#isOem} - */ - @Deprecated - public static boolean isOem(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isOem(); - } - - /** - * @deprecated Use {@link PackageState#isVendor} - */ - @Deprecated - public static boolean isVendor(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isVendor(); - } - - /** - * @deprecated Use {@link PackageState#isProduct} - */ - @Deprecated - public static boolean isProduct(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isProduct(); - } - - /** - * @deprecated Use {@link PackageState#isOdm} - */ - @Deprecated - public static boolean isOdm(@NonNull AndroidPackage pkg) { - return ((AndroidPackageHidden) pkg).isOdm(); - } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index 8bd2d94667f9..671e031b546b 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -120,8 +120,11 @@ import com.android.internal.compat.IPlatformCompat; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.os.RoSystemProperties; +import com.android.internal.pm.permission.CompatibilityPermissionInfo; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedPermission; import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.component.ParsedPermissionUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.IntPair; @@ -144,8 +147,6 @@ import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.SharedUserApi; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.component.ParsedPermissionUtils; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.SoftRestrictedPermissionPolicy; diff --git a/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java b/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java index 6cbc1de75010..6a156415982c 100644 --- a/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java +++ b/services/core/java/com/android/server/pm/pkg/SELinuxUtil.java @@ -16,6 +16,8 @@ package com.android.server.pm.pkg; +import com.android.internal.pm.pkg.SEInfoUtil; + /** * Utility methods that need to be used in application space. * @hide @@ -23,10 +25,10 @@ package com.android.server.pm.pkg; public final class SELinuxUtil { /** Append to existing seinfo label for instant apps @hide */ - private static final String INSTANT_APP_STR = ":ephemeralapp"; + private static final String INSTANT_APP_STR = SEInfoUtil.INSTANT_APP_STR; /** Append to existing seinfo when modifications are complete @hide */ - public static final String COMPLETE_STR = ":complete"; + public static final String COMPLETE_STR = SEInfoUtil.COMPLETE_STR; /** @hide */ public static String getSeinfoUser(PackageUserState userState) { diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java index 532a7f8f893f..c9da99da7902 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java @@ -45,11 +45,13 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.pm.pkg.component.ComponentMutateUtils; import com.android.internal.pm.pkg.component.ParsedActivity; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedMainComponent; import com.android.internal.pm.pkg.component.ParsedProvider; +import com.android.internal.pm.pkg.component.ParsedProviderImpl; import com.android.internal.pm.pkg.component.ParsedService; import com.android.internal.util.ArrayUtils; import com.android.server.IntentResolver; @@ -62,8 +64,6 @@ import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.PackageStateUtils; import com.android.server.pm.pkg.PackageUserStateInternal; -import com.android.server.pm.pkg.component.ComponentMutateUtils; -import com.android.server.pm.pkg.component.ParsedProviderImpl; import com.android.server.pm.snapshot.PackageDataSnapshot; import com.android.server.utils.Snappable; import com.android.server.utils.SnapshotCache; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4e5dc1dd76fa..938ed2329ffd 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -76,7 +76,6 @@ import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; -import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; @@ -97,7 +96,6 @@ import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY; import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE; import android.accessibilityservice.AccessibilityService; -import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -124,7 +122,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; @@ -204,8 +201,6 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; -import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; -import com.android.internal.accessibility.util.AccessibilityUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; import com.android.internal.display.BrightnessUtils; @@ -385,8 +380,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { public static final String TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD = "waitForAllWindowsDrawn"; - private static final String TALKBACK_LABEL = "TalkBack"; - private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800; /** @@ -477,6 +470,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** Controller that supports enabling an AccessibilityService by holding down the volume keys */ private AccessibilityShortcutController mAccessibilityShortcutController; + private TalkbackShortcutController mTalkbackShortcutController; + boolean mSafeMode; // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key. @@ -813,7 +808,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { handleScreenShot(msg.arg1); break; case MSG_SWITCH_KEYBOARD_LAYOUT: - handleSwitchKeyboardLayout(msg.arg1, msg.arg2); + SwitchKeyboardLayoutMessageObject object = + (SwitchKeyboardLayoutMessageObject) msg.obj; + handleSwitchKeyboardLayout(object.keyEvent, object.direction, + object.focusedToken); break; case MSG_LOG_KEYBOARD_SYSTEM_EVENT: handleKeyboardSystemEvent(KeyboardLogEvent.from(msg.arg1), (KeyEvent) msg.obj); @@ -934,6 +932,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private record SwitchKeyboardLayoutMessageObject(KeyEvent keyEvent, IBinder focusedToken, + int direction) { + } + final IPersistentVrStateCallbacks mPersistentVrModeListener = new IPersistentVrStateCallbacks.Stub() { @Override @@ -1602,19 +1604,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (DEBUG_INPUT) { Slog.d(TAG, "Executing stem primary triple press action behavior."); } - - if (Settings.System.getIntForUser(mContext.getContentResolver(), - Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, - /* def= */ 0, UserHandle.USER_CURRENT) == 1) { - /** Toggle talkback begin */ - ComponentName componentName = getTalkbackComponent(); - if (componentName != null && toggleTalkBack(componentName)) { - /** log stem triple press telemetry if it's a talkback enabled event */ - logStemTriplePressAccessibilityTelemetry(componentName); - } - performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */ false, - /* reason = */ "Stem primary - Triple Press - Toggle Accessibility"); - /** Toggle talkback end */ + mTalkbackShortcutController.toggleTalkback(mCurrentUserId); + if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) { + performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */ + false, /* reason = */ + "Stem primary - Triple Press - Toggle Accessibility"); } break; } @@ -1640,61 +1634,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** - * A function that toggles talkback service - * - * @return {@code true} if talkback is enabled, {@code false} if talkback is disabled - */ - private boolean toggleTalkBack(ComponentName componentName) { - final Set<ComponentName> enabledServices = - AccessibilityUtils.getEnabledServicesFromSettings(mContext, mCurrentUserId); - - boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName); - AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, - !isTalkbackAlreadyEnabled); - /** if isTalkbackAlreadyEnabled is true, then it's a disabled event so return false - * and if isTalkbackAlreadyEnabled is false, return true as it's an enabled event */ - return !isTalkbackAlreadyEnabled; - } - - /** - * A function that logs stem triple press accessibility telemetry - * If the user setup (Oobe) is not completed, set the - * WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE - * setting which will be later logged via Settings Snapshot - * else, log ACCESSIBILITY_SHORTCUT_REPORTED atom - */ - private void logStemTriplePressAccessibilityTelemetry(ComponentName componentName) { - if (!AccessibilityUtils.isUserSetupCompleted(mContext)) { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, 1); - } else { - AccessibilityStatsLogUtils.logAccessibilityShortcutActivated(mContext, componentName, - ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE, - /* serviceEnabled= */ true); - } - } - - private ComponentName getTalkbackComponent() { - AccessibilityManager accessibilityManager = mContext.getSystemService( - AccessibilityManager.class); - List<AccessibilityServiceInfo> serviceInfos = - accessibilityManager.getInstalledAccessibilityServiceList(); - - for (AccessibilityServiceInfo service : serviceInfos) { - final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo; - if (isTalkback(serviceInfo)) { - return new ComponentName(serviceInfo.packageName, serviceInfo.name); - } - } - return null; - } - - private boolean isTalkback(ServiceInfo info) { - String label = info.loadLabel(mPackageManager).toString(); - return label.equals(TALKBACK_LABEL); - } - - /** * Load most recent task (expect current task) and bring it to the front. */ void performStemPrimaryDoublePressSwitchToRecentTask() { @@ -1731,12 +1670,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TRIPLE_PRESS_PRIMARY_NOTHING: break; case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY: - if (Settings.System.getIntForUser( - mContext.getContentResolver(), - Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, - /* def= */ 0, - UserHandle.USER_CURRENT) - == 1) { + if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) { return 3; } break; @@ -2252,6 +2186,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { ButtonOverridePermissionChecker getButtonOverridePermissionChecker() { return new ButtonOverridePermissionChecker(); } + + TalkbackShortcutController getTalkbackShortcutController() { + return new TalkbackShortcutController(mContext); + } } /** {@inheritDoc} */ @@ -2515,6 +2453,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardDrawnTimeout = mContext.getResources().getInteger( com.android.internal.R.integer.config_keyguardDrawnTimeout); mKeyguardDelegate = injector.getKeyguardServiceDelegate(); + mTalkbackShortcutController = injector.getTalkbackShortcutController(); initKeyCombinationRules(); initSingleKeyGestureRules(injector.getLooper()); mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker(); @@ -3709,7 +3648,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_LANGUAGE_SWITCH: if (firstDown) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; - sendSwitchKeyboardLayout(event, direction); + sendSwitchKeyboardLayout(event, focusedToken, direction); logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH); return true; } @@ -3978,7 +3917,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { + ", policyFlags=" + policyFlags); } - if (interceptUnhandledKey(event)) { + if (interceptUnhandledKey(event, focusedToken)) { return null; } @@ -4036,7 +3975,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return fallbackEvent; } - private boolean interceptUnhandledKey(KeyEvent event) { + private boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) { final int keyCode = event.getKeyCode(); final int repeatCount = event.getRepeatCount(); final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; @@ -4049,7 +3988,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK, KeyEvent.META_CTRL_ON)) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; - sendSwitchKeyboardLayout(event, direction); + sendSwitchKeyboardLayout(event, focusedToken, direction); return true; } } @@ -4105,16 +4044,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void sendSwitchKeyboardLayout(@NonNull KeyEvent event, int direction) { - mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, event.getDeviceId(), - direction).sendToTarget(); + private void sendSwitchKeyboardLayout(@NonNull KeyEvent event, + @Nullable IBinder focusedToken, int direction) { + SwitchKeyboardLayoutMessageObject object = + new SwitchKeyboardLayoutMessageObject(event, focusedToken, direction); + mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, object).sendToTarget(); } - private void handleSwitchKeyboardLayout(int deviceId, int direction) { + private void handleSwitchKeyboardLayout(@NonNull KeyEvent event, int direction, + IBinder focusedToken) { if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) { - InputMethodManagerInternal.get().switchKeyboardLayout(direction); + IBinder targetWindowToken = + mWindowManagerInternal.getTargetWindowTokenFromInputToken(focusedToken); + InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction, + event.getDisplayId(), targetWindowToken); } else { - mWindowManagerFuncs.switchKeyboardLayout(deviceId, direction); + mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); } } @@ -4124,7 +4069,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if ((actions & ACTION_PASS_TO_USER) != 0) { long delayMillis = interceptKeyBeforeDispatching( focusedToken, fallbackEvent, policyFlags); - if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent)) { + if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent, focusedToken)) { return true; } } diff --git a/services/core/java/com/android/server/policy/TalkbackShortcutController.java b/services/core/java/com/android/server/policy/TalkbackShortcutController.java new file mode 100644 index 000000000000..906da2f4cdf5 --- /dev/null +++ b/services/core/java/com/android/server/policy/TalkbackShortcutController.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + +import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.UserHandle; +import android.provider.Settings; +import android.view.accessibility.AccessibilityManager; + +import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; +import com.android.internal.accessibility.util.AccessibilityUtils; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.List; +import java.util.Set; + +/** + * This class controls talkback shortcut related operations such as toggling, quering and + * logging. + */ +@VisibleForTesting +class TalkbackShortcutController { + private static final String TALKBACK_LABEL = "TalkBack"; + private final Context mContext; + private final PackageManager mPackageManager; + + TalkbackShortcutController(Context context) { + mContext = context; + mPackageManager = mContext.getPackageManager(); + } + + /** + * A function that toggles talkback service. + * + * @return talkback state after toggle. {@code true} if talkback is enabled, {@code false} if + * talkback is disabled + */ + boolean toggleTalkback(int userId) { + final Set<ComponentName> enabledServices = + AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId); + ComponentName componentName = getTalkbackComponent(); + boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName); + + if (isTalkBackShortcutGestureEnabled()) { + isTalkbackAlreadyEnabled = !isTalkbackAlreadyEnabled; + AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, + isTalkbackAlreadyEnabled); + + // log stem triple press telemetry if it's a talkback enabled event. + if (componentName != null && isTalkbackAlreadyEnabled) { + logStemTriplePressAccessibilityTelemetry(componentName); + } + } + return isTalkbackAlreadyEnabled; + } + + private ComponentName getTalkbackComponent() { + AccessibilityManager accessibilityManager = mContext.getSystemService( + AccessibilityManager.class); + List<AccessibilityServiceInfo> serviceInfos = + accessibilityManager.getInstalledAccessibilityServiceList(); + + for (AccessibilityServiceInfo service : serviceInfos) { + final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo; + if (isTalkback(serviceInfo)) { + return new ComponentName(serviceInfo.packageName, serviceInfo.name); + } + } + return null; + } + + boolean isTalkBackShortcutGestureEnabled() { + return Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED, + /* def= */ 0, UserHandle.USER_CURRENT) == 1; + } + + /** + * A function that logs stem triple press accessibility telemetry. If the user setup (Oobe) + * is not completed, set the WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE setting which + * will be later logged via Settings Snapshot else, log ACCESSIBILITY_SHORTCUT_REPORTED atom + */ + private void logStemTriplePressAccessibilityTelemetry(ComponentName componentName) { + if (!AccessibilityUtils.isUserSetupCompleted(mContext)) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, 1); + return; + } + AccessibilityStatsLogUtils.logAccessibilityShortcutActivated(mContext, + componentName, + ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE, + /* serviceEnabled= */ true); + } + + private boolean isTalkback(ServiceInfo info) { + return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString()); + } +} diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java index abfe9debc7de..e1eb8f07dd2c 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java @@ -20,7 +20,6 @@ import android.annotation.Nullable; import android.os.ConditionVariable; import android.os.Handler; import android.os.PersistableBundle; -import android.util.FastImmutableArraySet; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -30,8 +29,9 @@ import com.android.internal.os.PowerStats; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.function.Consumer; -import java.util.stream.Stream; /** * Collects snapshots of power-related system statistics. @@ -246,8 +246,7 @@ public abstract class PowerStatsCollector { @GuardedBy("this") @SuppressWarnings("unchecked") - private volatile FastImmutableArraySet<Consumer<PowerStats>> mConsumerList = - new FastImmutableArraySet<Consumer<PowerStats>>(new Consumer[0]); + private volatile List<Consumer<PowerStats>> mConsumerList = Collections.emptyList(); public PowerStatsCollector(Handler handler, long throttlePeriodMs, Clock clock) { mHandler = handler; @@ -262,9 +261,13 @@ public abstract class PowerStatsCollector { @SuppressWarnings("unchecked") public void addConsumer(Consumer<PowerStats> consumer) { synchronized (this) { - mConsumerList = new FastImmutableArraySet<Consumer<PowerStats>>( - Stream.concat(mConsumerList.stream(), Stream.of(consumer)) - .toArray(Consumer[]::new)); + if (mConsumerList.contains(consumer)) { + return; + } + + List<Consumer<PowerStats>> newList = new ArrayList<>(mConsumerList); + newList.add(consumer); + mConsumerList = Collections.unmodifiableList(newList); } } @@ -275,9 +278,9 @@ public abstract class PowerStatsCollector { @SuppressWarnings("unchecked") public void removeConsumer(Consumer<PowerStats> consumer) { synchronized (this) { - mConsumerList = new FastImmutableArraySet<Consumer<PowerStats>>( - mConsumerList.stream().filter(c -> c != consumer) - .toArray(Consumer[]::new)); + List<Consumer<PowerStats>> newList = new ArrayList<>(mConsumerList); + newList.remove(consumer); + mConsumerList = Collections.unmodifiableList(newList); } } @@ -302,8 +305,9 @@ public abstract class PowerStatsCollector { if (stats == null) { return; } - for (Consumer<PowerStats> consumer : mConsumerList) { - consumer.accept(stats); + List<Consumer<PowerStats>> consumerList = mConsumerList; + for (int i = consumerList.size() - 1; i >= 0; i--) { + consumerList.get(i).accept(stats); } } diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java index 97d872a1a539..121a98bab37a 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java @@ -18,7 +18,6 @@ package com.android.server.power.stats; import android.annotation.DurationMillisLong; import android.app.AlarmManager; -import android.content.Context; import android.os.ConditionVariable; import android.os.Handler; import android.util.IndentingPrintWriter; @@ -30,6 +29,7 @@ import com.android.internal.os.MonotonicClock; import java.io.PrintWriter; import java.util.Calendar; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; /** * Controls the frequency at which {@link PowerStatsSpan}'s are generated and stored in @@ -39,7 +39,7 @@ public class PowerStatsScheduler { private static final long MINUTE_IN_MILLIS = TimeUnit.MINUTES.toMillis(1); private static final long HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1); - private final Context mContext; + private final AlarmScheduler mAlarmScheduler; private boolean mEnablePeriodicPowerStatsCollection; @DurationMillisLong private final long mAggregatedPowerStatsSpanDuration; @@ -49,24 +49,38 @@ public class PowerStatsScheduler { private final Clock mClock; private final MonotonicClock mMonotonicClock; private final Handler mHandler; - private final BatteryStatsImpl mBatteryStats; + private final Runnable mPowerStatsCollector; + private final Supplier<Long> mEarliestAvailableBatteryHistoryTimeMs; private final PowerStatsAggregator mPowerStatsAggregator; private long mLastSavedSpanEndMonotonicTime; - public PowerStatsScheduler(Context context, PowerStatsAggregator powerStatsAggregator, + /** + * External dependency on AlarmManager. + */ + public interface AlarmScheduler { + /** + * Should use AlarmManager to schedule an inexact, non-wakeup alarm. + */ + void scheduleAlarm(long triggerAtMillis, String tag, + AlarmManager.OnAlarmListener onAlarmListener, Handler handler); + } + + public PowerStatsScheduler(Runnable powerStatsCollector, + PowerStatsAggregator powerStatsAggregator, @DurationMillisLong long aggregatedPowerStatsSpanDuration, @DurationMillisLong long powerStatsAggregationPeriod, PowerStatsStore powerStatsStore, - Clock clock, MonotonicClock monotonicClock, Handler handler, - BatteryStatsImpl batteryStats) { - mContext = context; + AlarmScheduler alarmScheduler, Clock clock, MonotonicClock monotonicClock, + Supplier<Long> earliestAvailableBatteryHistoryTimeMs, Handler handler) { mPowerStatsAggregator = powerStatsAggregator; mAggregatedPowerStatsSpanDuration = aggregatedPowerStatsSpanDuration; mPowerStatsAggregationPeriod = powerStatsAggregationPeriod; mPowerStatsStore = powerStatsStore; + mAlarmScheduler = alarmScheduler; mClock = clock; mMonotonicClock = monotonicClock; mHandler = handler; - mBatteryStats = batteryStats; + mPowerStatsCollector = powerStatsCollector; + mEarliestAvailableBatteryHistoryTimeMs = earliestAvailableBatteryHistoryTimeMs; } /** @@ -81,9 +95,8 @@ public class PowerStatsScheduler { } private void scheduleNextPowerStatsAggregation() { - AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class); - alarmManager.set(AlarmManager.ELAPSED_REALTIME, - mClock.elapsedRealtime() + mPowerStatsAggregationPeriod, "PowerStats", + mAlarmScheduler.scheduleAlarm(mClock.elapsedRealtime() + mPowerStatsAggregationPeriod, + "PowerStats", () -> { schedulePowerStatsAggregation(); mHandler.post(this::scheduleNextPowerStatsAggregation); @@ -96,7 +109,7 @@ public class PowerStatsScheduler { @VisibleForTesting public void schedulePowerStatsAggregation() { // Catch up the power stats collectors - mBatteryStats.schedulePowerStatsSampleCollection(); + mPowerStatsCollector.run(); mHandler.post(this::aggregateAndStorePowerStats); } @@ -105,7 +118,7 @@ public class PowerStatsScheduler { long currentMonotonicTime = mMonotonicClock.monotonicTime(); long startTime = getLastSavedSpanEndMonotonicTime(); if (startTime < 0) { - startTime = mBatteryStats.getHistory().getStartTime(); + startTime = mEarliestAvailableBatteryHistoryTimeMs.get(); } long endTimeMs = alignToWallClock(startTime + mAggregatedPowerStatsSpanDuration, mAggregatedPowerStatsSpanDuration, currentMonotonicTime, currentTimeMillis); diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index eac4fc00b667..9a85c42e1a10 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -1608,14 +1608,12 @@ public class TrustManagerService extends SystemService { user.name, user.id, user.flags); if (!user.supportsSwitchToByUser()) { final boolean locked; - if (user.isProfile()) { - if (mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)) { - fout.print(" (profile with separate challenge)"); - locked = isDeviceLockedInner(user.id); - } else { - fout.print(" (profile with unified challenge)"); - locked = isDeviceLockedInner(resolveProfileParent(user.id)); - } + if (mLockPatternUtils.isProfileWithUnifiedChallenge(user.id)) { + fout.print(" (profile with unified challenge)"); + locked = isDeviceLockedInner(resolveProfileParent(user.id)); + } else if (mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)) { + fout.print(" (profile with separate challenge)"); + locked = isDeviceLockedInner(user.id); } else { fout.println(" (user that cannot be switched to)"); locked = isDeviceLockedInner(user.id); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 6f2750767094..d903ad4d9f0d 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -41,7 +41,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; -import android.content.res.Resources; import android.graphics.Rect; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; @@ -2081,6 +2080,45 @@ public final class TvInputManagerService extends SystemService { } @Override + public void stopPlayback(IBinder sessionToken, int mode, int userId) { + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "stopPlayback"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + getSessionLocked(sessionToken, callingUid, resolvedUserId).stopPlayback( + mode); + } catch (RemoteException | SessionNotFoundException e) { + Slog.e(TAG, "error in stopPlayback(mode)", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void startPlayback(IBinder sessionToken, int userId) { + final int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, + userId, "stopPlayback"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + getSessionLocked(sessionToken, callingUid, resolvedUserId).startPlayback(); + } catch (RemoteException | SessionNotFoundException e) { + Slog.e(TAG, "error in startPlayback()", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void timeShiftPlay(IBinder sessionToken, final Uri recordedProgramUri, int userId) { final int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java new file mode 100644 index 000000000000..2eeb903bb551 --- /dev/null +++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.frameworks.vibrator.IVibratorControlService; +import android.frameworks.vibrator.IVibratorController; +import android.frameworks.vibrator.VibrationParam; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import java.util.Objects; + +/** + * Implementation of {@link IVibratorControlService} which allows the registration of + * {@link IVibratorController} to set and receive vibration params. + * + * @hide + */ +public final class VibratorControlService extends IVibratorControlService.Stub { + private static final String TAG = "VibratorControlService"; + + private final VibratorControllerHolder mVibratorControllerHolder; + private final Object mLock; + + public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) { + mVibratorControllerHolder = vibratorControllerHolder; + mLock = lock; + } + + @Override + public void registerVibratorController(IVibratorController controller) + throws RemoteException { + synchronized (mLock) { + mVibratorControllerHolder.setVibratorController(controller); + } + } + + @Override + public void unregisterVibratorController(@NonNull IVibratorController controller) + throws RemoteException { + Objects.requireNonNull(controller); + + synchronized (mLock) { + if (mVibratorControllerHolder.getVibratorController() == null) { + Slog.w(TAG, "Received request to unregister IVibratorController = " + + controller + ", but no controller was previously registered. Request " + + "Ignored."); + return; + } + if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(), + controller.asBinder())) { + Slog.wtf(TAG, "Failed to unregister IVibratorController. The provided " + + "controller doesn't match the registered one. " + this); + return; + } + mVibratorControllerHolder.setVibratorController(null); + } + } + + @Override + public void setVibrationParams( + @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token) + throws RemoteException { + // TODO(b/305939964): Add set vibration implementation. + } + + @Override + public void clearVibrationParams(int types, IVibratorController token) throws RemoteException { + // TODO(b/305939964): Add clear vibration implementation. + } + + @Override + public void onRequestVibrationParamsComplete( + IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) + throws RemoteException { + // TODO(305942827): Cache the vibration params in VibrationScaler + } + + @Override + public int getInterfaceVersion() throws RemoteException { + return this.VERSION; + } + + @Override + public String getInterfaceHash() throws RemoteException { + return this.HASH; + } +} diff --git a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java new file mode 100644 index 000000000000..63e69db9480f --- /dev/null +++ b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.annotation.NonNull; +import android.frameworks.vibrator.IVibratorController; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +/** + * Holder class for {@link IVibratorController}. + * + * @hide + */ +public final class VibratorControllerHolder implements IBinder.DeathRecipient { + private static final String TAG = "VibratorControllerHolder"; + + private IVibratorController mVibratorController; + + public IVibratorController getVibratorController() { + return mVibratorController; + } + + /** + * Sets the {@link IVibratorController} in {@link VibratorControllerHolder} to the new + * controller. This will also take care of registering and unregistering death notifications + * for the cached {@link IVibratorController}. + */ + public void setVibratorController(IVibratorController controller) { + try { + if (mVibratorController != null) { + mVibratorController.asBinder().unlinkToDeath(this, 0); + } + mVibratorController = controller; + if (mVibratorController != null) { + mVibratorController.asBinder().linkToDeath(this, 0); + } + } catch (RemoteException e) { + Slog.wtf(TAG, "Failed to set IVibratorController: " + this, e); + } + } + + @Override + public void binderDied(@NonNull IBinder deadBinder) { + if (deadBinder == mVibratorController.asBinder()) { + setVibratorController(null); + } + } + + @Override + public void binderDied() { + // Should not be used as binderDied(IBinder who) is overridden. + Slog.wtf(TAG, "binderDied() called unexpectedly."); + } +} diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 7d4bd3baf613..fc824abd80f5 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -91,6 +91,8 @@ import java.util.function.Function; public class VibratorManagerService extends IVibratorManagerService.Stub { private static final String TAG = "VibratorManagerService"; private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service"; + private static final String VIBRATOR_CONTROL_SERVICE = + "android.frameworks.vibrator.IVibratorControlService/default"; private static final boolean DEBUG = false; private static final VibrationAttributes DEFAULT_ATTRIBUTES = new VibrationAttributes.Builder().build(); @@ -269,6 +271,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED); injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); + if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) { + injector.addService(VIBRATOR_CONTROL_SERVICE, + new VibratorControlService(new VibratorControllerHolder(), mLock)); + } + } /** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */ diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java index 81e5fbd564e0..769f01c34e17 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java @@ -142,8 +142,11 @@ public class ActivityMetricsLaunchObserver { * if the launching activity is started from an existing launch sequence (trampoline) * but cannot coalesce to the existing one, e.g. to a different display. * @param name The launching activity name. + * @param temperature The temperature at which a launch sequence had started. + * @param userId The id of the user the activity is being launched for. */ - public void onActivityLaunched(long id, ComponentName name, @Temperature int temperature) { + public void onActivityLaunched(long id, ComponentName name, @Temperature int temperature, + int userId) { } /** @@ -177,13 +180,15 @@ public class ActivityMetricsLaunchObserver { * @param timestampNanos the timestamp of ActivityLaunchFinished event in nanoseconds. * To compute the TotalTime duration, deduct the timestamp {@link #onIntentStarted} * from {@code timestampNanos}. + * @param launchMode The activity launch mode. * * @apiNote The finishing activity isn't necessarily the same as the starting activity; * in the case of a trampoline, multiple activities could've been started * and only the latest activity that was top-most during first-frame drawn * is reported here. */ - public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos) { + public void onActivityLaunchFinished(long id, ComponentName name, long timestampNanos, + int launchMode) { } /** diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 7b20529ce5e1..78f501ad9fed 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -1737,7 +1737,8 @@ class ActivityMetricsLogger { // Beginning a launch is timing sensitive and so should be observed as soon as possible. mLaunchObserver.onActivityLaunched(info.mLaunchingState.mStartUptimeNs, - info.mLastLaunchedActivity.mActivityComponent, temperature); + info.mLastLaunchedActivity.mActivityComponent, temperature, + info.mLastLaunchedActivity.mUserId); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } @@ -1774,7 +1775,8 @@ class ActivityMetricsLogger { "MetricsLogger:launchObserverNotifyActivityLaunchFinished"); mLaunchObserver.onActivityLaunchFinished(info.mLaunchingState.mStartUptimeNs, - info.mLastLaunchedActivity.mActivityComponent, timestampNs); + info.mLastLaunchedActivity.mActivityComponent, timestampNs, + info.mLastLaunchedActivity.launchMode); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 73edb4bed6ca..869bcc0f9b8f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1053,6 +1053,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mWindowManager = wm; mRootWindowContainer = wm.mRoot; mWindowOrganizerController.mTransitionController.setWindowManager(wm); + mLifecycleManager.setWindowManager(wm); mTempConfig.setToDefaults(); mTempConfig.setLocales(LocaleList.getDefault()); mConfigurationSeq = mTempConfig.seq = 1; @@ -1274,7 +1275,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) { - final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions); assertPackageMatchesCallingUid(callingPackage); @@ -1315,7 +1315,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .setActivityOptions(opts) .setUserId(userId) .execute(); - } @Override diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 92665af1075b..4929df8061b2 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static android.os.Process.SYSTEM_UID; import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER; @@ -49,7 +50,6 @@ import android.compat.annotation.EnabledAfter; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; -import android.os.Build; import android.os.Process; import android.os.UserHandle; import android.provider.DeviceConfig; @@ -86,7 +86,7 @@ public class BackgroundActivityStartController { private static final int NO_PROCESS_UID = -1; /** If enabled the creator will not allow BAL on its behalf by default. */ @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE) private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR = 296478951; public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED = @@ -228,6 +228,7 @@ public class BackgroundActivityStartController { private final Intent mIntent; private final WindowProcessController mCallerApp; private final WindowProcessController mRealCallerApp; + private final boolean mIsCallForResult; private BalState(int callingUid, int callingPid, final String callingPackage, int realCallingUid, int realCallingPid, @@ -247,8 +248,10 @@ public class BackgroundActivityStartController { mOriginatingPendingIntent = originatingPendingIntent; mIntent = intent; mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid); - if (originatingPendingIntent == null // not a PendingIntent - || resultRecord != null // sent for result + mIsCallForResult = resultRecord != null; + if (balRequireOptInByPendingIntentCreator() // auto-opt in introduced with this feature + && (originatingPendingIntent == null // not a PendingIntent + || mIsCallForResult) // sent for result ) { // grant BAL privileges unless explicitly opted out mBalAllowedByPiCreatorWithHardening = mBalAllowedByPiCreator = @@ -351,6 +354,19 @@ public class BackgroundActivityStartController { return name + "[debugOnly]"; } + /** @return valid targetSdk or <code>-1</code> */ + private int getTargetSdk(String packageName) { + if (packageName == null) { + return -1; + } + try { + PackageManager pm = mService.mContext.getPackageManager(); + return pm.getTargetSdkVersion(packageName); + } catch (Exception e) { + return -1; + } + } + private boolean hasRealCaller() { return mRealCallingUid != NO_PROCESS_UID; } @@ -368,6 +384,7 @@ public class BackgroundActivityStartController { StringBuilder sb = new StringBuilder(2048); sb.append("[callingPackage: ") .append(getDebugPackageName(mCallingPackage, mCallingUid)); + sb.append("; callingPackageTargetSdk: ").append(getTargetSdk(mCallingPackage)); sb.append("; callingUid: ").append(mCallingUid); sb.append("; callingPid: ").append(mCallingPid); sb.append("; appSwitchState: ").append(mAppSwitchState); @@ -387,10 +404,13 @@ public class BackgroundActivityStartController { .append(mBalAllowedByPiCreatorWithHardening); sb.append("; resultIfPiCreatorAllowsBal: ").append(resultIfPiCreatorAllowsBal); sb.append("; hasRealCaller: ").append(hasRealCaller()); + sb.append("; isCallForResult: ").append(mIsCallForResult); sb.append("; isPendingIntent: ").append(isPendingIntent()); if (hasRealCaller()) { sb.append("; realCallingPackage: ") .append(getDebugPackageName(mRealCallingPackage, mRealCallingUid)); + sb.append("; realCallingPackageTargetSdk: ") + .append(getTargetSdk(mRealCallingPackage)); sb.append("; realCallingUid: ").append(mRealCallingUid); sb.append("; realCallingPid: ").append(mRealCallingPid); sb.append("; realCallingUidHasAnyVisibleWindow: ") diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java index 8b282dd3e7a8..6b6388b10ce7 100644 --- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java +++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java @@ -22,7 +22,13 @@ import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; import android.os.Binder; +import android.os.IBinder; import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; /** * Class that is able to combine multiple client lifecycle transition requests and/or callbacks, @@ -31,8 +37,18 @@ import android.os.RemoteException; * @see ClientTransaction */ class ClientLifecycleManager { - // TODO(lifecycler): Implement building transactions or global transaction. - // TODO(lifecycler): Use object pools for transactions and transaction items. + + private static final String TAG = "ClientLifecycleManager"; + + /** Mapping from client process binder to its pending transaction. */ + @VisibleForTesting + final ArrayMap<IBinder, ClientTransaction> mPendingTransactions = new ArrayMap<>(); + + private WindowManagerService mWms; + + void setWindowManager(@NonNull WindowManagerService wms) { + mWms = wms; + } /** * Schedules a transaction, which may consist of multiple callbacks and a lifecycle request. @@ -82,14 +98,24 @@ class ClientLifecycleManager { */ void scheduleTransactionItem(@NonNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem) throws RemoteException { - // TODO(b/260873529): queue the transaction items. - final ClientTransaction clientTransaction = ClientTransaction.obtain(client); - if (transactionItem.isActivityLifecycleItem()) { - clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem); + // The behavior is different depending on the flag. + // When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to + // dispatch all pending transactions at once. + if (Flags.bundleClientTransactionFlag()) { + final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client); + clientTransaction.addTransactionItem(transactionItem); + + onClientTransactionItemScheduledLocked(clientTransaction); } else { - clientTransaction.addCallback(transactionItem); + // TODO(b/260873529): cleanup after launch. + final ClientTransaction clientTransaction = ClientTransaction.obtain(client); + if (transactionItem.isActivityLifecycleItem()) { + clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem); + } else { + clientTransaction.addCallback(transactionItem); + } + scheduleTransaction(clientTransaction); } - scheduleTransaction(clientTransaction); } /** @@ -100,10 +126,67 @@ class ClientLifecycleManager { void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem, @NonNull ActivityLifecycleItem lifecycleItem) throws RemoteException { - // TODO(b/260873529): replace with #scheduleTransactionItem after launch for cleanup. - final ClientTransaction clientTransaction = ClientTransaction.obtain(client); - clientTransaction.addCallback(transactionItem); - clientTransaction.setLifecycleStateRequest(lifecycleItem); + // The behavior is different depending on the flag. + // When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to + // dispatch all pending transactions at once. + if (Flags.bundleClientTransactionFlag()) { + final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client); + clientTransaction.addTransactionItem(transactionItem); + clientTransaction.addTransactionItem(lifecycleItem); + + onClientTransactionItemScheduledLocked(clientTransaction); + } else { + // TODO(b/260873529): cleanup after launch. + final ClientTransaction clientTransaction = ClientTransaction.obtain(client); + clientTransaction.addCallback(transactionItem); + clientTransaction.setLifecycleStateRequest(lifecycleItem); + scheduleTransaction(clientTransaction); + } + } + + /** Executes all the pending transactions. */ + void dispatchPendingTransactions() { + final int size = mPendingTransactions.size(); + for (int i = 0; i < size; i++) { + final ClientTransaction transaction = mPendingTransactions.valueAt(i); + try { + scheduleTransaction(transaction); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to deliver transaction for " + transaction.getClient()); + } + } + mPendingTransactions.clear(); + } + + @NonNull + private ClientTransaction getOrCreatePendingTransaction(@NonNull IApplicationThread client) { + final IBinder clientBinder = client.asBinder(); + final ClientTransaction pendingTransaction = mPendingTransactions.get(clientBinder); + if (pendingTransaction != null) { + return pendingTransaction; + } + + // Create new transaction if there is no existing. + final ClientTransaction transaction = ClientTransaction.obtain(client); + mPendingTransactions.put(clientBinder, transaction); + return transaction; + } + + /** Must only be called with WM lock. */ + private void onClientTransactionItemScheduledLocked( + @NonNull ClientTransaction clientTransaction) throws RemoteException { + // TODO(b/260873529): make sure WindowSurfacePlacer#requestTraversal is called before + // ClientTransaction scheduled when needed. + + if (mWms != null && (mWms.mWindowPlacerLocked.isInLayout() + || mWms.mWindowPlacerLocked.isTraversalScheduled())) { + // The pending transactions will be dispatched when + // RootWindowContainer#performSurfacePlacementNoTrace. + return; + } + + // Dispatch the pending transaction immediately. + mPendingTransactions.remove(clientTransaction.getClient().asBinder()); scheduleTransaction(clientTransaction); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 03d6c2cab828..ae10ce3690aa 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5149,8 +5149,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** @return the orientation of the display when it's rotation is ROTATION_0. */ int getNaturalOrientation() { - return mBaseDisplayWidth < mBaseDisplayHeight - ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; + final Configuration config = getConfiguration(); + if (config.windowConfiguration.getDisplayRotation() == ROTATION_0) { + return config.orientation; + } + final Rect frame = mDisplayPolicy.getDecorInsetsInfo( + ROTATION_0, mBaseDisplayWidth, mBaseDisplayHeight).mConfigFrame; + return frame.width() <= frame.height() ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; } void performLayout(boolean initial, boolean updateInputWindows) { diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index 4a3d0c142e1d..32d60c5f52e6 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static com.android.input.flags.Flags.enablePointerChoreographer; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -23,6 +24,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.content.ClipData; import android.content.Context; +import android.hardware.input.InputManagerGlobal; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -31,6 +33,8 @@ import android.os.Message; import android.util.Slog; import android.view.Display; import android.view.IWindow; +import android.view.InputDevice; +import android.view.PointerIcon; import android.view.SurfaceControl; import android.view.View; import android.view.accessibility.AccessibilityManager; @@ -97,8 +101,8 @@ class DragDropController { } IBinder performDrag(int callerPid, int callerUid, IWindow window, int flags, - SurfaceControl surface, int touchSource, float touchX, float touchY, - float thumbCenterX, float thumbCenterY, ClipData data) { + SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId, + float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) { if (DEBUG_DRAG) { Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" + Integer.toHexString(flags) + " data=" + data + " touch(" + touchX + "," @@ -208,7 +212,17 @@ class DragDropController { final SurfaceControl surfaceControl = mDragState.mSurfaceControl; mDragState.broadcastDragStartedLocked(touchX, touchY); - mDragState.overridePointerIconLocked(touchSource); + if (enablePointerChoreographer()) { + if ((touchSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) { + InputManagerGlobal.getInstance().setPointerIcon( + PointerIcon.getSystemIcon( + mService.mContext, PointerIcon.TYPE_GRABBING), + mDragState.mDisplayContent.getDisplayId(), touchDeviceId, + touchPointerId, mDragState.getInputToken()); + } + } else { + mDragState.overridePointerIconLocked(touchSource); + } // remember the thumb offsets for later mDragState.mThumbOffsetX = thumbCenterX; mDragState.mThumbOffsetY = thumbCenterY; diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index a888f8467b3a..adbe3bc1d6b3 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -416,6 +416,13 @@ class DragState { return mInputInterceptor == null ? null : mInputInterceptor.mDragWindowHandle; } + IBinder getInputToken() { + if (mInputInterceptor == null || mInputInterceptor.mClientChannel == null) { + return null; + } + return mInputInterceptor.mClientChannel.getToken(); + } + /** * @param display The Display that the window being dragged is on. */ diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java index 9cbc1bdcbeeb..4c73a415d3f3 100644 --- a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java +++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java @@ -84,10 +84,10 @@ class LaunchObserverRegistryImpl extends ActivityMetricsLaunchObserver implement } @Override - public void onActivityLaunched(long id, ComponentName name, int temperature) { + public void onActivityLaunched(long id, ComponentName name, int temperature, int userId) { mHandler.sendMessage(PooledLambda.obtainMessage( LaunchObserverRegistryImpl::handleOnActivityLaunched, - this, id, name, temperature)); + this, id, name, temperature, userId)); } @Override @@ -97,10 +97,11 @@ class LaunchObserverRegistryImpl extends ActivityMetricsLaunchObserver implement } @Override - public void onActivityLaunchFinished(long id, ComponentName name, long timestampNs) { + public void onActivityLaunchFinished(long id, ComponentName name, long timestampNs, + int launchMode) { mHandler.sendMessage(PooledLambda.obtainMessage( LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, - this, id, name, timestampNs)); + this, id, name, timestampNs, launchMode)); } @Override @@ -137,10 +138,10 @@ class LaunchObserverRegistryImpl extends ActivityMetricsLaunchObserver implement } private void handleOnActivityLaunched(long id, ComponentName name, - @Temperature int temperature) { + @Temperature int temperature, int userId) { // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. for (int i = 0; i < mList.size(); i++) { - mList.get(i).onActivityLaunched(id, name, temperature); + mList.get(i).onActivityLaunched(id, name, temperature, userId); } } @@ -151,10 +152,11 @@ class LaunchObserverRegistryImpl extends ActivityMetricsLaunchObserver implement } } - private void handleOnActivityLaunchFinished(long id, ComponentName name, long timestampNs) { + private void handleOnActivityLaunchFinished(long id, ComponentName name, long timestampNs, + int launchMode) { // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. for (int i = 0; i < mList.size(); i++) { - mList.get(i).onActivityLaunchFinished(id, name, timestampNs); + mList.get(i).onActivityLaunchFinished(id, name, timestampNs, launchMode); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index d5aa2760d41a..9a75dae3569e 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -842,6 +842,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> handleResizingWindows(); clearFrameChangingWindows(); + // Called after #handleResizingWindows to include WindowStateResizeItem if any. + mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions(); + if (mWmService.mDisplayFrozen) { ProtoLog.v(WM_DEBUG_ORIENTATION, "With display frozen, orientationChangeComplete=%b", diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 56f9aa4c6361..57939bc4f348 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -340,7 +340,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public IBinder performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource, - float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) { + int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX, + float thumbCenterY, ClipData data) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); // Validate and resolve ClipDescription data before clearing the calling identity @@ -349,7 +350,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final long ident = Binder.clearCallingIdentity(); try { return mDragDropController.performDrag(mPid, mUid, window, flags, surface, touchSource, - touchX, touchY, thumbCenterX, thumbCenterY, data); + touchDeviceId, touchPointerId, touchX, touchY, thumbCenterX, thumbCenterY, + data); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index fc92755c6550..5d019122d52e 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -73,6 +73,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityOptions; +import android.app.IApplicationThread; import android.app.ResultInfo; import android.app.WindowConfiguration; import android.app.servertransaction.ActivityResultItem; @@ -103,6 +104,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.server.am.HostingRecord; import com.android.server.pm.pkg.AndroidPackage; +import com.android.window.flags.Flags; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -1509,23 +1511,38 @@ class TaskFragment extends WindowContainer<WindowContainer> { } try { - final ClientTransaction transaction = ClientTransaction.obtain( - next.app.getThread()); + final IApplicationThread appThread = next.app.getThread(); + final ClientTransaction transaction = Flags.bundleClientTransactionFlag() + ? null + : ClientTransaction.obtain(appThread); // Deliver all pending results. - ArrayList<ResultInfo> a = next.results; + final ArrayList<ResultInfo> a = next.results; if (a != null) { final int size = a.size(); if (!next.finishing && size > 0) { if (DEBUG_RESULTS) { Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a); } - transaction.addCallback(ActivityResultItem.obtain(next.token, a)); + final ActivityResultItem activityResultItem = ActivityResultItem.obtain( + next.token, a); + if (transaction == null) { + mAtmService.getLifecycleManager().scheduleTransactionItem( + appThread, activityResultItem); + } else { + transaction.addCallback(activityResultItem); + } } } if (next.newIntents != null) { - transaction.addCallback( - NewIntentItem.obtain(next.token, next.newIntents, true /* resume */)); + final NewIntentItem newIntentItem = NewIntentItem.obtain( + next.token, next.newIntents, true /* resume */); + if (transaction == null) { + mAtmService.getLifecycleManager().scheduleTransactionItem( + appThread, newIntentItem); + } else { + transaction.addCallback(newIntentItem); + } } // Well the app will no longer be stopped. @@ -1539,10 +1556,16 @@ class TaskFragment extends WindowContainer<WindowContainer> { final int topProcessState = mAtmService.mTopProcessState; next.app.setPendingUiCleanAndForceProcessStateUpTo(topProcessState); next.abortAndClearOptionsAnimation(); - transaction.setLifecycleStateRequest( - ResumeActivityItem.obtain(next.token, topProcessState, - dc.isNextTransitionForward(), next.shouldSendCompatFakeFocus())); - mAtmService.getLifecycleManager().scheduleTransaction(transaction); + final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain( + next.token, topProcessState, dc.isNextTransitionForward(), + next.shouldSendCompatFakeFocus()); + if (transaction == null) { + mAtmService.getLifecycleManager().scheduleTransactionItem( + appThread, resumeActivityItem); + } else { + transaction.setLifecycleStateRequest(resumeActivityItem); + mAtmService.getLifecycleManager().scheduleTransaction(transaction); + } ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next); } catch (Exception e) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 808a11d4adae..516d37c0136a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -866,6 +866,11 @@ public abstract class WindowManagerInternal { public abstract ImeTargetInfo onToggleImeRequested(boolean show, @NonNull IBinder focusedToken, @NonNull IBinder requestToken, int displayId); + /** + * Returns the token to identify the target window that the IME is associated with. + */ + public abstract @Nullable IBinder getTargetWindowTokenFromInputToken(IBinder inputToken); + /** The information of input method target when IME is requested to show or hide. */ public static class ImeTargetInfo { public final String focusedWindowName; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 10dd334ed50c..2125c63eed34 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8552,6 +8552,12 @@ public class WindowManagerService extends IWindowManager.Stub fromOrientations, toOrientations); } } + + @Override + public @Nullable IBinder getTargetWindowTokenFromInputToken(IBinder inputToken) { + InputTarget inputTarget = WindowManagerService.this.getInputTargetFromToken(inputToken); + return inputTarget == null ? null : inputTarget.getWindowToken(); + } } private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index a872fd0baaae..4b99432b2943 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -63,6 +63,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; +import static com.android.server.wm.ActivityRecord.State.PAUSING; import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; @@ -1178,6 +1179,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub mService.mRootWindowContainer.moveActivityToPinnedRootTask( pipActivity, null /* launchIntoPipHostActivity */, "moveActivityToPinnedRootTask", null /* transition */, entryBounds); + + // Continue the pausing process after potential task reparenting. + if (pipActivity.isState(PAUSING) && pipActivity.mPauseSchedulePendingForPip) { + pipActivity.getTask().schedulePauseActivity( + pipActivity, false /* userLeaving */, + false /* pauseImmediately */, true /* autoEnteringPip */, "auto-pip"); + } + effects |= TRANSACT_EFFECTS_LIFECYCLE; break; } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index fd5f7a3f5cfa..5721750fbf63 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -66,6 +66,7 @@ import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.os.Binder; import android.os.Build; +import android.os.DeadObjectException; import android.os.FactoryTest; import android.os.LocaleList; import android.os.Message; @@ -1675,6 +1676,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio @NonNull ClientTransactionItem transactionItem) { try { mAtm.getLifecycleManager().scheduleTransactionItem(thread, transactionItem); + } catch (DeadObjectException e) { + // Expected if the process has been killed. + Slog.w(TAG_CONFIGURATION, "Failed for dead process. ClientTransactionItem=" + + transactionItem + " owner=" + mOwner); } catch (Exception e) { Slog.e(TAG_CONFIGURATION, "Failed to schedule ClientTransactionItem=" + transactionItem + " owner=" + mOwner, e); diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index aa589024fdc6..dff718a4b7d5 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -210,6 +210,10 @@ class WindowSurfacePlacer { return mInLayout; } + boolean isTraversalScheduled() { + return mTraversalScheduled; + } + void requestTraversal() { if (mTraversalScheduled) { return; diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index 3d4f866948af..2a0f1e2ede55 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -182,7 +182,7 @@ static inline IncFsSize verityTreeSizeForFile(IncFsSize fileSize) { auto block_count = 1 + (fileSize - 1) / INCFS_DATA_FILE_BLOCK_SIZE; auto hash_block_count = block_count; - for (auto i = 0; hash_block_count > 1; i++) { + while (hash_block_count > 1) { hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block; total_tree_block_count += hash_block_count; } @@ -468,7 +468,6 @@ private: borrowed_fd incomingFd, bool waitOnEof, std::vector<char>* buffer, std::vector<IncFsDataBlock>* blocks) { IncFsSize remaining = size; - IncFsSize totalSize = 0; IncFsBlockIndex blockIdx = 0; while (remaining > 0) { constexpr auto capacity = BUFFER_SIZE; @@ -502,7 +501,6 @@ private: buffer->resize(size + read); remaining -= read; - totalSize += read; } if (!buffer->empty() && !flashToIncFs(incfsFd, kind, true, &blockIdx, buffer, blocks)) { return false; diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig index 47c2a1b079f8..29e258cc90ff 100644 --- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig +++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig @@ -5,4 +5,12 @@ flag { namespace: "display_manager" description: "Feature flag for dual display blocking" bug: "278667199" +} + +flag { + name: "enable_foldables_posture_based_closed_state" + namespace: "windowing_frontend" + description: "Enables smarter closed device state state for foldable devices" + bug: "309792734" + is_fixed_read_only: true }
\ No newline at end of file diff --git a/services/manifest_services.xml b/services/manifest_services.xml index 76389154a885..eae159fe9e89 100644 --- a/services/manifest_services.xml +++ b/services/manifest_services.xml @@ -4,4 +4,9 @@ <version>1</version> <fqname>IAltitudeService/default</fqname> </hal> + <hal format="aidl"> + <name>android.frameworks.vibrator</name> + <version>1</version> + <fqname>IVibratorControlService/default</fqname> + </hal> </manifest> diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt index 02032c786563..f69f6283f968 100644 --- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt @@ -24,6 +24,7 @@ import android.content.pm.SigningDetails import android.os.Build import android.util.Slog import com.android.internal.os.RoSystemProperties +import com.android.internal.pm.permission.CompatibilityPermissionInfo import com.android.modules.utils.BinaryXmlPullParser import com.android.modules.utils.BinaryXmlSerializer import com.android.server.permission.access.AccessState @@ -42,7 +43,6 @@ import com.android.server.permission.access.util.hasBits import com.android.server.permission.access.util.isInternal import com.android.server.pm.KnownPackages import com.android.server.pm.parsing.PackageInfoUtils -import com.android.server.pm.permission.CompatibilityPermissionInfo import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.pkg.PackageState diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java index d62da1a02525..5b222c01d56b 100644 --- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -57,11 +57,11 @@ import android.platform.test.annotations.Presubmit; import androidx.test.core.app.ApplicationProvider; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.util.FunctionalUtils.ThrowingSupplier; import com.android.server.LocalServices; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowUserManager; diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt index 3011fa17d4fc..5c4716dc751e 100644 --- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt +++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt @@ -25,6 +25,7 @@ import android.os.Binder import android.os.UserHandle import android.util.ArrayMap import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.parsing.pkg.PackageImpl import com.android.internal.pm.parsing.pkg.ParsedPackage import com.android.internal.pm.pkg.component.ParsedActivity import com.android.server.pm.AppsFilterImpl @@ -38,7 +39,6 @@ import com.android.server.pm.Settings import com.android.server.pm.SharedLibrariesImpl import com.android.server.pm.UserManagerInternal import com.android.server.pm.UserManagerService -import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.resolution.ComponentResolver import com.android.server.pm.snapshot.PackageDataSnapshot @@ -49,6 +49,8 @@ import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import com.android.server.wm.ActivityTaskManagerInternal import com.google.common.truth.Truth.assertThat +import java.io.File +import java.util.UUID import org.junit.After import org.junit.Before import org.junit.BeforeClass @@ -61,8 +63,6 @@ import org.mockito.Mockito.doReturn import org.mockito.Mockito.intThat import org.mockito.Mockito.same import org.testng.Assert.assertThrows -import java.io.File -import java.util.UUID @RunWith(Parameterized::class) class PackageManagerComponentLabelIconOverrideTest { diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java index 3461bb6b2c55..7277fd79fdd5 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java @@ -49,20 +49,20 @@ import android.util.SparseArray; import androidx.annotation.NonNull; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.component.ParsedActivity; +import com.android.internal.pm.pkg.component.ParsedActivityImpl; +import com.android.internal.pm.pkg.component.ParsedComponentImpl; +import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl; +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl; import com.android.internal.pm.pkg.component.ParsedPermission; +import com.android.internal.pm.pkg.component.ParsedPermissionImpl; +import com.android.internal.pm.pkg.component.ParsedProviderImpl; +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.server.om.OverlayReferenceMapper; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.component.ParsedActivityImpl; -import com.android.server.pm.pkg.component.ParsedComponentImpl; -import com.android.server.pm.pkg.component.ParsedInstrumentationImpl; -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl; -import com.android.server.pm.pkg.component.ParsedPermissionImpl; -import com.android.server.pm.pkg.component.ParsedProviderImpl; -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.server.utils.WatchableTester; import org.junit.Before; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java index f0d389be7cb6..b1c3e94c2138 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/CompatibilityModeTest.java @@ -32,11 +32,11 @@ import android.content.pm.ApplicationInfo; import android.os.Build; import android.platform.test.annotations.Presubmit; +import com.android.internal.pm.parsing.pkg.PackageImpl; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.parsing.PackageInfoUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.PackageStateUnserialized; import com.android.server.pm.pkg.PackageUserStateImpl; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import org.junit.After; import org.junit.Before; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java index f07e820bf8b3..b396cf498a67 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -63,10 +63,10 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.permission.persistence.RuntimePermissionsPersistence; import com.android.server.LocalServices; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.permission.LegacyPermissionDataProvider; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.ArchiveState; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java index 9c48af8ecd01..285c059e973a 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerTests.java @@ -73,7 +73,7 @@ import androidx.test.filters.Suppress; import com.android.compatibility.common.util.CddTest; import com.android.internal.content.InstallLocationUtils; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; +import com.android.server.pm.parsing.ParsingUtils; import com.android.server.pm.test.service.server.R; import dalvik.system.VMRuntime; @@ -346,7 +346,7 @@ public class PackageManagerTests extends AndroidTestCase { private ParsedPackage parsePackage(Uri packageURI) { final String archiveFilePath = packageURI.getPath(); - ParseResult<ParsedPackage> result = ParsingPackageUtils.parseDefaultOneTime( + ParseResult<ParsedPackage> result = ParsingUtils.parseDefaultOneTime( new File(archiveFilePath), 0 /*flags*/, Collections.emptyList(), false /*collectCertificates*/); if (result.isError()) { diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java index a62cd4fc5fc2..03e45a27390f 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageParserTest.java @@ -15,7 +15,7 @@ */ package com.android.server.pm; -import static com.android.server.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS; +import static com.android.internal.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -58,17 +58,28 @@ import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.permission.CompatibilityPermissionInfo; import com.android.internal.pm.pkg.component.ParsedActivity; +import com.android.internal.pm.pkg.component.ParsedActivityImpl; import com.android.internal.pm.pkg.component.ParsedApexSystemService; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedInstrumentation; +import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl; import com.android.internal.pm.pkg.component.ParsedIntentInfo; +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl; import com.android.internal.pm.pkg.component.ParsedPermission; import com.android.internal.pm.pkg.component.ParsedPermissionGroup; +import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl; +import com.android.internal.pm.pkg.component.ParsedPermissionImpl; +import com.android.internal.pm.pkg.component.ParsedPermissionUtils; import com.android.internal.pm.pkg.component.ParsedProvider; +import com.android.internal.pm.pkg.component.ParsedProviderImpl; import com.android.internal.pm.pkg.component.ParsedService; +import com.android.internal.pm.pkg.component.ParsedServiceImpl; import com.android.internal.pm.pkg.component.ParsedUsesPermission; +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.internal.util.ArrayUtils; import com.android.server.pm.parsing.PackageCacher; @@ -76,19 +87,8 @@ import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.TestPackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; -import com.android.server.pm.permission.CompatibilityPermissionInfo; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageUserStateInternal; -import com.android.server.pm.pkg.component.ParsedActivityImpl; -import com.android.server.pm.pkg.component.ParsedInstrumentationImpl; -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl; -import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl; -import com.android.server.pm.pkg.component.ParsedPermissionImpl; -import com.android.server.pm.pkg.component.ParsedPermissionUtils; -import com.android.server.pm.pkg.component.ParsedProviderImpl; -import com.android.server.pm.pkg.component.ParsedServiceImpl; -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl; import org.junit.Before; import org.junit.Rule; @@ -725,6 +725,16 @@ public class PackageParserTest { public boolean hasFeature(String feature) { return false; } + + @Override + public Set<String> getHiddenApiWhitelistedApps() { + return new ArraySet<>(); + } + + @Override + public Set<String> getInstallConstraintsAllowlist() { + return new ArraySet<>(); + } }); if (cacheDir != null) { setCacheDir(cacheDir); diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java index 6202908cdfbf..c1271bb0ee36 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java @@ -50,13 +50,13 @@ import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.util.Pair; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.server.compat.PlatformCompat; import com.android.server.pm.parsing.PackageInfoUtils; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import org.hamcrest.BaseMatcher; @@ -376,7 +376,7 @@ public class ScanTests { // Create the ParsedPackage for the apex final ParsedPackage basicPackage = ((ParsedPackage) new PackageImpl(DUMMY_PACKAGE_NAME, codePath, codePath, - mock(TypedArray.class), false) + mock(TypedArray.class), false, null) .setVolumeUuid(UUID_ONE.toString()) .hideAsParsed()) .setVersionCodeMajor(1) @@ -595,7 +595,7 @@ public class ScanTests { // TODO(b/135203078): Make this use PackageImpl.forParsing and separate the steps return (ParsingPackage) ((ParsedPackage) new PackageImpl(packageName, "/data/tmp/randompath/base.apk", createCodePath(packageName), - mock(TypedArray.class), false) + mock(TypedArray.class), false, null) .setVolumeUuid(UUID_ONE.toString()) .addUsesStaticLibrary("some.static.library", 234L, new String[]{"testCert1"}) .addUsesStaticLibrary("some.other.static.library", 456L, new String[]{"testCert2"}) diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java index b102ab4f7e3b..b63950c10a18 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java @@ -39,14 +39,14 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.component.ParsedActivityUtils; import com.android.internal.pm.pkg.component.ParsedComponent; import com.android.internal.pm.pkg.component.ParsedIntentInfo; import com.android.internal.pm.pkg.component.ParsedPermission; +import com.android.internal.pm.pkg.component.ParsedPermissionUtils; import com.android.internal.util.ArrayUtils; import com.android.server.pm.PackageManagerException; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.component.ParsedActivityUtils; -import com.android.server.pm.pkg.component.ParsedPermissionUtils; import com.android.server.pm.test.service.server.R; import com.google.common.truth.Expect; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt index 67b91d2646d9..c435b94fcacd 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/PackageParsingDeferErrorTest.kt @@ -22,7 +22,6 @@ import android.content.pm.parsing.result.ParseResult import android.platform.test.annotations.Presubmit import androidx.test.InstrumentationRegistry import com.android.internal.pm.parsing.pkg.ParsedPackage -import com.android.server.pm.pkg.parsing.ParsingPackageUtils import com.android.server.pm.test.service.server.R import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage @@ -121,7 +120,7 @@ class PackageParsingDeferErrorTest { input.copyTo(output) } } - return ParsingPackageUtils.parseDefaultOneTime(file, 0 /*flags*/, emptyList(), + return ParsingUtils.parseDefaultOneTime(file, 0 /*flags*/, emptyList(), false /*collectCertificates*/) } } diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/ParsingUtils.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/ParsingUtils.java new file mode 100644 index 000000000000..a9eac95dfd87 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/ParsingUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.parsing; + +import android.annotation.NonNull; +import android.content.pm.parsing.result.ParseInput; +import android.content.pm.parsing.result.ParseResult; +import android.content.pm.parsing.result.ParseTypeImpl; +import android.content.res.TypedArray; +import android.permission.PermissionManager; + +import com.android.internal.pm.parsing.pkg.PackageImpl; +import com.android.internal.pm.parsing.pkg.ParsedPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackage; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; +import com.android.server.SystemConfig; + +import java.io.File; +import java.util.List; +import java.util.Set; + +/** @hide **/ +public class ParsingUtils { + + /** + * @see ParsingPackageUtils#parseDefault(ParseInput, File, int, List, boolean, + * ParsingPackageUtils.Callback) + */ + @NonNull + public static ParseResult<ParsedPackage> parseDefaultOneTime(File file, + @ParsingPackageUtils.ParseFlags int parseFlags, + @NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions, + boolean collectCertificates) { + ParseInput input = ParseTypeImpl.forDefaultParsing().reset(); + return ParsingPackageUtils.parseDefault(input, file, parseFlags, splitPermissions, collectCertificates, + new ParsingPackageUtils.Callback() { + @Override + public boolean hasFeature(String feature) { + // Assume the device doesn't support anything. This will affect permission + // parsing and will force <uses-permission/> declarations to include all + // requiredNotFeature permissions and exclude all requiredFeature + // permissions. This mirrors the old behavior. + return false; + } + + @Override + public ParsingPackage startParsingPackage( + @NonNull String packageName, + @NonNull String baseApkPath, + @NonNull String path, + @NonNull TypedArray manifestArray, boolean isCoreApp) { + return PackageImpl.forParsing(packageName, baseApkPath, path, manifestArray, + isCoreApp, this); + } + + @Override + public Set<String> getHiddenApiWhitelistedApps() { + return SystemConfig.getInstance().getHiddenApiWhitelistedApps(); + } + + @Override + public Set<String> getInstallConstraintsAllowlist() { + return SystemConfig.getInstance().getInstallConstraintsAllowlist(); + } + }); + } +} diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt index 1f57b6c9f95f..98af63c65e90 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt @@ -17,15 +17,15 @@ package com.android.server.pm.parsing import android.content.pm.PackageManager -import com.android.server.pm.pkg.parsing.ParsingPackageUtils import android.platform.test.annotations.Postsubmit +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils import com.android.server.pm.PackageManagerException import com.android.server.pm.PackageManagerService import com.android.server.pm.PackageManagerServiceUtils +import java.io.File import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import java.io.File /** * This test parses all the system APKs on the device image to ensure that they succeed. diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java index 6cd71237efa2..9517e49c4a73 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java @@ -24,8 +24,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java index 27fd781584f2..01fad8f57d16 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java index b13d6de55cf6..b1f26c253043 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java @@ -23,8 +23,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java index fa69f844c33a..349763ae18b3 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java @@ -24,9 +24,9 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.parsing.library.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java index 856013a96017..71bdacbce9f7 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java @@ -22,9 +22,9 @@ import android.util.ArrayMap; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.SystemConfig; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Before; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java index ae5ea21aec4e..6aa0c2db4d39 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java @@ -21,8 +21,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java index e126ffcab5ff..44098d0be2b2 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java @@ -23,8 +23,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java index d0b0cf894340..9d5ce8a66395 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java @@ -28,10 +28,10 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.parsing.ParsingPackage; import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Assume; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java index c141c0337540..bffeb7255b31 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java @@ -23,9 +23,9 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java index a58604b81e06..b114cd375050 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java @@ -23,9 +23,9 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.Test; diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt index 09b66c11d200..ef9c62fd3795 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt @@ -30,21 +30,21 @@ import android.util.ArraySet import android.util.SparseArray import android.util.SparseIntArray import com.android.internal.R -import com.android.server.pm.parsing.pkg.AndroidPackageUtils -import com.android.server.pm.parsing.pkg.PackageImpl +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils +import com.android.internal.pm.parsing.pkg.PackageImpl +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedApexSystemServiceImpl +import com.android.internal.pm.pkg.component.ParsedAttributionImpl +import com.android.internal.pm.pkg.component.ParsedComponentImpl +import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl +import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl +import com.android.internal.pm.pkg.component.ParsedPermissionImpl +import com.android.internal.pm.pkg.component.ParsedProcessImpl +import com.android.internal.pm.pkg.component.ParsedProviderImpl +import com.android.internal.pm.pkg.component.ParsedServiceImpl +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl import com.android.server.pm.pkg.AndroidPackage -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl -import com.android.server.pm.pkg.component.ParsedAttributionImpl -import com.android.server.pm.pkg.component.ParsedComponentImpl -import com.android.server.pm.pkg.component.ParsedInstrumentationImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl -import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl -import com.android.server.pm.pkg.component.ParsedPermissionImpl -import com.android.server.pm.pkg.component.ParsedProcessImpl -import com.android.server.pm.pkg.component.ParsedProviderImpl -import com.android.server.pm.pkg.component.ParsedServiceImpl -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import java.security.KeyPairGenerator @@ -534,34 +534,34 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag } ), getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT")), - getSetByValue({ AndroidPackageUtils.isOdm(it) }, "isOdm", PackageImpl::setOdm, true), - getSetByValue({ AndroidPackageUtils.isOem(it) }, "isOem", PackageImpl::setOem, true), + getSetByValue({ AndroidPackageLegacyUtils.isOdm(it) }, "isOdm", PackageImpl::setOdm, true), + getSetByValue({ AndroidPackageLegacyUtils.isOem(it) }, "isOem", PackageImpl::setOem, true), getSetByValue( - { AndroidPackageUtils.isPrivileged(it) }, + { AndroidPackageLegacyUtils.isPrivileged(it) }, "isPrivileged", PackageImpl::setPrivileged, true ), getSetByValue( - { AndroidPackageUtils.isProduct(it) }, + { AndroidPackageLegacyUtils.isProduct(it) }, "isProduct", PackageImpl::setProduct, true ), getSetByValue( - { AndroidPackageUtils.isVendor(it) }, + { AndroidPackageLegacyUtils.isVendor(it) }, "isVendor", PackageImpl::setVendor, true ), getSetByValue( - { AndroidPackageUtils.isSystem(it) }, + { AndroidPackageLegacyUtils.isSystem(it) }, "isSystem", PackageImpl::setSystem, true ), getSetByValue( - { AndroidPackageUtils.isSystemExt(it) }, + { AndroidPackageLegacyUtils.isSystemExt(it) }, "isSystemExt", PackageImpl::setSystemExt, true @@ -593,7 +593,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag ) ) { "" } }, - true + true, null ) .asSplit( arrayOf("testSplitNameZero", "testSplitNameOne"), diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt index 26468544e8d6..2c8b1cd884f1 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt @@ -18,7 +18,7 @@ package com.android.server.pm.test.parsing.parcelling import android.content.pm.ActivityInfo import com.android.internal.pm.pkg.component.ParsedActivity -import com.android.server.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedActivityImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt index 52d5b3bccb72..ad5374672d22 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedAttribution -import com.android.server.pm.pkg.component.ParsedAttributionImpl +import com.android.internal.pm.pkg.component.ParsedAttributionImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt index af0c0de2db15..3ac48536b39c 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt @@ -18,8 +18,8 @@ package com.android.server.pm.test.parsing.parcelling import android.content.pm.PackageManager import com.android.internal.pm.pkg.component.ParsedComponent -import com.android.server.pm.pkg.component.ParsedComponentImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl +import com.android.internal.pm.pkg.component.ParsedComponentImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import android.os.Bundle import android.os.Parcelable import kotlin.contracts.ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt index dc0f194f10cc..2bd4f617dcb5 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedInstrumentation -import com.android.server.pm.pkg.component.ParsedInstrumentationImpl +import com.android.internal.pm.pkg.component.ParsedInstrumentationImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt index 5224f23d38d1..af385e202a9e 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedIntentInfo -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import android.os.Parcelable import android.os.PatternMatcher import kotlin.contracts.ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt index dfff6025e2eb..061e39ddf6df 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedMainComponent -import com.android.server.pm.pkg.component.ParsedMainComponentImpl +import com.android.internal.pm.pkg.component.ParsedMainComponentImpl import android.os.Parcelable import java.util.Arrays import kotlin.contracts.ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt index ccbf558734d3..3a6418824dfa 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedPermissionGroup -import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl +import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt index 2814783b6849..551f16d2a3bb 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt @@ -18,8 +18,8 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedPermission import com.android.internal.pm.pkg.component.ParsedPermissionGroup -import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl -import com.android.server.pm.pkg.component.ParsedPermissionImpl +import com.android.internal.pm.pkg.component.ParsedPermissionGroupImpl +import com.android.internal.pm.pkg.component.ParsedPermissionImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt index 2e9604696acb..93bdeaeb90e3 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt @@ -16,9 +16,9 @@ package com.android.server.pm.test.parsing.parcelling -import com.android.internal.pm.pkg.component.ParsedProcess -import com.android.server.pm.pkg.component.ParsedProcessImpl import android.util.ArrayMap +import com.android.internal.pm.pkg.component.ParsedProcess +import com.android.internal.pm.pkg.component.ParsedProcessImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt index 290dbd6277b6..1e844705fe3c 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt @@ -17,9 +17,9 @@ package com.android.server.pm.test.parsing.parcelling import android.content.pm.PathPermission -import com.android.internal.pm.pkg.component.ParsedProvider -import com.android.server.pm.pkg.component.ParsedProviderImpl import android.os.PatternMatcher +import com.android.internal.pm.pkg.component.ParsedProvider +import com.android.internal.pm.pkg.component.ParsedProviderImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt index 3ae7e9220cc6..79d5a4f7030a 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedService -import com.android.server.pm.pkg.component.ParsedServiceImpl +import com.android.internal.pm.pkg.component.ParsedServiceImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt index 67dfc6d4f7ef..d0ad09be982b 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt @@ -17,7 +17,7 @@ package com.android.server.pm.test.parsing.parcelling import com.android.internal.pm.pkg.component.ParsedUsesPermission -import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl +import com.android.internal.pm.pkg.component.ParsedUsesPermissionImpl import kotlin.contracts.ExperimentalContracts @ExperimentalContracts diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt index 1da3a2234ffc..b21c34905bad 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt @@ -17,37 +17,34 @@ package com.android.server.pm.test.pkg import android.content.Intent -import android.content.pm.overlay.OverlayPaths import android.content.pm.PackageManager import android.content.pm.PathPermission import android.content.pm.SharedLibraryInfo import android.content.pm.VersionedPackage +import android.content.pm.overlay.OverlayPaths import android.os.PatternMatcher import android.util.ArraySet +import com.android.internal.pm.parsing.pkg.PackageImpl import com.android.internal.pm.pkg.component.ParsedActivity +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedComponentImpl import com.android.internal.pm.pkg.component.ParsedInstrumentation +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.internal.pm.pkg.component.ParsedPermission import com.android.internal.pm.pkg.component.ParsedPermissionGroup +import com.android.internal.pm.pkg.component.ParsedPermissionImpl import com.android.internal.pm.pkg.component.ParsedProcess +import com.android.internal.pm.pkg.component.ParsedProcessImpl import com.android.internal.pm.pkg.component.ParsedProvider +import com.android.internal.pm.pkg.component.ParsedProviderImpl import com.android.internal.pm.pkg.component.ParsedService import com.android.server.pm.PackageSetting import com.android.server.pm.PackageSettingBuilder -import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.pkg.AndroidPackage import com.android.server.pm.pkg.PackageState import com.android.server.pm.pkg.PackageUserState -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedComponentImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl -import com.android.server.pm.pkg.component.ParsedPermissionImpl -import com.android.server.pm.pkg.component.ParsedProcessImpl -import com.android.server.pm.pkg.component.ParsedProviderImpl import com.android.server.pm.test.parsing.parcelling.AndroidPackageTest import com.google.common.truth.Expect -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TemporaryFolder import kotlin.contracts.ExperimentalContracts import kotlin.reflect.KClass import kotlin.reflect.KFunction @@ -55,6 +52,9 @@ import kotlin.reflect.KType import kotlin.reflect.full.isSubtypeOf import kotlin.reflect.full.memberFunctions import kotlin.reflect.full.starProjectedType +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder class PackageStateTest { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt index 9341e9d96335..5e73d198f528 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCollectorTest.kt @@ -22,11 +22,11 @@ import android.os.Build import android.os.PatternMatcher import android.util.ArraySet import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.SystemConfig import com.android.server.compat.PlatformCompat import com.android.server.pm.pkg.AndroidPackage -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationCollector import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt index a737b9097b53..d307608e0be2 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt @@ -29,17 +29,22 @@ import android.util.IndentingPrintWriter import android.util.SparseArray import androidx.test.platform.app.InstrumentationRegistry import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.Computer import com.android.server.pm.pkg.PackageStateInternal import com.android.server.pm.pkg.PackageUserStateInternal -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationEnforcer import com.android.server.pm.verify.domain.DomainVerificationManagerInternal import com.android.server.pm.verify.domain.DomainVerificationService import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever +import java.util.UUID +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger +import kotlin.test.assertFailsWith +import kotlin.test.fail import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -51,11 +56,6 @@ import org.mockito.Mockito.doReturn import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.verifyNoMoreInteractions -import java.util.UUID -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.atomic.AtomicInteger -import kotlin.test.assertFailsWith -import kotlin.test.fail @RunWith(Parameterized::class) class DomainVerificationEnforcerTest { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt index f38df22af630..5edf30a33def 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt @@ -30,24 +30,24 @@ import android.os.Process import android.util.ArraySet import android.util.SparseArray import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.pkg.PackageStateInternal import com.android.server.pm.pkg.PackageUserStateInternal -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationManagerStub import com.android.server.pm.verify.domain.DomainVerificationService import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat +import java.util.UUID +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.test.assertFailsWith import org.junit.Test import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong import org.mockito.ArgumentMatchers.anyString import org.mockito.Mockito.doReturn -import java.util.UUID -import java.util.concurrent.atomic.AtomicBoolean -import kotlin.test.assertFailsWith class DomainVerificationManagerApiTest { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt index 874e0d2bbc9a..85f012534113 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt @@ -37,27 +37,27 @@ import android.util.ArraySet import android.util.SparseArray import android.util.Xml import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.Computer import com.android.server.pm.pkg.PackageStateInternal import com.android.server.pm.pkg.PackageUserStateInternal -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationService import com.android.server.testutils.mock import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.spy import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.security.PublicKey +import java.util.UUID import org.junit.Test import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong import org.mockito.ArgumentMatchers.anyString import org.mockito.Mockito.doReturn -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.security.PublicKey -import java.util.UUID class DomainVerificationPackageTest { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt index 3207e6c2a411..a5c4f6cc0289 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt @@ -25,15 +25,16 @@ import android.os.Process import android.util.ArraySet import android.util.SparseArray import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.pkg.PackageStateInternal import com.android.server.pm.pkg.PackageUserStateInternal -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationManagerInternal import com.android.server.pm.verify.domain.DomainVerificationService import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever +import java.util.UUID import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -44,7 +45,6 @@ import org.mockito.Mockito.anyString import org.mockito.Mockito.doReturn import org.mockito.Mockito.eq import org.mockito.Mockito.verify -import java.util.UUID @RunWith(Parameterized::class) class DomainVerificationSettingsMutationTest { diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt index a90b7d5ec7da..ae570a3a0ee2 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt @@ -27,14 +27,15 @@ import android.os.Process import android.util.ArraySet import android.util.SparseArray import com.android.internal.pm.parsing.pkg.AndroidPackageInternal +import com.android.internal.pm.pkg.component.ParsedActivityImpl +import com.android.internal.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.pkg.PackageStateInternal import com.android.server.pm.pkg.PackageUserStateInternal -import com.android.server.pm.pkg.component.ParsedActivityImpl -import com.android.server.pm.pkg.component.ParsedIntentInfoImpl import com.android.server.pm.verify.domain.DomainVerificationService import com.android.server.testutils.mockThrowOnUnmocked import com.android.server.testutils.whenever import com.google.common.truth.Truth.assertThat +import java.util.UUID import org.junit.Test import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt @@ -42,8 +43,6 @@ import org.mockito.ArgumentMatchers.anyLong import org.mockito.ArgumentMatchers.anyString import org.mockito.Mockito.doReturn -import java.util.UUID - class DomainVerificationUserStateOverrideTest { companion object { diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index 76d4d55f4d5d..9739e4b46063 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -2423,6 +2423,14 @@ public class GameManagerServiceTests { } })); + when(mSysPropsMock.getInt( + ArgumentMatchers.eq(PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE), + anyInt())).thenReturn(60); + when(mSysPropsMock.getBoolean( + ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED), + ArgumentMatchers.eq(true))).thenReturn(true); + gameManagerService.onBootCompleted(); + // Set up a game in the foreground. String[] packages = {mPackageName}; when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages); @@ -2430,12 +2438,6 @@ public class GameManagerServiceTests { DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0); // Toggle game default frame rate on. - when(mSysPropsMock.getInt( - ArgumentMatchers.eq(PROPERTY_RO_SURFACEFLINGER_GAME_DEFAULT_FRAME_RATE), - anyInt())).thenReturn(60); - when(mSysPropsMock.getBoolean( - ArgumentMatchers.eq(PROPERTY_PERSISTENT_GFX_GAME_DEFAULT_FRAME_RATE_ENABLED), - ArgumentMatchers.eq(true))).thenReturn(true); gameManagerService.toggleGameDefaultFrameRate(true); // Verify that: diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java index 23886a17c890..00fe3d92413c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/BackgroundJobsControllerTest.java @@ -25,6 +25,7 @@ import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BACKGROUND import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -167,8 +168,12 @@ public class BackgroundJobsControllerTest { } private void setStoppedState(int uid, String pkgName, boolean stopped) { - doReturn(stopped).when(mPackageManagerInternal).isPackageStopped(pkgName, uid); - sendPackageStoppedBroadcast(uid, pkgName, stopped); + try { + doReturn(stopped).when(mPackageManagerInternal).isPackageStopped(pkgName, uid); + sendPackageStoppedBroadcast(uid, pkgName, stopped); + } catch (PackageManager.NameNotFoundException e) { + fail("Unable to set stopped state for unknown package: " + pkgName); + } } private void sendPackageStoppedBroadcast(int uid, String pkgName, boolean stopped) { diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java index de8b3080907c..c2b52b4ee9c8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java @@ -45,14 +45,15 @@ import android.os.Environment; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.pm.parsing.pkg.AndroidPackageLegacyUtils; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.server.pm.parsing.PackageParser2; -import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; import org.junit.Before; import org.junit.Rule; @@ -67,6 +68,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Objects; +import java.util.Set; @SmallTest @Presubmit @@ -106,6 +108,18 @@ public class ApexManagerTest { public boolean hasFeature(String feature) { return true; } + + @androidx.annotation.NonNull + @Override + public Set<String> getHiddenApiWhitelistedApps() { + return new ArraySet<>(); + } + + @androidx.annotation.NonNull + @Override + public Set<String> getInstallConstraintsAllowlist() { + return new ArraySet<>(); + } }); mMockSystem.system().stageNominalSystemState(); @@ -385,7 +399,7 @@ public class ApexManagerTest { findFactory(results, "test.apex.rebootless").apexInfo); assertThat(factoryPkg.getBaseApkPath()).isEqualTo(activeApexInfo.modulePath); assertThat(factoryPkg.getLongVersionCode()).isEqualTo(1); - assertThat(AndroidPackageUtils.isSystem(factoryPkg)).isTrue(); + assertThat(AndroidPackageLegacyUtils.isSystem(factoryPkg)).isTrue(); } @Test @@ -416,7 +430,7 @@ public class ApexManagerTest { findFactory(results, "test.apex.rebootless").apexInfo); assertThat(factoryPkg.getBaseApkPath()).isEqualTo(factoryApexInfo.modulePath); assertThat(factoryPkg.getLongVersionCode()).isEqualTo(1); - assertThat(AndroidPackageUtils.isSystem(factoryPkg)).isTrue(); + assertThat(AndroidPackageLegacyUtils.isSystem(factoryPkg)).isTrue(); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 28bd98781ced..7b29e2a4159d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -56,8 +56,10 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.spy import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder import com.android.internal.R +import com.android.internal.pm.parsing.pkg.PackageImpl import com.android.internal.pm.parsing.pkg.ParsedPackage import com.android.internal.pm.pkg.parsing.ParsingPackage +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils import com.android.server.LocalManagerRegistry import com.android.server.LocalServices import com.android.server.LockGuard @@ -68,10 +70,8 @@ import com.android.server.extendedtestutils.wheneverStatic import com.android.server.pm.dex.DexManager import com.android.server.pm.dex.DynamicCodeLogger import com.android.server.pm.parsing.PackageParser2 -import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.pkg.AndroidPackage -import com.android.server.pm.pkg.parsing.ParsingPackageUtils import com.android.server.pm.resolution.ComponentResolver import com.android.server.pm.snapshot.PackageDataSnapshot import com.android.server.pm.verify.domain.DomainVerificationManagerInternal @@ -81,14 +81,6 @@ import com.android.server.testutils.mock import com.android.server.testutils.nullable import com.android.server.testutils.whenever import com.android.server.utils.WatchedArrayMap -import libcore.util.HexEncoding -import org.junit.Assert -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement -import org.mockito.AdditionalMatchers.or -import org.mockito.Mockito -import org.mockito.quality.Strictness import java.io.File import java.io.IOException import java.nio.file.Files @@ -97,6 +89,14 @@ import java.security.cert.CertificateException import java.util.Arrays import java.util.Random import java.util.concurrent.FutureTask +import libcore.util.HexEncoding +import org.junit.Assert +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement +import org.mockito.AdditionalMatchers.or +import org.mockito.Mockito +import org.mockito.quality.Strictness /** * A utility for mocking behavior of the system and dependencies when testing PackageManagerService diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java index 2332988cc832..e5ecdc478df7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java @@ -62,6 +62,7 @@ import android.os.ParcelableException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.text.TextUtils; +import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -174,6 +175,7 @@ public class PackageArchiverTest { mUserState = new PackageUserStateImpl().setInstalled(true); mPackageSetting.setUserState(mUserId, mUserState); when(mPackageState.getUserStateOrDefault(eq(mUserId))).thenReturn(mUserState); + when(mPackageState.getUserStates()).thenReturn(new SparseArray<>()); when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps); when(mContext.getSystemService(AppOpsManager.class)).thenReturn( @@ -343,7 +345,7 @@ public class PackageArchiverTest { @Test public void archiveApp_appOptedOutOfArchiving() { - when(mAppOpsManager.checkOp( + when(mAppOpsManager.checkOpNoThrow( eq(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(PACKAGE))).thenReturn(MODE_IGNORED); @@ -430,7 +432,7 @@ public class PackageArchiverTest { @Test public void isAppArchivable_appOptedOutOfArchiving() throws PackageManager.NameNotFoundException { - when(mAppOpsManager.checkOp( + when(mAppOpsManager.checkOpNoThrow( eq(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(PACKAGE))).thenReturn(MODE_IGNORED); @@ -551,22 +553,12 @@ public class PackageArchiverTest { when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn( null); - Exception e = assertThrows( - ParcelableException.class, - () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)); - assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); - assertThat(e.getCause()).hasMessageThat().isEqualTo( - String.format("Package %s not found.", PACKAGE)); + assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull(); } @Test public void getArchivedAppIcon_notArchived() { - Exception e = assertThrows( - ParcelableException.class, - () -> mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)); - assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); - assertThat(e.getCause()).hasMessageThat().isEqualTo( - String.format("Package %s is not currently archived.", PACKAGE)); + assertThat(mArchiveManager.getArchivedAppIcon(PACKAGE, UserHandle.CURRENT)).isNull(); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt index a6ba5d4c3032..7b80aea80035 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt @@ -36,6 +36,7 @@ import org.mockito.MockitoAnnotations open class PackageHelperTestBase { companion object { + const val PLATFORM_PACKAGE_NAME = "android" const val TEST_PACKAGE_1 = "com.android.test.package1" const val TEST_PACKAGE_2 = "com.android.test.package2" const val DEVICE_OWNER_PACKAGE = "com.android.test.owner" diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt index e685c3fa9fa4..944b1aae7bda 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt @@ -21,17 +21,17 @@ import android.content.pm.PackageManager import android.content.pm.SharedLibraryInfo import android.content.pm.VersionedPackage import android.os.Build -import android.os.storage.StorageManager import android.os.UserHandle +import android.os.storage.StorageManager import android.util.ArrayMap import android.util.PackageUtils +import com.android.internal.pm.parsing.pkg.PackageImpl import com.android.internal.pm.parsing.pkg.ParsedPackage import com.android.server.SystemConfig.SharedLibraryEntry import com.android.server.compat.PlatformCompat import com.android.server.extendedtestutils.wheneverStatic import com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME import com.android.server.pm.pkg.AndroidPackage -import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.testutils.any import com.android.server.testutils.eq import com.android.server.testutils.mock diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt index 147303363200..ae53e707a7cc 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt @@ -283,6 +283,72 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { } @Test + fun getSuspendingPackagePrecedence() { + val launcherExtras = PersistableBundle() + launcherExtras.putString(TEST_PACKAGE_2, TEST_PACKAGE_2) + val targetPackages = arrayOf(TEST_PACKAGE_2) + // Suspend. + var failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), + targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, + null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, + false /* quarantined */) + assertThat(failedNames).isEmpty() + testHandler.flush() + + assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) + + // Suspend by system. + failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), + targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, + null /* dialogInfo */, PLATFORM_PACKAGE_NAME, TEST_USER_ID, deviceOwnerUid, + false /* quarantined */) + assertThat(failedNames).isEmpty() + testHandler.flush() + + assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(PLATFORM_PACKAGE_NAME) + + // QAS by package1. + failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), + targetPackages, true /* suspended */, null /* appExtras */, launcherExtras, + null /* dialogInfo */, TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid, + true /* quarantined */) + assertThat(failedNames).isEmpty() + testHandler.flush() + + assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(TEST_PACKAGE_1) + + // Un-QAS by package1. + suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), + targetPackages, { suspendingPackage -> suspendingPackage == TEST_PACKAGE_1 }, + TEST_USER_ID) + testHandler.flush() + + assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(PLATFORM_PACKAGE_NAME) + + // Un-suspend by system. + suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), + targetPackages, { suspendingPackage -> suspendingPackage == PLATFORM_PACKAGE_NAME }, + TEST_USER_ID) + testHandler.flush() + + assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE) + + // Unsuspend. + suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(), + targetPackages, { suspendingPackage -> suspendingPackage == DEVICE_OWNER_PACKAGE }, + TEST_USER_ID) + testHandler.flush() + + assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(), + TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull() + } + + @Test fun getSuspendedDialogInfo() { val dialogInfo = SuspendDialogInfo.Builder() .setTitle(TEST_PACKAGE_1).build() diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp index 07197b1ab9df..05e0e8f4a4f7 100644 --- a/services/tests/powerstatstests/Android.bp +++ b/services/tests/powerstatstests/Android.bp @@ -3,6 +3,20 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +filegroup { + name: "power_stats_ravenwood_tests", + srcs: [ + "src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java", + "src/com/android/server/power/stats/AggregatedPowerStatsTest.java", + "src/com/android/server/power/stats/MultiStateStatsTest.java", + "src/com/android/server/power/stats/PowerStatsAggregatorTest.java", + "src/com/android/server/power/stats/PowerStatsCollectorTest.java", + "src/com/android/server/power/stats/PowerStatsSchedulerTest.java", + "src/com/android/server/power/stats/PowerStatsStoreTest.java", + "src/com/android/server/power/stats/PowerStatsUidResolverTest.java", + ], +} + android_test { name: "PowerStatsTests", @@ -12,8 +26,7 @@ android_test { ], exclude_srcs: [ - "src/com/android/server/power/stats/MultiStateStatsTest.java", - "src/com/android/server/power/stats/PowerStatsStoreTest.java", + ":power_stats_ravenwood_tests", ], static_libs: [ @@ -65,10 +78,12 @@ android_ravenwood_test { "modules-utils-binary-xml", "androidx.annotation_annotation", "androidx.test.rules", + "truth", + "mockito_ravenwood", ], srcs: [ - "src/com/android/server/power/stats/MultiStateStatsTest.java", - "src/com/android/server/power/stats/PowerStatsStoreTest.java", + ":power_stats_ravenwood_tests", + "src/com/android/server/power/stats/MockClock.java", ], auto_gen_config: true, } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java index 6d61dc8d31fa..af83be04db7d 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatePowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsProcessorTest.java @@ -35,7 +35,7 @@ import java.util.Objects; @RunWith(AndroidJUnit4.class) @SmallTest -public class AggregatePowerStatsProcessorTest { +public class AggregatedPowerStatsProcessorTest { @Test public void createPowerEstimationPlan_allDeviceStatesPresentInUidStates() { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java index e8f46b30fb8c..1b045c532759 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java @@ -22,12 +22,10 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; import android.os.BatteryConsumer; -import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,11 +36,6 @@ import java.util.Arrays; @RunWith(AndroidJUnit4.class) @SmallTest public class MultiStateStatsTest { - - @Rule - public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() - .build(); - public static final int DIMENSION_COUNT = 2; @Test diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java index 67049871f396..2456636970fa 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java @@ -24,7 +24,6 @@ import static org.mockito.Mockito.mock; import android.os.BatteryConsumer; import android.os.BatteryStats; import android.os.PersistableBundle; -import android.text.format.DateFormat; import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; @@ -39,7 +38,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.text.ParseException; -import java.util.Calendar; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; import java.util.TimeZone; @@ -60,7 +60,7 @@ public class PowerStatsAggregatorTest { public void setup() throws ParseException { mHistory = new BatteryStatsHistory(32, 1024, mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock, - mMonotonicClock); + mMonotonicClock, mock(BatteryStatsHistory.TraceDelegate.class)); AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig(); config.trackPowerComponent(TEST_POWER_COMPONENT) @@ -179,9 +179,9 @@ public class PowerStatsAggregatorTest { @NonNull private static CharSequence formatDateTime(long timeInMillis) { - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - cal.setTimeInMillis(timeInMillis); - return DateFormat.format("yyyy-MM-dd hh:mm:ss", cal); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); + format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT")); + return format.format(new Date(timeInMillis)); } @Test diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java index 330f698277f8..17a7d3ecf9d3 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java @@ -22,6 +22,7 @@ import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; import android.os.PersistableBundle; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -29,12 +30,18 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.PowerStats; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest public class PowerStatsCollectorTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + private final MockClock mMockClock = new MockClock(); private final HandlerThread mHandlerThread = new HandlerThread("test"); private Handler mHandler; diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java index 7257a94cbb9a..beec66156fe4 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java @@ -24,26 +24,26 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import android.content.Context; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; +import android.platform.test.ravenwood.RavenwoodRule; -import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.MonotonicClock; -import com.android.internal.os.PowerProfile; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.time.Duration; import java.time.Instant; +import java.util.ArrayList; import java.util.List; import java.util.TimeZone; import java.util.concurrent.TimeUnit; @@ -51,39 +51,46 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class PowerStatsSchedulerTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + private PowerStatsStore mPowerStatsStore; private Handler mHandler; private MockClock mClock = new MockClock(); private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock); - private MockBatteryStatsImpl mBatteryStats; private PowerStatsScheduler mPowerStatsScheduler; - private PowerProfile mPowerProfile; private PowerStatsAggregator mPowerStatsAggregator; private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig; + private List<Long> mScheduledAlarms = new ArrayList<>(); + private boolean mPowerStatsCollectionOccurred; + + private static final int START_REALTIME = 7654321; @Before @SuppressWarnings("GuardedBy") - public void setup() { - final Context context = InstrumentationRegistry.getContext(); - + public void setup() throws IOException { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); mClock.currentTime = Instant.parse("2023-01-02T03:04:05.00Z").toEpochMilli(); - mClock.realtime = 7654321; + mClock.realtime = START_REALTIME; HandlerThread bgThread = new HandlerThread("bg thread"); bgThread.start(); - File systemDir = context.getCacheDir(); mHandler = new Handler(bgThread.getLooper()); mAggregatedPowerStatsConfig = new AggregatedPowerStatsConfig(); - mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, mAggregatedPowerStatsConfig); - mPowerProfile = mock(PowerProfile.class); - when(mPowerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT)).thenReturn(1000000.0); - mBatteryStats = new MockBatteryStatsImpl(mClock).setPowerProfile(mPowerProfile); + mPowerStatsStore = new PowerStatsStore( + Files.createTempDirectory("PowerStatsSchedulerTest").toFile(), + mHandler, mAggregatedPowerStatsConfig); mPowerStatsAggregator = mock(PowerStatsAggregator.class); - mPowerStatsScheduler = new PowerStatsScheduler(context, mPowerStatsAggregator, - TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1), mPowerStatsStore, mClock, - mMonotonicClock, mHandler, mBatteryStats); + mPowerStatsScheduler = new PowerStatsScheduler( + () -> mPowerStatsCollectionOccurred = true, + mPowerStatsAggregator, TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1), + mPowerStatsStore, + ((triggerAtMillis, tag, onAlarmListener, handler) -> + mScheduledAlarms.add(triggerAtMillis)), + mClock, mMonotonicClock, () -> 12345L, mHandler); } @Test @@ -113,7 +120,7 @@ public class PowerStatsSchedulerTest { long endTimeWallClock = mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime); - assertThat(startTime).isEqualTo(7654321 + 123); + assertThat(startTime).isEqualTo(START_REALTIME + 123); assertThat(endTime - startTime).isAtLeast(TimeUnit.MINUTES.toMillis(30)); assertThat(Instant.ofEpochMilli(endTimeWallClock)) .isEqualTo(Instant.parse("2023-01-02T04:00:00Z")); @@ -142,11 +149,15 @@ public class PowerStatsSchedulerTest { }).when(mPowerStatsAggregator).aggregatePowerStats(anyLong(), anyLong(), any(Consumer.class)); - mPowerStatsScheduler.schedulePowerStatsAggregation(); + mPowerStatsScheduler.start(/*enabled*/ true); ConditionVariable done = new ConditionVariable(); mHandler.post(done::open); done.block(); + assertThat(mPowerStatsCollectionOccurred).isTrue(); + assertThat(mScheduledAlarms).containsExactly( + START_REALTIME + TimeUnit.MINUTES.toMillis(90) + TimeUnit.HOURS.toMillis(1)); + verify(mPowerStatsAggregator, times(2)) .aggregatePowerStats(anyLong(), anyLong(), any(Consumer.class)); @@ -155,7 +166,7 @@ public class PowerStatsSchedulerTest { // Skip the first entry, which was placed in the store at the beginning of this test PowerStatsSpan.TimeFrame timeFrame1 = contents.get(1).getTimeFrames().get(0); PowerStatsSpan.TimeFrame timeFrame2 = contents.get(2).getTimeFrames().get(0); - assertThat(timeFrame1.startMonotonicTime).isEqualTo(7654321 + 123); + assertThat(timeFrame1.startMonotonicTime).isEqualTo(START_REALTIME + 123); assertThat(timeFrame2.startMonotonicTime) .isEqualTo(timeFrame1.startMonotonicTime + timeFrame1.duration); assertThat(Instant.ofEpochMilli(timeFrame2.startTime)) diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java index fe37f4241d8e..b3d25f2eef25 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java @@ -47,6 +47,10 @@ import java.util.Map; @Presubmit @RunWith(AndroidJUnit4.class) public final class OverrideRequestControllerTest { + + private static final DeviceState TEST_DEVICE_STATE_ZERO = new DeviceState(0, "TEST_STATE", 0); + private static final DeviceState TEST_DEVICE_STATE_ONE = new DeviceState(1, "TEST_STATE", 0); + private TestStatusChangeListener mStatusListener; private OverrideRequestController mController; @@ -59,7 +63,7 @@ public final class OverrideRequestControllerTest { @Test public void addRequest() { OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); assertNull(mStatusListener.getLastStatus(request)); mController.addRequest(request); @@ -69,14 +73,14 @@ public final class OverrideRequestControllerTest { @Test public void addRequest_cancelExistingRequestThroughNewRequest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); assertNull(mStatusListener.getLastStatus(firstRequest)); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); assertNull(mStatusListener.getLastStatus(secondRequest)); mController.addRequest(secondRequest); @@ -87,7 +91,7 @@ public final class OverrideRequestControllerTest { @Test public void addRequest_cancelActiveRequest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); mController.addRequest(firstRequest); @@ -101,7 +105,7 @@ public final class OverrideRequestControllerTest { @Test public void addBaseStateRequest() { OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); assertNull(mStatusListener.getLastStatus(request)); mController.addBaseStateRequest(request); @@ -111,14 +115,14 @@ public final class OverrideRequestControllerTest { @Test public void addBaseStateRequest_cancelExistingBaseStateRequestThroughNewRequest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); assertNull(mStatusListener.getLastStatus(firstRequest)); mController.addBaseStateRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); assertNull(mStatusListener.getLastStatus(secondRequest)); mController.addBaseStateRequest(secondRequest); @@ -129,7 +133,7 @@ public final class OverrideRequestControllerTest { @Test public void addBaseStateRequest_cancelActiveBaseStateRequest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addBaseStateRequest(firstRequest); @@ -143,13 +147,13 @@ public final class OverrideRequestControllerTest { @Test public void handleBaseStateChanged() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, + TEST_DEVICE_STATE_ZERO, DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); @@ -169,11 +173,11 @@ public final class OverrideRequestControllerTest { @Test public void handleProcessDied() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); @@ -192,11 +196,11 @@ public final class OverrideRequestControllerTest { mController.setStickyRequestsAllowed(true); OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ZERO, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); @@ -215,11 +219,11 @@ public final class OverrideRequestControllerTest { @Test public void handleNewSupportedStates() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE); mController.addRequest(firstRequest); @@ -242,7 +246,7 @@ public final class OverrideRequestControllerTest { @Test public void cancelOverrideRequestsTest() { OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */, 0 /* uid */, - 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); + TEST_DEVICE_STATE_ONE, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE); mController.addRequest(firstRequest); assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index a2a8424881d4..0973d46283ed 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -23,6 +23,7 @@ import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; import static com.android.server.hdmi.Constants.ADDR_RECORDER_1; import static com.android.server.hdmi.Constants.ADDR_TV; +import static com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; import static com.google.common.truth.Truth.assertThat; @@ -1712,13 +1713,14 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); + assertThat(mHdmiControlService.getLocalActiveSource()).isEqualTo( + new ActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS)); mNativeWrapper.clearResultMessages(); mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); mTestLooper.dispatchAll(); @@ -1728,8 +1730,64 @@ public class HdmiCecLocalDeviceTvTest { mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(activeSourceFromTv); + assertThat(mHdmiControlService.getLocalActiveSource()).isEqualTo( + new ActiveSource(mTvLogicalAddress, mTvPhysicalAddress)); + } + + @Test + public void requestActiveSourceActionComplete_validLocalActiveSource_doNotSendActiveSource() { + HdmiCecMessage requestActiveSource = + HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); + HdmiCecMessage activeSourceFromTv = + HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); + mHdmiControlService.setActiveSource(mTvLogicalAddress, mTvPhysicalAddress, + "HdmiCecLocalDeviceTvTest"); + mNativeWrapper.clearResultMessages(); + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); + } + + @Test + public void onAddressAllocated_startRequestActiveSourceAction_cancelOnDeviceSelect() { + HdmiCecMessage requestActiveSource = + HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); + HdmiCecMessage activeSourceFromTv = + HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + HdmiDeviceInfo playbackDevice = HdmiDeviceInfo.cecDeviceBuilder() + .setLogicalAddress(ADDR_PLAYBACK_1) + .setPhysicalAddress(0x1000) + .setPortId(PORT_1) + .setDeviceType(HdmiDeviceInfo.DEVICE_PLAYBACK) + .setVendorId(0x1234) + .setDisplayName("Playback 1") + .setDevicePowerStatus(HdmiControlManager.POWER_STATUS_ON) + .setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B) + .build(); + mHdmiControlService.getHdmiCecNetwork().addCecDevice(playbackDevice); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); + mNativeWrapper.clearResultMessages(); + mHdmiCecLocalDeviceTv.deviceSelect(playbackDevice.getId(), null); + mTestLooper.dispatchAll(); + + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); + mTestLooper.dispatchAll(); + + // RequestActiveSourceAction should be cancelled and TV will not broadcast <Active Source>. + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); } + @Test public void newDeviceConnectedIfOnlyOneGiveOsdNameSent() { mHdmiControlService.getHdmiCecNetwork().clearDeviceList(); @@ -1737,7 +1795,7 @@ public class HdmiCecLocalDeviceTvTest { .isEmpty(); HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( - ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK); + ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK); HdmiCecMessage giveOsdName = HdmiCecMessageBuilder.buildGiveOsdNameCommand( ADDR_TV, ADDR_PLAYBACK_2); mNativeWrapper.onCecMessage(reportPhysicalAddress); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java index 18961c0feef9..ee076c6bcf4b 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -140,9 +140,9 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override - public ManagedProfilePasswordCache getManagedProfilePasswordCache( + public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache( java.security.KeyStore ks) { - return mock(ManagedProfilePasswordCache.class); + return mock(UnifiedProfilePasswordCache.class); } @Override diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java index dd687fd4286c..86a1358f371c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java @@ -27,9 +27,9 @@ import static org.mockito.Mockito.when; import android.os.Build; import android.platform.test.annotations.Presubmit; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.compat.PlatformCompat; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.PackageState; import org.junit.Test; diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java index 8464969cd0f3..ee93bc1e3562 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java @@ -53,10 +53,10 @@ import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.UiDevice; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.server.LocalServices; import com.android.server.SystemConfig; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import org.junit.After; diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java index 0f87202f8939..587f5fa9c623 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java @@ -29,9 +29,9 @@ import android.util.SparseArray; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.internal.pm.parsing.pkg.ParsedPackage; import com.android.internal.pm.pkg.parsing.ParsingPackage; -import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.pkg.AndroidPackage; import dalvik.system.DelegateLastClassLoader; diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt index ee23a00f27d7..9b4ca2a86f2c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt @@ -21,17 +21,17 @@ import android.os.Environment import android.os.SystemProperties.PROP_VALUE_MAX import android.platform.test.annotations.Postsubmit import com.android.internal.R +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils import com.android.server.pm.PackageManagerService -import com.android.server.pm.pkg.parsing.ParsingPackageUtils import com.google.common.truth.Truth.assertThat +import java.io.ByteArrayInputStream +import java.io.File import org.junit.Assert.assertEquals import org.junit.Assert.assertThrows import org.junit.Assert.fail import org.junit.Test import org.xmlpull.v1.XmlPullParser import org.xmlpull.v1.XmlPullParserFactory -import java.io.ByteArrayInputStream -import java.io.File @Postsubmit class AndroidPackageParsingValidationTest { diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt index 2332817911f7..c44f583a93ef 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/TestPackageParser2.kt @@ -17,6 +17,7 @@ package com.android.server.pm.parsing import android.content.pm.ApplicationInfo +import android.util.ArraySet import java.io.File class TestPackageParser2(var cacheDir: File? = null) : PackageParser2( @@ -33,4 +34,7 @@ class TestPackageParser2(var cacheDir: File? = null) : PackageParser2( // behavior. return false } + + override fun getHiddenApiWhitelistedApps() = ArraySet<String>() + override fun getInstallConstraintsAllowlist() = ArraySet<String>() }) diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java index 5ba4851270fd..759b204516c6 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java @@ -32,8 +32,9 @@ import android.content.rollback.PackageRollbackInfo; import android.util.SparseArray; import android.util.SparseIntArray; +import com.android.internal.pm.parsing.pkg.PackageImpl; import com.android.server.pm.PackageList; -import com.android.server.pm.parsing.pkg.PackageImpl; + import com.google.common.collect.Range; import org.junit.Before; @@ -415,7 +416,7 @@ public class RollbackUnitTest { private void addPkgWithMinExtVersions(String pkg, int[][] minExtVersions) { mPackages.add(pkg); - PackageImpl pkgImpl = new PackageImpl(pkg, "baseCodePath", "codePath", null, false); + PackageImpl pkgImpl = new PackageImpl(pkg, "baseCodePath", "codePath", null, false, null); pkgImpl.setMinExtensionVersions(sparseArrayFrom(minExtVersions)); when(mMockPmi.getPackage(pkg)).thenReturn(pkgImpl); diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 6335b3bfade2..4e1c72af2727 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -46,6 +46,7 @@ android_test { "testng", "flag-junit", "notification_flags_lib", + "platform-test-rules", ], libs: [ diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 4a537dfaf8b3..14b551ae0b22 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -211,6 +211,9 @@ import android.os.WorkSource; import android.permission.PermissionManager; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; +import android.platform.test.rule.DeniedDevices; +import android.platform.test.rule.DeviceProduct; +import android.platform.test.rule.LimitDevicesRule; import android.provider.DeviceConfig; import android.provider.MediaStore; import android.provider.Settings; @@ -281,6 +284,7 @@ import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; @@ -313,6 +317,7 @@ import java.util.function.Consumer; @RunWith(AndroidTestingRunner.class) @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service. @RunWithLooper +@DeniedDevices(denied = {DeviceProduct.CF_AUTO}) public class NotificationManagerServiceTest extends UiServiceTestCase { private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"; private static final String TEST_PACKAGE = "The.name.is.Package.Test.Package"; @@ -331,6 +336,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private final int mUid = Binder.getCallingUid(); private final @UserIdInt int mUserId = UserHandle.getUserId(mUid); + @ClassRule + public static final LimitDevicesRule sLimitDevicesRule = new LimitDevicesRule(); + @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); @@ -621,10 +629,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { }); // TODO (b/291907312): remove feature flag - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_VISIT_RISKY_URIS); - mSetFlagsRule.disableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR); mSetFlagsRule.disableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER, - Flags.FLAG_POLITE_NOTIFICATIONS, android.app.Flags.FLAG_MODES_API); + Flags.FLAG_POLITE_NOTIFICATIONS); initNMS(); } @@ -6274,21 +6280,15 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(visitor, times(1)).accept(eq(personIcon3.getUri())); } - private PendingIntent getPendingIntentWithUri(Uri uri) { - return PendingIntent.getActivity(mContext, 0, - new Intent("action", uri), - PendingIntent.FLAG_IMMUTABLE); - } - @Test - public void testVisitUris_callStyle_ongoingCall() { + public void testVisitUris_callStyle() { Icon personIcon = Icon.createWithContentUri("content://media/person"); Icon verificationIcon = Icon.createWithContentUri("content://media/verification"); Person callingPerson = new Person.Builder().setName("Someone") .setIcon(personIcon) .build(); - Uri hangUpUri = Uri.parse("content://intent/hangup"); - PendingIntent hangUpIntent = getPendingIntentWithUri(hangUpUri); + PendingIntent hangUpIntent = PendingIntent.getActivity(mContext, 0, new Intent(), + PendingIntent.FLAG_IMMUTABLE); Notification n = new Notification.Builder(mContext, "a") .setStyle(Notification.CallStyle.forOngoingCall(callingPerson, hangUpIntent) .setVerificationIcon(verificationIcon)) @@ -6301,35 +6301,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(visitor, times(1)).accept(eq(personIcon.getUri())); verify(visitor, times(1)).accept(eq(verificationIcon.getUri())); - verify(visitor, times(1)).accept(eq(hangUpUri)); - } - - @Test - public void testVisitUris_callStyle_incomingCall() { - Icon personIcon = Icon.createWithContentUri("content://media/person"); - Icon verificationIcon = Icon.createWithContentUri("content://media/verification"); - Person callingPerson = new Person.Builder().setName("Someone") - .setIcon(personIcon) - .build(); - Uri answerUri = Uri.parse("content://intent/answer"); - PendingIntent answerIntent = getPendingIntentWithUri(answerUri); - Uri declineUri = Uri.parse("content://intent/decline"); - PendingIntent declineIntent = getPendingIntentWithUri(declineUri); - Notification n = new Notification.Builder(mContext, "a") - .setStyle(Notification.CallStyle.forIncomingCall(callingPerson, declineIntent, - answerIntent) - .setVerificationIcon(verificationIcon)) - .setContentTitle("Calling...") - .setSmallIcon(android.R.drawable.sym_def_app_icon) - .build(); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - n.visitUris(visitor); - - verify(visitor, times(1)).accept(eq(personIcon.getUri())); - verify(visitor, times(1)).accept(eq(verificationIcon.getUri())); - verify(visitor, times(1)).accept(eq(answerIntent.getIntent().getData())); - verify(visitor, times(1)).accept(eq(declineUri)); } @Test @@ -6381,74 +6352,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testVisitUris_wearableExtender() { Icon actionIcon = Icon.createWithContentUri("content://media/action"); Icon wearActionIcon = Icon.createWithContentUri("content://media/wearAction"); - Uri displayIntentUri = Uri.parse("content://intent/display"); - PendingIntent displayIntent = getPendingIntentWithUri(displayIntentUri); - Uri actionIntentUri = Uri.parse("content://intent/action"); - PendingIntent actionIntent = getPendingIntentWithUri(actionIntentUri); - Uri wearActionIntentUri = Uri.parse("content://intent/wear"); - PendingIntent wearActionIntent = getPendingIntentWithUri(wearActionIntentUri); + PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent(), + PendingIntent.FLAG_IMMUTABLE); Notification n = new Notification.Builder(mContext, "a") .setSmallIcon(android.R.drawable.sym_def_app_icon) - .addAction( - new Notification.Action.Builder(actionIcon, "Hey!", actionIntent).build()) - .extend(new Notification.WearableExtender() - .setDisplayIntent(displayIntent) - .addAction(new Notification.Action.Builder(wearActionIcon, "Wear!", - wearActionIntent) - .build())) + .addAction(new Notification.Action.Builder(actionIcon, "Hey!", intent).build()) + .extend(new Notification.WearableExtender().addAction( + new Notification.Action.Builder(wearActionIcon, "Wear!", intent).build())) .build(); Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); n.visitUris(visitor); verify(visitor).accept(eq(actionIcon.getUri())); - verify(visitor, times(1)).accept(eq(actionIntentUri)); verify(visitor).accept(eq(wearActionIcon.getUri())); - verify(visitor, times(1)).accept(eq(wearActionIntentUri)); - } - - @Test - public void testVisitUris_tvExtender() { - Uri contentIntentUri = Uri.parse("content://intent/content"); - PendingIntent contentIntent = getPendingIntentWithUri(contentIntentUri); - Uri deleteIntentUri = Uri.parse("content://intent/delete"); - PendingIntent deleteIntent = getPendingIntentWithUri(deleteIntentUri); - Notification n = new Notification.Builder(mContext, "a") - .extend( - new Notification.TvExtender() - .setContentIntent(contentIntent) - .setDeleteIntent(deleteIntent)) - .build(); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - n.visitUris(visitor); - - verify(visitor, times(1)).accept(eq(contentIntentUri)); - verify(visitor, times(1)).accept(eq(deleteIntentUri)); - } - - @Test - public void testVisitUris_carExtender() { - final String testParticipant = "testParticipant"; - Uri readPendingIntentUri = Uri.parse("content://intent/read"); - PendingIntent readPendingIntent = getPendingIntentWithUri(readPendingIntentUri); - Uri replyPendingIntentUri = Uri.parse("content://intent/reply"); - PendingIntent replyPendingIntent = getPendingIntentWithUri(replyPendingIntentUri); - final RemoteInput testRemoteInput = new RemoteInput.Builder("key").build(); - - Notification n = new Notification.Builder(mContext, "a") - .extend(new Notification.CarExtender().setUnreadConversation( - new Notification.CarExtender.Builder(testParticipant) - .setReadPendingIntent(readPendingIntent) - .setReplyAction(replyPendingIntent, testRemoteInput) - .build())) - .build(); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - n.visitUris(visitor); - - verify(visitor, times(1)).accept(eq(readPendingIntentUri)); - verify(visitor, times(1)).accept(eq(replyPendingIntentUri)); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java index ea948ca0e28b..44dbe385a144 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java @@ -146,10 +146,6 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { .put(Notification.Action.Builder.class, "extend") // Overwrites icon supplied to constructor. .put(Notification.BubbleMetadata.Builder.class, "setIcon") - // Overwrites intent supplied to constructor. - .put(Notification.BubbleMetadata.Builder.class, "setIntent") - // Overwrites intent supplied to constructor. - .put(Notification.BubbleMetadata.Builder.class, "setDeleteIntent") // Discards previously-added actions. .put(RemoteViews.class, "mergeRemoteViews") .build(); @@ -684,14 +680,14 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { } if (clazz == Intent.class) { - return new Intent("action", generateUri(where.plus(Intent.class))); + // TODO(b/281044385): Are Intent Uris (new Intent(String,Uri)) relevant? + return new Intent("action"); } if (clazz == PendingIntent.class) { - // PendingIntent can have an Intent with a Uri. - Uri intentUri = generateUri(where.plus(PendingIntent.class)); - return PendingIntent.getActivity(mContext, 0, - new Intent("action", intentUri), + // PendingIntent can have an Intent with a Uri but those are inaccessible and + // not inspected. + return PendingIntent.getActivity(mContext, 0, new Intent("action"), PendingIntent.FLAG_IMMUTABLE); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 646ee3f6d206..1aea56cd8f9f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -80,6 +80,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.notNull; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -265,10 +266,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { .thenReturn(CUSTOM_PKG_UID); when(mPackageManager.getPackagesForUid(anyInt())).thenReturn( new String[] {pkg}); - ApplicationInfo mockAppInfo = mock(ApplicationInfo.class); - when(mockAppInfo.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL); + + ApplicationInfo appInfoSpy = spy(new ApplicationInfo()); + appInfoSpy.icon = ICON_RES_ID; + when(appInfoSpy.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL); when(mPackageManager.getApplicationInfo(eq(CUSTOM_PKG_NAME), anyInt())) - .thenReturn(mockAppInfo); + .thenReturn(appInfoSpy); mZenModeHelper.mPm = mPackageManager; mZenModeEventLogger.reset(); @@ -3753,6 +3756,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { rule.zenPolicy = policy; rule.pkg = ownerPkg; rule.name = CUSTOM_APP_LABEL; + rule.iconResName = ICON_RES_NAME; + rule.triggerDescription = mContext.getString(R.string.zen_mode_implicit_trigger_description, + CUSTOM_APP_LABEL); + rule.type = AutomaticZenRule.TYPE_OTHER; rule.enabled = true; return rule; } diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java new file mode 100644 index 000000000000..49efd1bdd92a --- /dev/null +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.RemoteException; + +import org.junit.Before; +import org.junit.Test; + +public class VibratorControlServiceTest { + + private VibratorControlService mVibratorControlService; + private final Object mLock = new Object(); + + @Before + public void setUp() throws Exception { + mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), mLock); + } + + @Test + public void testRegisterVibratorController() throws RemoteException { + FakeVibratorController fakeController = new FakeVibratorController(); + mVibratorControlService.registerVibratorController(fakeController); + + assertThat(fakeController.isLinkedToDeath).isTrue(); + } + + @Test + public void testUnregisterVibratorController_providingTheRegisteredController_performsRequest() + throws RemoteException { + FakeVibratorController fakeController = new FakeVibratorController(); + mVibratorControlService.registerVibratorController(fakeController); + mVibratorControlService.unregisterVibratorController(fakeController); + assertThat(fakeController.isLinkedToDeath).isFalse(); + } + + @Test + public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest() + throws RemoteException { + FakeVibratorController fakeController1 = new FakeVibratorController(); + FakeVibratorController fakeController2 = new FakeVibratorController(); + mVibratorControlService.registerVibratorController(fakeController1); + + mVibratorControlService.unregisterVibratorController(fakeController2); + assertThat(fakeController1.isLinkedToDeath).isTrue(); + } +} diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java new file mode 100644 index 000000000000..79abe21a301d --- /dev/null +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerHolderTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.RemoteException; + +import org.junit.Before; +import org.junit.Test; + +public class VibratorControllerHolderTest { + + private final FakeVibratorController mFakeVibratorController = new FakeVibratorController(); + private VibratorControllerHolder mVibratorControllerHolder; + + @Before + public void setUp() throws Exception { + mVibratorControllerHolder = new VibratorControllerHolder(); + } + + @Test + public void testSetVibratorController_linksVibratorControllerToDeath() throws RemoteException { + mVibratorControllerHolder.setVibratorController(mFakeVibratorController); + assertThat(mVibratorControllerHolder.getVibratorController()) + .isEqualTo(mFakeVibratorController); + assertThat(mFakeVibratorController.isLinkedToDeath).isTrue(); + } + + @Test + public void testSetVibratorController_setControllerToNull_unlinksVibratorControllerToDeath() + throws RemoteException { + mVibratorControllerHolder.setVibratorController(mFakeVibratorController); + mVibratorControllerHolder.setVibratorController(null); + assertThat(mFakeVibratorController.isLinkedToDeath).isFalse(); + assertThat(mVibratorControllerHolder.getVibratorController()).isNull(); + } + + @Test + public void testBinderDied_withValidController_unlinksVibratorControllerToDeath() + throws RemoteException { + mVibratorControllerHolder.setVibratorController(mFakeVibratorController); + mVibratorControllerHolder.binderDied(mFakeVibratorController); + assertThat(mFakeVibratorController.isLinkedToDeath).isFalse(); + assertThat(mVibratorControllerHolder.getVibratorController()).isNull(); + } + + @Test + public void testBinderDied_withInvalidController_ignoresRequest() + throws RemoteException { + mVibratorControllerHolder.setVibratorController(mFakeVibratorController); + FakeVibratorController imposterVibratorController = new FakeVibratorController(); + mVibratorControllerHolder.binderDied(imposterVibratorController); + assertThat(mFakeVibratorController.isLinkedToDeath).isTrue(); + assertThat(mVibratorControllerHolder.getVibratorController()) + .isEqualTo(mFakeVibratorController); + } +} diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java index 4e9bbe0a28fe..d6b2116e2682 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -307,9 +307,10 @@ public class VibratorManagerServiceTest { @Override void addService(String name, IBinder service) { - Object serviceInstance = service; - mExternalVibratorService = - (VibratorManagerService.ExternalVibratorService) serviceInstance; + if (service instanceof VibratorManagerService.ExternalVibratorService) { + mExternalVibratorService = + (VibratorManagerService.ExternalVibratorService) service; + } } HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider( diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java new file mode 100644 index 000000000000..7e235870cedc --- /dev/null +++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java @@ -0,0 +1,58 @@ +/* + * 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 com.android.server.vibrator; + +import android.annotation.NonNull; +import android.frameworks.vibrator.IVibratorController; +import android.os.IBinder; +import android.os.RemoteException; + +/** + * Provides a fake implementation of {@link android.frameworks.vibrator.IVibratorController} for + * testing. + */ +public final class FakeVibratorController extends IVibratorController.Stub { + + public boolean isLinkedToDeath = false; + + @Override + public void requestVibrationParams(int i, long l, IBinder iBinder) throws RemoteException { + + } + + @Override + public int getInterfaceVersion() throws RemoteException { + return 0; + } + + @Override + public String getInterfaceHash() throws RemoteException { + return null; + } + + @Override + public void linkToDeath(@NonNull DeathRecipient recipient, int flags) { + super.linkToDeath(recipient, flags); + isLinkedToDeath = true; + } + + @Override + public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) { + isLinkedToDeath = false; + return super.unlinkToDeath(recipient, flags); + } +} diff --git a/services/tests/voiceinteractiontests/Android.bp b/services/tests/voiceinteractiontests/Android.bp index 744cb63f72b3..8a79fe443179 100644 --- a/services/tests/voiceinteractiontests/Android.bp +++ b/services/tests/voiceinteractiontests/Android.bp @@ -44,6 +44,7 @@ android_test { "servicestests-core-utils", "servicestests-utils-mockito-extended", "truth", + "frameworks-base-testutils", ], libs: [ diff --git a/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java new file mode 100644 index 000000000000..159c760992c6 --- /dev/null +++ b/services/tests/voiceinteractiontests/src/com/android/server/voiceinteraction/SetSandboxedTrainingDataAllowedTest.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.voiceinteraction; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.Manifest; +import android.app.ActivityManagerInternal; +import android.app.AppOpsManager; +import android.app.role.RoleManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.os.PermissionEnforcer; +import android.os.Process; +import android.os.test.FakePermissionEnforcer; +import android.platform.test.annotations.Presubmit; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.modules.utils.testing.ExtendedMockitoRule; +import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; +import com.android.server.pm.permission.LegacyPermissionManagerInternal; +import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.wm.ActivityTaskManagerInternal; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.quality.Strictness; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class SetSandboxedTrainingDataAllowedTest { + + @Captor private ArgumentCaptor<Integer> mOpIdCaptor, mUidCaptor, mOpModeCaptor; + + @Mock + private AppOpsManager mAppOpsManager; + + @Mock + private VoiceInteractionManagerServiceImpl mVoiceInteractionManagerServiceImpl; + + private FakePermissionEnforcer mPermissionEnforcer = new FakePermissionEnforcer(); + + private Context mContext; + + private VoiceInteractionManagerService mVoiceInteractionManagerService; + private VoiceInteractionManagerService.VoiceInteractionManagerServiceStub + mVoiceInteractionManagerServiceStub; + + private ApplicationInfo mApplicationInfo = new ApplicationInfo(); + + @Rule + public final ExtendedMockitoRule mExtendedMockitoRule = + new ExtendedMockitoRule.Builder(this) + .setStrictness(Strictness.WARN) + .mockStatic(LocalServices.class) + .mockStatic(PermissionEnforcer.class) + .build(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = spy(ApplicationProvider.getApplicationContext()); + + doReturn(mPermissionEnforcer).when(() -> PermissionEnforcer.fromContext(any())); + doReturn(mock(PermissionManagerServiceInternal.class)).when( + () -> LocalServices.getService(PermissionManagerServiceInternal.class)); + doReturn(mock(ActivityManagerInternal.class)).when( + () -> LocalServices.getService(ActivityManagerInternal.class)); + doReturn(mock(UserManagerInternal.class)).when( + () -> LocalServices.getService(UserManagerInternal.class)); + doReturn(mock(ActivityTaskManagerInternal.class)).when( + () -> LocalServices.getService(ActivityTaskManagerInternal.class)); + doReturn(mock(LegacyPermissionManagerInternal.class)).when( + () -> LocalServices.getService(LegacyPermissionManagerInternal.class)); + doReturn(mock(RoleManager.class)).when(mContext).getSystemService(RoleManager.class); + doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE); + doReturn(mApplicationInfo).when(mVoiceInteractionManagerServiceImpl).getApplicationInfo(); + + mVoiceInteractionManagerService = new VoiceInteractionManagerService(mContext); + mVoiceInteractionManagerServiceStub = + mVoiceInteractionManagerService.new VoiceInteractionManagerServiceStub(); + mVoiceInteractionManagerServiceStub.mImpl = mVoiceInteractionManagerServiceImpl; + mPermissionEnforcer.grant(Manifest.permission.MANAGE_HOTWORD_DETECTION); + } + + @Test + public void setShouldReceiveSandboxedTrainingData_currentAndPreinstalledAssistant_setsOp() { + // Set application info so current app is the current and preinstalled assistant. + mApplicationInfo.uid = Process.myUid(); + mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + + mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData( + /* allowed= */ true); + + verify(mAppOpsManager).setUidMode(mOpIdCaptor.capture(), mUidCaptor.capture(), + mOpModeCaptor.capture()); + assertThat(mOpIdCaptor.getValue()).isEqualTo( + AppOpsManager.OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA); + assertThat(mOpModeCaptor.getValue()).isEqualTo(AppOpsManager.MODE_ALLOWED); + assertThat(mUidCaptor.getValue()).isEqualTo(Process.myUid()); + } + + @Test + public void setShouldReceiveSandboxedTrainingData_missingPermission_doesNotSetOp() { + // Set application info so current app is the current and preinstalled assistant. + mApplicationInfo.uid = Process.myUid(); + mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + + // Simulate missing MANAGE_HOTWORD_DETECTION permission. + mPermissionEnforcer.revoke(Manifest.permission.MANAGE_HOTWORD_DETECTION); + + assertThrows(SecurityException.class, + () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData( + /* allowed= */ true)); + + verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt()); + } + + @Test + public void setShouldReceiveSandboxedTrainingData_notPreinstalledAssistant_doesNotSetOp() { + // Set application info so current app is not preinstalled assistant. + mApplicationInfo.uid = Process.myUid(); + mApplicationInfo.flags = ApplicationInfo.FLAG_INSTALLED; // Does not contain FLAG_SYSTEM. + + assertThrows(SecurityException.class, + () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData( + /* allowed= */ true)); + + verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt()); + } + + @Test + public void setShouldReceiveSandboxedTrainingData_notCurrentAssistant_doesNotSetOp() { + // Set application info so current app is not current assistant. + mApplicationInfo.uid = Process.SHELL_UID; // Set current assistant uid to shell UID. + mApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + + assertThrows(SecurityException.class, + () -> mVoiceInteractionManagerServiceStub.setShouldReceiveSandboxedTrainingData( + /* allowed= */ true)); + + verify(mAppOpsManager, never()).setUidMode(anyInt(), anyInt(), anyInt()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java index 8d236eda5dc5..0382ca0d9fec 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java @@ -61,6 +61,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { META_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC); META_SHORTCUTS.append(KEYCODE_S, Intent.CATEGORY_APP_MESSAGING); } + private static final int ANY_DISPLAY_ID = 123; @Before public void setUp() { @@ -96,8 +97,9 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { */ @Test public void testCtrlSpace() { - sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, 0); - mPhoneWindowManager.assertSwitchKeyboardLayout(1); + sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, /* duration= */ 0, + ANY_DISPLAY_ID); + mPhoneWindowManager.assertSwitchKeyboardLayout(/* direction= */ 1, ANY_DISPLAY_ID); } /** @@ -105,8 +107,9 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { */ @Test public void testCtrlShiftSpace() { - sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE}, 0); - mPhoneWindowManager.assertSwitchKeyboardLayout(-1); + sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE}, + /* duration= */ 0, ANY_DISPLAY_ID); + mPhoneWindowManager.assertSwitchKeyboardLayout(/* direction= */ -1, ANY_DISPLAY_ID); } /** diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java index 9cdec2588501..157d1627d993 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java @@ -114,7 +114,7 @@ class ShortcutKeyTestBase { } } - void sendKeyCombination(int[] keyCodes, long durationMillis, boolean longPress) { + void sendKeyCombination(int[] keyCodes, long durationMillis, boolean longPress, int displayId) { final long downTime = mPhoneWindowManager.getCurrentTime(); final int count = keyCodes.length; int metaState = 0; @@ -124,7 +124,7 @@ class ShortcutKeyTestBase { final KeyEvent event = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode, 0 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/, InputDevice.SOURCE_KEYBOARD); - event.setDisplayId(DEFAULT_DISPLAY); + event.setDisplayId(displayId); interceptKey(event); // The order is important here, metaState could be updated and applied to the next key. metaState |= MODIFIER.getOrDefault(keyCode, 0); @@ -142,7 +142,7 @@ class ShortcutKeyTestBase { KeyEvent.ACTION_DOWN, keyCode, 1 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, KeyEvent.FLAG_LONG_PRESS /*flags*/, InputDevice.SOURCE_KEYBOARD); - nextDownEvent.setDisplayId(DEFAULT_DISPLAY); + nextDownEvent.setDisplayId(displayId); interceptKey(nextDownEvent); } } @@ -153,18 +153,23 @@ class ShortcutKeyTestBase { final KeyEvent upEvent = new KeyEvent(downTime, eventTime, KeyEvent.ACTION_UP, keyCode, 0, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/, InputDevice.SOURCE_KEYBOARD); - upEvent.setDisplayId(DEFAULT_DISPLAY); + upEvent.setDisplayId(displayId); interceptKey(upEvent); metaState &= ~MODIFIER.getOrDefault(keyCode, 0); } } void sendKeyCombination(int[] keyCodes, long durationMillis) { - sendKeyCombination(keyCodes, durationMillis, false /* longPress */); + sendKeyCombination(keyCodes, durationMillis, false /* longPress */, DEFAULT_DISPLAY); + } + + void sendKeyCombination(int[] keyCodes, long durationMillis, int displayId) { + sendKeyCombination(keyCodes, durationMillis, false /* longPress */, displayId); } void sendLongPressKeyCombination(int[] keyCodes) { - sendKeyCombination(keyCodes, ViewConfiguration.getLongPressTimeout(), true /* longPress */); + sendKeyCombination(keyCodes, ViewConfiguration.getLongPressTimeout(), true /* longPress */, + DEFAULT_DISPLAY); } void sendKey(int keyCode) { @@ -172,7 +177,7 @@ class ShortcutKeyTestBase { } void sendKey(int keyCode, boolean longPress) { - sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress); + sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress, DEFAULT_DISPLAY); } private void interceptKey(KeyEvent keyEvent) { diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index d057226836a3..0678210e1d2f 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -166,12 +166,34 @@ class TestPhoneWindowManager { @Mock private PhoneWindowManager.ButtonOverridePermissionChecker mButtonOverridePermissionChecker; + @Mock private IBinder mInputToken; + @Mock private IBinder mImeTargetWindowToken; + private StaticMockitoSession mMockitoSession; private OffsettableClock mClock = new OffsettableClock(); private TestLooper mTestLooper = new TestLooper(() -> mClock.now()); private HandlerThread mHandlerThread; private Handler mHandler; + private boolean mIsTalkBackEnabled; + + class TestTalkbackShortcutController extends TalkbackShortcutController { + TestTalkbackShortcutController(Context context) { + super(context); + } + + @Override + boolean toggleTalkback(int currentUserId) { + mIsTalkBackEnabled = !mIsTalkBackEnabled; + return mIsTalkBackEnabled; + } + + @Override + boolean isTalkBackShortcutGestureEnabled() { + return true; + } + } + private class TestInjector extends PhoneWindowManager.Injector { TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) { super(context, funcs, mTestLooper.getLooper()); @@ -197,6 +219,10 @@ class TestPhoneWindowManager { PhoneWindowManager.ButtonOverridePermissionChecker getButtonOverridePermissionChecker() { return mButtonOverridePermissionChecker; } + + TalkbackShortcutController getTalkbackShortcutController() { + return new TestTalkbackShortcutController(mContext); + } } TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) { @@ -304,6 +330,9 @@ class TestPhoneWindowManager { doNothing().when(mPhoneWindowManager).finishedWakingUp(anyInt(), anyInt()); doNothing().when(mPhoneWindowManager).lockNow(any()); + doReturn(mImeTargetWindowToken) + .when(mWindowManagerInternal).getTargetWindowTokenFromInputToken(mInputToken); + mPhoneWindowManager.init(new TestInjector(mContext, mWindowManagerFuncsImpl)); mPhoneWindowManager.systemReady(); mPhoneWindowManager.systemBooted(); @@ -342,12 +371,12 @@ class TestPhoneWindowManager { } long interceptKeyBeforeDispatching(KeyEvent event) { - return mPhoneWindowManager.interceptKeyBeforeDispatching(null /*focusedToken*/, - event, FLAG_INTERACTIVE); + return mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, event, + FLAG_INTERACTIVE); } void dispatchUnhandledKey(KeyEvent event) { - mPhoneWindowManager.dispatchUnhandledKey(null /*focusedToken*/, event, FLAG_INTERACTIVE); + mPhoneWindowManager.dispatchUnhandledKey(mInputToken, event, FLAG_INTERACTIVE); } long getCurrentTime() { @@ -623,14 +652,16 @@ class TestPhoneWindowManager { verify(mStatusBarManagerInternal).startAssist(any()); } - void assertSwitchKeyboardLayout(int direction) { + void assertSwitchKeyboardLayout(int direction, int displayId) { mTestLooper.dispatchAll(); if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) { - verify(mInputMethodManagerInternal).switchKeyboardLayout(eq(direction)); + verify(mInputMethodManagerInternal).onSwitchKeyboardLayoutShortcut(eq(direction), + eq(displayId), eq(mImeTargetWindowToken)); verify(mWindowManagerFuncsImpl, never()).switchKeyboardLayout(anyInt(), anyInt()); } else { verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), eq(direction)); - verify(mInputMethodManagerInternal, never()).switchKeyboardLayout(anyInt()); + verify(mInputMethodManagerInternal, never()) + .onSwitchKeyboardLayoutShortcut(anyInt(), anyInt(), any()); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 65e77dcd4ca9..d4ba3b25178d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -124,7 +124,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { private void verifyOnActivityLaunched(ActivityRecord activity) { final ArgumentCaptor<Long> idCaptor = ArgumentCaptor.forClass(Long.class); verifyAsync(mLaunchObserver).onActivityLaunched(idCaptor.capture(), - eq(activity.mActivityComponent), anyInt()); + eq(activity.mActivityComponent), anyInt(), anyInt()); final long id = idCaptor.getValue(); setExpectedStartedId(id, activity); mLastLaunchedIds.put(activity.mActivityComponent, id); @@ -132,7 +132,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { private void verifyOnActivityLaunchFinished(ActivityRecord activity) { verifyAsync(mLaunchObserver).onActivityLaunchFinished(eq(mExpectedStartedId), - eq(activity.mActivityComponent), anyLong()); + eq(activity.mActivityComponent), anyLong(), anyInt()); } private void setExpectedStartedId(long id, Object reason) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 7aa46a62b0f1..85c6f9e9b2fe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -123,6 +123,7 @@ import android.app.ICompatCameraControlCallback; import android.app.PictureInPictureParams; import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ClientTransaction; +import android.app.servertransaction.ClientTransactionItem; import android.app.servertransaction.DestroyActivityItem; import android.app.servertransaction.PauseActivityItem; import android.app.servertransaction.WindowStateResizeItem; @@ -169,6 +170,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.invocation.InvocationOnMock; import java.util.ArrayList; +import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -260,8 +262,18 @@ public class ActivityRecordTests extends WindowTestsBase { final MutableBoolean pauseFound = new MutableBoolean(false); doAnswer((InvocationOnMock invocationOnMock) -> { final ClientTransaction transaction = invocationOnMock.getArgument(0); - if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) { - pauseFound.value = true; + final List<ClientTransactionItem> items = transaction.getTransactionItems(); + if (items != null) { + for (ClientTransactionItem item : items) { + if (item instanceof PauseActivityItem) { + pauseFound.value = true; + break; + } + } + } else { + if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) { + pauseFound.value = true; + } } return null; }).when(mClientLifecycleManager).scheduleTransaction(any()); @@ -279,6 +291,8 @@ public class ActivityRecordTests extends WindowTestsBase { // If the activity is not focusable, it should move to paused. activity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); + mClientLifecycleManager.dispatchPendingTransactions(); + assertTrue(activity.isState(PAUSING)); assertTrue(pauseFound.value); @@ -1239,7 +1253,7 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test - public void testFinishActivityIfPossible_sendResultImmediately() { + public void testFinishActivityIfPossible_sendResultImmediately() throws RemoteException { // Create activity representing the source of the activity result. final ComponentName sourceComponent = ComponentName.createRelative( DEFAULT_COMPONENT_PACKAGE_NAME, ".SourceActivity"); @@ -1270,12 +1284,8 @@ public class ActivityRecordTests extends WindowTestsBase { targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */); - try { - verify(mClientLifecycleManager, atLeastOnce()).scheduleTransaction( - any(ClientTransaction.class)); - } catch (RemoteException ignored) { - } - + verify(mClientLifecycleManager, atLeastOnce()).scheduleTransactionItem( + any(), any(ClientTransactionItem.class)); assertNull(targetActivity.results); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java index 04aa9815e698..7fdc5fc2cff6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java @@ -17,13 +17,15 @@ package com.android.server.wm; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; @@ -31,12 +33,15 @@ import android.app.IApplicationThread; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; +import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.filters.SmallTest; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -47,30 +52,47 @@ import org.mockito.MockitoAnnotations; * Build/Install/Run: * atest WmTests:ClientLifecycleManagerTests */ +// Suppress GuardedBy warning on unit tests +@SuppressWarnings("GuardedBy") @SmallTest @Presubmit public class ClientLifecycleManagerTests { + @Rule(order = 0) + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + @Rule(order = 1) + public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule(); + + @Mock + private IBinder mClientBinder; @Mock private IApplicationThread mClient; @Mock private IApplicationThread.Stub mNonBinderClient; @Mock + private ClientTransaction mTransaction; + @Mock private ClientTransactionItem mTransactionItem; @Mock private ActivityLifecycleItem mLifecycleItem; @Captor private ArgumentCaptor<ClientTransaction> mTransactionCaptor; + private WindowManagerService mWms; private ClientLifecycleManager mLifecycleManager; @Before public void setup() { MockitoAnnotations.initMocks(this); + mWms = mSystemServices.getWindowManagerService(); mLifecycleManager = spy(new ClientLifecycleManager()); + mLifecycleManager.setWindowManager(mWms); doReturn(true).when(mLifecycleItem).isActivityLifecycleItem(); + doReturn(mClientBinder).when(mClient).asBinder(); + doReturn(mNonBinderClient).when(mNonBinderClient).asBinder(); } @Test @@ -92,9 +114,11 @@ public class ClientLifecycleManagerTests { } @Test - public void testScheduleTransactionItem() throws RemoteException { - doNothing().when(mLifecycleManager).scheduleTransaction(any()); - mLifecycleManager.scheduleTransactionItem(mClient, mTransactionItem); + public void testScheduleTransactionItem_notBundle() throws RemoteException { + mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + + // Use non binder client to get non-recycled ClientTransaction. + mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mTransactionItem); verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture()); ClientTransaction transaction = mTransactionCaptor.getValue(); @@ -104,7 +128,7 @@ public class ClientLifecycleManagerTests { assertNull(transaction.getTransactionItems()); clearInvocations(mLifecycleManager); - mLifecycleManager.scheduleTransactionItem(mClient, mLifecycleItem); + mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem); verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture()); transaction = mTransactionCaptor.getValue(); @@ -113,9 +137,54 @@ public class ClientLifecycleManagerTests { } @Test - public void testScheduleTransactionAndLifecycleItems() throws RemoteException { - doNothing().when(mLifecycleManager).scheduleTransaction(any()); - mLifecycleManager.scheduleTransactionAndLifecycleItems(mClient, mTransactionItem, + public void testScheduleTransactionItem() throws RemoteException { + mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + spyOn(mWms.mWindowPlacerLocked); + doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled(); + + // Use non binder client to get non-recycled ClientTransaction. + mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mTransactionItem); + + // When there is traversal scheduled, add transaction items to pending. + assertEquals(1, mLifecycleManager.mPendingTransactions.size()); + ClientTransaction transaction = + mLifecycleManager.mPendingTransactions.get(mNonBinderClient); + assertEquals(1, transaction.getTransactionItems().size()); + assertEquals(mTransactionItem, transaction.getTransactionItems().get(0)); + assertNull(transaction.getCallbacks()); + assertNull(transaction.getLifecycleStateRequest()); + verify(mLifecycleManager, never()).scheduleTransaction(any()); + + // Add new transaction item to the existing pending. + clearInvocations(mLifecycleManager); + mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem); + + assertEquals(1, mLifecycleManager.mPendingTransactions.size()); + transaction = mLifecycleManager.mPendingTransactions.get(mNonBinderClient); + assertEquals(2, transaction.getTransactionItems().size()); + assertEquals(mTransactionItem, transaction.getTransactionItems().get(0)); + assertEquals(mLifecycleItem, transaction.getTransactionItems().get(1)); + assertNull(transaction.getCallbacks()); + assertNull(transaction.getLifecycleStateRequest()); + verify(mLifecycleManager, never()).scheduleTransaction(any()); + } + + @Test + public void testScheduleTransactionItemUnlocked() throws RemoteException { + // Use non binder client to get non-recycled ClientTransaction. + mLifecycleManager.scheduleTransactionItemUnlocked(mNonBinderClient, mTransactionItem); + + // Dispatch immediately. + assertTrue(mLifecycleManager.mPendingTransactions.isEmpty()); + verify(mLifecycleManager).scheduleTransaction(any()); + } + + @Test + public void testScheduleTransactionAndLifecycleItems_notBundle() throws RemoteException { + mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + + // Use non binder client to get non-recycled ClientTransaction. + mLifecycleManager.scheduleTransactionAndLifecycleItems(mNonBinderClient, mTransactionItem, mLifecycleItem); verify(mLifecycleManager).scheduleTransaction(mTransactionCaptor.capture()); @@ -124,4 +193,36 @@ public class ClientLifecycleManagerTests { assertEquals(mTransactionItem, transaction.getCallbacks().get(0)); assertEquals(mLifecycleItem, transaction.getLifecycleStateRequest()); } + + @Test + public void testScheduleTransactionAndLifecycleItems() throws RemoteException { + mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + spyOn(mWms.mWindowPlacerLocked); + doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled(); + + // Use non binder client to get non-recycled ClientTransaction. + mLifecycleManager.scheduleTransactionAndLifecycleItems(mNonBinderClient, mTransactionItem, + mLifecycleItem); + + assertEquals(1, mLifecycleManager.mPendingTransactions.size()); + final ClientTransaction transaction = + mLifecycleManager.mPendingTransactions.get(mNonBinderClient); + assertEquals(2, transaction.getTransactionItems().size()); + assertEquals(mTransactionItem, transaction.getTransactionItems().get(0)); + assertEquals(mLifecycleItem, transaction.getTransactionItems().get(1)); + assertNull(transaction.getCallbacks()); + assertNull(transaction.getLifecycleStateRequest()); + verify(mLifecycleManager, never()).scheduleTransaction(any()); + } + + @Test + public void testDispatchPendingTransactions() throws RemoteException { + mLifecycleManager.mPendingTransactions.put(mClientBinder, mTransaction); + + mLifecycleManager.dispatchPendingTransactions(); + + assertTrue(mLifecycleManager.mPendingTransactions.isEmpty()); + verify(mTransaction).schedule(); + verify(mTransaction).recycle(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 71d2504e1746..dfe79bf1e3e6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -993,7 +993,9 @@ public class DisplayContentTests extends WindowTestsBase { dc.getDisplayPolicy().getDecorInsetsInfo(ROTATION_0, dc.mBaseDisplayHeight, dc.mBaseDisplayWidth).mConfigFrame.set(0, 0, 1000, 990); dc.computeScreenConfiguration(config, ROTATION_0); + dc.onRequestedOverrideConfigurationChanged(config); assertEquals(Configuration.ORIENTATION_LANDSCAPE, config.orientation); + assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getNaturalOrientation()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 50fe0425fe9c..1fb7cd8e6e1c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -553,7 +553,7 @@ public class DragDropControllerTests extends WindowTestsBase { assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(), new InputChannel(), true /* isDragDrop */)); mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient, - flag, surface, 0, 0, 0, 0, 0, data); + flag, surface, 0, 0, 0, 0, 0, 0, 0, data); assertNotNull(mToken); r.run(); @@ -575,7 +575,7 @@ public class DragDropControllerTests extends WindowTestsBase { private void startA11yDrag(int flags, ClipData data, Runnable r) { mToken = mTarget.performDrag(0, 0, mWindow.mClient, - flags | View.DRAG_FLAG_ACCESSIBILITY_ACTION, null, 0, 0, 0, 0, 0, data); + flags | View.DRAG_FLAG_ACCESSIBILITY_ACTION, null, 0, 0, 0, 0, 0, 0, 0, data); assertNotNull(mToken); r.run(); } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 3d2340cca378..72db7fecb8ac 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -2278,6 +2278,12 @@ public class UsageStatsService extends SystemService implements "Only the system or holders of the REPORT_USAGE_STATS" + " permission are allowed to call reportUserInteraction"); } + if (userId != UserHandle.getCallingUserId()) { + // Cross-user event reporting. + getContext().enforceCallingPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "Caller doesn't have INTERACT_ACROSS_USERS_FULL permission"); + } } else { if (!isCallingUidSystem()) { throw new SecurityException("Only system is allowed to call" @@ -2287,7 +2293,8 @@ public class UsageStatsService extends SystemService implements // Verify if this package exists before reporting an event for it. if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) < 0) { - throw new IllegalArgumentException("Package " + packageName + "not exist!"); + throw new IllegalArgumentException("Package " + packageName + + " does not exist!"); } final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime()); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index b214591610de..c902d4597571 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -1569,13 +1569,13 @@ public class VoiceInteractionManagerService extends SystemService { @Override @EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION) - public void setIsReceiveSandboxedTrainingDataAllowed(boolean allowed) { - super.setIsReceiveSandboxedTrainingDataAllowed_enforcePermission(); + public void setShouldReceiveSandboxedTrainingData(boolean allowed) { + super.setShouldReceiveSandboxedTrainingData_enforcePermission(); synchronized (this) { if (mImpl == null) { throw new IllegalStateException( - "setIsReceiveSandboxedTrainingDataAllowed without running voice " + "setShouldReceiveSandboxedTrainingData without running voice " + "interaction service"); } @@ -2273,9 +2273,9 @@ public class VoiceInteractionManagerService extends SystemService { private boolean isCallerPreinstalledAssistant() { return mImpl != null - && mImpl.mInfo.getServiceInfo().applicationInfo.uid == Binder.getCallingUid() - && (mImpl.mInfo.getServiceInfo().applicationInfo.isSystemApp() - || mImpl.mInfo.getServiceInfo().applicationInfo.isUpdatedSystemApp()); + && mImpl.getApplicationInfo().uid == Binder.getCallingUid() + && (mImpl.getApplicationInfo().isSystemApp() + || mImpl.getApplicationInfo().isUpdatedSystemApp()); } private void setImplLocked(VoiceInteractionManagerServiceImpl impl) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 3c4b58fa2b69..7e0cbad1e828 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -40,6 +40,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; @@ -540,6 +541,10 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne return mInfo.getSupportsLocalInteraction(); } + public ApplicationInfo getApplicationInfo() { + return mInfo.getServiceInfo().applicationInfo; + } + public void startListeningVisibleActivityChangedLocked(@NonNull IBinder token) { if (DEBUG) { Slog.d(TAG, "startListeningVisibleActivityChangedLocked: token=" + token); diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java index eac4d1682aa9..cc768bc00250 100644 --- a/telephony/java/android/telephony/CarrierRestrictionRules.java +++ b/telephony/java/android/telephony/CarrierRestrictionRules.java @@ -16,12 +16,16 @@ package android.telephony; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.service.carrier.CarrierIdentifier; +import android.telephony.TelephonyManager.CarrierRestrictionStatus; + +import com.android.internal.telephony.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -104,7 +108,7 @@ public final class CarrierRestrictionRules implements Parcelable { private int mCarrierRestrictionDefault; @MultiSimPolicy private int mMultiSimPolicy; - @TelephonyManager.CarrierRestrictionStatus + @CarrierRestrictionStatus private int mCarrierRestrictionStatus; private CarrierRestrictionRules() { @@ -293,8 +297,22 @@ public final class CarrierRestrictionRules implements Parcelable { return true; } - /** @hide */ - public int getCarrierRestrictionStatus() { + /** + * Get the carrier restriction status of the device. + * The return value of the API is as follows. + * <ul> + * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER} + * if the caller and the device locked by the network are same</li> + * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_RESTRICTED} if the + * caller and the device locked by the network are different</li> + * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED} if the + * device is not locked</li> + * <li>return {@link TelephonyManager#CARRIER_RESTRICTION_STATUS_UNKNOWN} if the device + * locking state is unavailable or radio does not supports the feature</li> + * </ul> + */ + @FlaggedApi(Flags.FLAG_CARRIER_RESTRICTION_STATUS) + public @CarrierRestrictionStatus int getCarrierRestrictionStatus() { return mCarrierRestrictionStatus; } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index b356fde53417..326b6f5af613 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1508,8 +1508,14 @@ public class SubscriptionManager { public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) { if (listener == null) return; - addOnSubscriptionsChangedListener( - new HandlerExecutor(new Handler(listener.getCreatorLooper())), listener); + Looper looper = listener.getCreatorLooper(); + if (looper == null) { + throw new RuntimeException( + "Can't create handler inside thread " + Thread.currentThread() + + " that has not called Looper.prepare()"); + } + + addOnSubscriptionsChangedListener(new HandlerExecutor(new Handler(looper)), listener); } /** diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 3e8787281f85..8679bd4baf2e 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1866,7 +1866,7 @@ public class ApnSetting implements Parcelable { private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT; private boolean mAlwaysOn; - private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR; + private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR | INFRASTRUCTURE_SATELLITE; private boolean mEsimBootstrapProvisioning; /** diff --git a/telephony/java/android/telephony/satellite/NtnSignalStrength.java b/telephony/java/android/telephony/satellite/NtnSignalStrength.java index 16d765455d21..2fec423d1d65 100644 --- a/telephony/java/android/telephony/satellite/NtnSignalStrength.java +++ b/telephony/java/android/telephony/satellite/NtnSignalStrength.java @@ -86,6 +86,9 @@ public final class NtnSignalStrength implements Parcelable { readFromParcel(in); } + /** + * Returns notified non-terrestrial network signal strength level. + */ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) @NtnSignalStrengthLevel public int getLevel() { return mLevel; diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 71786b308937..e09bd201f93e 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -34,6 +34,7 @@ import android.os.ICancellationSignal; import android.os.OutcomeReceiver; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.ServiceSpecificException; import android.telephony.SubscriptionManager; import android.telephony.TelephonyCallback; import android.telephony.TelephonyFrameworkInitializer; @@ -1919,7 +1920,6 @@ public final class SatelliteManager { */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - @NonNull public void requestNtnSignalStrength(@NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<NtnSignalStrength, SatelliteException> callback) { Objects.requireNonNull(executor); @@ -1962,6 +1962,8 @@ public final class SatelliteManager { /** * Registers for NTN signal strength changed from satellite modem. + * If the registration operation is not successful, a {@link SatelliteException} that contains + * {@link SatelliteResult} will be thrown. * * <p> * Note: This API is specifically designed for OEM enabled satellite connectivity only. @@ -1973,16 +1975,14 @@ public final class SatelliteManager { * @param executor The executor on which the callback will be called. * @param callback The callback to handle the NTN signal strength changed event. * - * @return The {@link SatelliteResult} result of the operation. - * * @throws SecurityException if the caller doesn't have required permission. * @throws IllegalStateException if the Telephony process is not currently available. + * @throws SatelliteException if the callback registration operation fails. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) - @SatelliteResult public int registerForNtnSignalStrengthChanged( - @NonNull @CallbackExecutor Executor executor, - @NonNull NtnSignalStrengthCallback callback) { + public void registerForNtnSignalStrengthChanged(@NonNull @CallbackExecutor Executor executor, + @NonNull NtnSignalStrengthCallback callback) throws SatelliteException { Objects.requireNonNull(executor); Objects.requireNonNull(callback); @@ -1999,16 +1999,18 @@ public final class SatelliteManager { ntnSignalStrength))); } }; + telephony.registerForNtnSignalStrengthChanged(mSubId, internalCallback); sNtnSignalStrengthCallbackMap.put(callback, internalCallback); - return telephony.registerForNtnSignalStrengthChanged(mSubId, internalCallback); } else { throw new IllegalStateException("Telephony service is null."); } + } catch (ServiceSpecificException ex) { + logd("registerForNtnSignalStrengthChanged() registration fails: " + ex.errorCode); + throw new SatelliteException(ex.errorCode); } catch (RemoteException ex) { loge("registerForNtnSignalStrengthChanged() RemoteException: " + ex); ex.rethrowFromSystemServer(); } - return SATELLITE_RESULT_REQUEST_FAILED; } /** @@ -2025,6 +2027,8 @@ public final class SatelliteManager { * {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}. * * @throws SecurityException if the caller doesn't have required permission. + * @throws IllegalArgumentException if the callback is not valid or has already been + * unregistered. * @throws IllegalStateException if the Telephony process is not currently available. */ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) @@ -2041,6 +2045,7 @@ public final class SatelliteManager { telephony.unregisterForNtnSignalStrengthChanged(mSubId, internalCallback); } else { loge("unregisterForNtnSignalStrengthChanged: No internal callback."); + throw new IllegalArgumentException("callback is not valid"); } } else { throw new IllegalStateException("Telephony service is null."); diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index d7d28a17e7f2..397fb2d6db96 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -3101,16 +3101,21 @@ interface ITelephony { void requestNtnSignalStrength(int subId, in ResultReceiver receiver); /** - * Registers for NTN signal strength changed from satellite modem. + * Registers for NTN signal strength changed from satellite modem. If the registration operation + * is not successful, a {@link SatelliteException} that contains {@link SatelliteResult} will be + * thrown. * * @param subId The subId of the subscription to request for. - * @param callback The callback to handle the NTN signal strength changed event. - * - * @return The {@link SatelliteResult} result of the operation. + * @param callback The callback to handle the NTN signal strength changed event. If the + * operation is successful, {@link NtnSignalStrengthCallback#onNtnSignalStrengthChanged( + * NtnSignalStrength)} will return an instance of {@link NtnSignalStrength} with a value of + * {@link NtnSignalStrength.NtnSignalStrengthLevel} when the signal strength of non-terrestrial + * network has changed. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForNtnSignalStrengthChanged(int subId, in INtnSignalStrengthCallback callback); + void registerForNtnSignalStrengthChanged(int subId, + in INtnSignalStrengthCallback callback); /** * Unregisters for NTN signal strength changed from satellite modem. diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.bp b/tools/aapt2/integration-tests/SymlinkTest/Android.bp index 15a6a207d6d1..6fcdf1c77704 100644 --- a/tools/aapt2/integration-tests/SymlinkTest/Android.bp +++ b/tools/aapt2/integration-tests/SymlinkTest/Android.bp @@ -27,4 +27,7 @@ android_test { name: "AaptSymlinkTest", sdk_version: "current", use_resource_processor: false, + compile_data: [ + "targets/*", + ], } diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java index 068dfe8f3d11..a1356237e5a4 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java @@ -100,6 +100,10 @@ public class LongArrayMultiStateCounter_host { mLastStateChangeTimestampMs = timestampMs; } + public void setValue(int state, long[] values) { + System.arraycopy(values, 0, mStates[state].mCounter, 0, mArrayLength); + } + public void updateValue(long[] values, long timestampMs) { if (mEnabled || mLastUpdateTimestampMs < mLastStateChangeTimestampMs) { if (timestampMs < mLastStateChangeTimestampMs) { @@ -306,6 +310,11 @@ public class LongArrayMultiStateCounter_host { return getInstance(instanceId).mArrayLength; } + public static void native_setValues(long instanceId, int state, long containerInstanceId) { + getInstance(instanceId).setValue(state, + LongArrayContainer_host.getInstance(containerInstanceId)); + } + public static void native_updateValues(long instanceId, long containerInstanceId, long timestampMs) { getInstance(instanceId).updateValue( diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java index 3bcabcb01c5e..d63bff6f4da3 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java @@ -343,6 +343,28 @@ public class Parcel_host { p.mPos += length; p.updateSize(); } + public static int nativeCompareData(long thisNativePtr, long otherNativePtr) { + var a = getInstance(thisNativePtr); + var b = getInstance(otherNativePtr); + if ((a.mSize == b.mSize) && Arrays.equals(a.mBuffer, b.mBuffer)) { + return 0; + } else { + return -1; + } + } + public static boolean nativeCompareDataInRange( + long ptrA, int offsetA, long ptrB, int offsetB, int length) { + var a = getInstance(ptrA); + var b = getInstance(ptrB); + if (offsetA < 0 || offsetA + length > a.mSize) { + throw new IllegalArgumentException(); + } + if (offsetB < 0 || offsetB + length > b.mSize) { + throw new IllegalArgumentException(); + } + return Arrays.equals(Arrays.copyOfRange(a.mBuffer, offsetA, offsetA + length), + Arrays.copyOfRange(b.mBuffer, offsetB, offsetB + length)); + } public static void nativeAppendFrom( long thisNativePtr, long otherNativePtr, int srcOffset, int length) { var dst = getInstance(thisNativePtr); |