diff options
248 files changed, 7135 insertions, 3596 deletions
diff --git a/Android.bp b/Android.bp index 5af77562de85..06f3666c9aab 100644 --- a/Android.bp +++ b/Android.bp @@ -330,6 +330,7 @@ filegroup { ":gatekeeper_aidl", ":gsiservice_aidl", ":incidentcompanion_aidl", + ":inputconstants_aidl", ":installd_aidl", ":keystore_aidl", ":libaudioclient_aidl", @@ -598,6 +599,12 @@ java_library { "//frameworks/base/apex/jobscheduler/framework", "//frameworks/base/packages/Tethering/tests/unit", ], + errorprone: { + javacflags: [ + "-Xep:AndroidFrameworkCompatChange:ERROR", + "-Xep:AndroidFrameworkUid:ERROR", + ], + }, } // This "framework" module is NOT installed to the device. It's diff --git a/StubLibraries.bp b/StubLibraries.bp index a3a209458679..53053ce87bbe 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -58,7 +58,24 @@ stubs_defaults { "media/aidl", ], }, - libs: ["framework-internal-utils"], + // These are libs from framework-internal-utils that are required (i.e. being referenced) + // from framework-non-updatable-sources. Add more here when there's a need. + // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular + // dependencies gets bigger. + libs: [ + "android.hardware.cas-V1.2-java", + "android.hardware.health-V1.0-java-constants", + "android.hardware.radio-V1.5-java", + "android.hardware.thermal-V1.0-java-constants", + "android.hardware.thermal-V2.0-java", + "android.hardware.tv.input-V1.0-java-constants", + "android.hardware.tv.tuner-V1.0-java-constants", + "android.hardware.usb-V1.0-java-constants", + "android.hardware.usb-V1.1-java-constants", + "android.hardware.usb.gadget-V1.0-java", + "android.hardware.vibrator-V1.3-java", + "framework-protos", + ], installable: false, annotations_enabled: true, previous_api: ":android.api.public.latest", @@ -143,6 +160,11 @@ droidstubs { api_file: "non-updatable-api/current.txt", removed_api_file: "non-updatable-api/removed.txt", }, + last_released: { + api_file: ":android-non-updatable.api.public.latest", + removed_api_file: ":android-non-updatable-removed.api.public.latest", + baseline_file: ":public-api-incompatibilities-with-last-released", + }, api_lint: { enabled: true, new_since: ":android-non-updatable.api.public.latest", @@ -205,6 +227,11 @@ droidstubs { api_file: "non-updatable-api/system-current.txt", removed_api_file: "non-updatable-api/system-removed.txt", }, + last_released: { + api_file: ":android-non-updatable.api.system.latest", + removed_api_file: ":android-non-updatable-removed.api.system.latest", + baseline_file: ":system-api-incompatibilities-with-last-released" + }, api_lint: { enabled: true, new_since: ":android-non-updatable.api.system.latest", diff --git a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java index bb6b691226d6..66b2b0ecb21d 100644 --- a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java +++ b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java @@ -53,19 +53,19 @@ public class CanvasDrawTextTest { final String text = mTextUtil.nextRandomParagraph( WORD_LENGTH, 4 * 1024 * 1024 /* 4mb text */).toString(); final RenderNode node = RenderNode.create("benchmark", null); - final RenderNode child = RenderNode.create("child", null); - child.setLeftTopRightBottom(50, 50, 100, 100); - - RecordingCanvas canvas = node.start(100, 100); - node.end(canvas); - canvas = child.start(50, 50); - child.end(canvas); final Random r = new Random(0); - while (state.keepRunning()) { + state.pauseTiming(); + RecordingCanvas canvas = node.beginRecording(); int start = r.nextInt(text.length() - 100); + state.resumeTiming(); + canvas.drawText(text, start, start + 100, 0, 0, PAINT); + + state.pauseTiming(); + node.endRecording(); + state.resumeTiming(); } } } diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java index efcabd817109..833cc0ff37a0 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java @@ -226,7 +226,7 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase mMeasuredTimeNs = 0; final long startTime = SystemClock.elapsedRealtimeNanos(); - atm.startRecentsActivity(sRecentsIntent, null /* unused */, anim); + atm.startRecentsActivity(sRecentsIntent, 0 /* eventTime */, anim); final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime; mMeasuredTimeNs += elapsedTimeNsOfStart; state.addExtraResult("start", elapsedTimeNsOfStart); diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java index d955289e5f49..a9d57167f6d2 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java @@ -115,7 +115,7 @@ public class WmPerfRunListener extends RunListener { runWithShellPermissionIdentity(() -> { final ActivityTaskManager atm = mContext.getSystemService(ActivityTaskManager.class); atm.removeAllVisibleRecentTasks(); - atm.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD, + atm.removeRootTasksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED }); }); PhoneWindow.sendCloseSystemWindows(mContext, "WmPerfTests"); diff --git a/apex/media/framework/api/module-lib-current.txt b/apex/media/framework/api/module-lib-current.txt index d2826d01664c..2b69863675a5 100644 --- a/apex/media/framework/api/module-lib-current.txt +++ b/apex/media/framework/api/module-lib-current.txt @@ -1,14 +1,14 @@ // Signature format: 2.0 package android.media { - public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable { - ctor public MediaParceledListSlice(@NonNull java.util.List<T>); - method public int describeContents(); - method @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList(); - method public java.util.List<T> getList(); - method public void setInlineCountLimit(int); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR; + @Deprecated public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable { + ctor @Deprecated public MediaParceledListSlice(@NonNull java.util.List<T>); + method @Deprecated public int describeContents(); + method @Deprecated @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList(); + method @Deprecated public java.util.List<T> getList(); + method @Deprecated public void setInlineCountLimit(int); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR; } } diff --git a/apex/media/framework/java/android/media/MediaParceledListSlice.java b/apex/media/framework/java/android/media/MediaParceledListSlice.java index e1223f6a6bed..47ac193231a0 100644 --- a/apex/media/framework/java/android/media/MediaParceledListSlice.java +++ b/apex/media/framework/java/android/media/MediaParceledListSlice.java @@ -17,7 +17,6 @@ package android.media; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,13 +31,16 @@ import java.util.List; * Transfer a large list of Parcelable objects across an IPC. Splits into * multiple transactions if needed. * - * @see BaseMediaParceledListSlice - * * TODO: Remove this from @SystemApi once all the MediaSession related classes are moved * to apex (or ParceledListSlice moved to apex). This class is temporaily added to system API * for moving classes step by step. + * + * @param <T> The type of the elements in the list. + * @see BaseMediaParceledListSlice + * @deprecated This is temporary marked as @SystemApi. Should be removed from the API surface. * @hide */ +@Deprecated @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public final class MediaParceledListSlice<T extends Parcelable> extends BaseMediaParceledListSlice<T> { diff --git a/api/Android.bp b/api/Android.bp index 490c980cb7cb..e403082b1fb2 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -64,6 +64,24 @@ genrule { } genrule { + name: "frameworks-base-api-removed-merged.txt", + srcs: [ + ":conscrypt.module.public.api{.public.removed-api.txt}", + ":framework-media{.public.removed-api.txt}", + ":framework-mediaprovider{.public.removed-api.txt}", + ":framework-permission{.public.removed-api.txt}", + ":framework-sdkextensions{.public.removed-api.txt}", + ":framework-statsd{.public.removed-api.txt}", + ":framework-tethering{.public.removed-api.txt}", + ":framework-wifi{.public.removed-api.txt}", + ":non-updatable-removed.txt", + ], + out: ["removed.txt"], + tools: ["metalava"], + cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", +} + +genrule { name: "frameworks-base-api-system-current-merged.txt", srcs: [ ":framework-graphics{.system.api.txt}", @@ -82,6 +100,23 @@ genrule { } genrule { + name: "frameworks-base-api-system-removed-merged.txt", + srcs: [ + ":framework-media{.system.removed-api.txt}", + ":framework-mediaprovider{.system.removed-api.txt}", + ":framework-permission{.system.removed-api.txt}", + ":framework-sdkextensions{.system.removed-api.txt}", + ":framework-statsd{.system.removed-api.txt}", + ":framework-tethering{.system.removed-api.txt}", + ":framework-wifi{.system.removed-api.txt}", + ":non-updatable-system-removed.txt", + ], + out: ["system-removed.txt"], + tools: ["metalava"], + cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", +} + +genrule { name: "frameworks-base-api-module-lib-current-merged.txt", srcs: [ ":framework-graphics{.module-lib.api.txt}", @@ -98,3 +133,20 @@ genrule { tools: ["metalava"], cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", } + +genrule { + name: "frameworks-base-api-module-lib-removed-merged.txt", + srcs: [ + ":framework-media{.module-lib.removed-api.txt}", + ":framework-mediaprovider{.module-lib.removed-api.txt}", + ":framework-permission{.module-lib.removed-api.txt}", + ":framework-sdkextensions{.module-lib.removed-api.txt}", + ":framework-statsd{.module-lib.removed-api.txt}", + ":framework-tethering{.module-lib.removed-api.txt}", + ":framework-wifi{.module-lib.removed-api.txt}", + ":non-updatable-module-lib-removed.txt", + ], + out: ["module-lib-removed.txt"], + tools: ["metalava"], + cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)", +} diff --git a/api/current.txt b/api/current.txt index 94e90a75f644..153cf69e1546 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11454,10 +11454,10 @@ package android.content.pm { public final class ApkChecksum implements android.os.Parcelable { method public int describeContents(); - method public int getKind(); - method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException; - method @Nullable public String getSourcePackageName(); + method @Nullable public java.security.cert.Certificate getInstallerCertificate() throws java.security.cert.CertificateException; + method @Nullable public String getInstallerPackageName(); method @Nullable public String getSplitName(); + method public int getType(); method @NonNull public byte[] getValue(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ApkChecksum> CREATOR; @@ -11567,17 +11567,17 @@ package android.content.pm { public final class Checksum implements android.os.Parcelable { ctor public Checksum(int, @NonNull byte[]); method public int describeContents(); - method public int getKind(); + method public int getType(); method @NonNull public byte[] getValue(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Checksum> CREATOR; - field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20 - field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40 - field @Deprecated public static final int WHOLE_MD5 = 2; // 0x2 - field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1 - field @Deprecated public static final int WHOLE_SHA1 = 4; // 0x4 - field public static final int WHOLE_SHA256 = 8; // 0x8 - field public static final int WHOLE_SHA512 = 16; // 0x10 + field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20 + field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40 + field @Deprecated public static final int TYPE_WHOLE_MD5 = 2; // 0x2 + field public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1 + field @Deprecated public static final int TYPE_WHOLE_SHA1 = 4; // 0x4 + field @Deprecated public static final int TYPE_WHOLE_SHA256 = 8; // 0x8 + field @Deprecated public static final int TYPE_WHOLE_SHA512 = 16; // 0x10 } public class ComponentInfo extends android.content.pm.PackageItemInfo { @@ -12027,7 +12027,6 @@ package android.content.pm { method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public CharSequence getBackgroundPermissionOptionLabel(); method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int); - method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName); method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo); @@ -12100,6 +12099,7 @@ package android.content.pm { method @Deprecated public abstract void removePackageFromPreferred(@NonNull String); method public abstract void removePermission(@NonNull String); method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int); + method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int); method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int); method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int); diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 3801e1d34b06..19f348c84062 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -53,14 +53,14 @@ package android.media { field public static final int FLAG_FROM_KEY = 4096; // 0x1000 } - public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable { - ctor public MediaParceledListSlice(@NonNull java.util.List<T>); - method public int describeContents(); - method @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList(); - method public java.util.List<T> getList(); - method public void setInlineCountLimit(int); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR; + @Deprecated public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable { + ctor @Deprecated public MediaParceledListSlice(@NonNull java.util.List<T>); + method @Deprecated public int describeContents(); + method @Deprecated @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList(); + method @Deprecated public java.util.List<T> getList(); + method @Deprecated public void setInlineCountLimit(int); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR; } } diff --git a/api/system-current.txt b/api/system-current.txt index 325175b369f3..8979471ab9a0 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -126,6 +126,7 @@ package android { field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY"; field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER"; field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS"; + field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION"; field public static final String MANAGE_USB = "android.permission.MANAGE_USB"; field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS"; field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE"; @@ -306,6 +307,7 @@ package android { field public static final int config_helpPackageNameValue = 17039388; // 0x104001c field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemGallery = 17039399; // 0x1040027 + field public static final int config_systemVideoCall = 17039401; // 0x1040029 } public static final class R.style { @@ -1390,6 +1392,57 @@ package android.app.role { } +package android.app.time { + + public final class TimeManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void addTimeZoneDetectorListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.time.TimeManager.TimeZoneDetectorListener); + method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public android.app.time.TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig(); + method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void removeTimeZoneDetectorListener(@NonNull android.app.time.TimeManager.TimeZoneDetectorListener); + method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public boolean updateTimeZoneConfiguration(@NonNull android.app.time.TimeZoneConfiguration); + } + + @java.lang.FunctionalInterface public static interface TimeManager.TimeZoneDetectorListener { + method public void onChange(); + } + + public final class TimeZoneCapabilities implements android.os.Parcelable { + method public int describeContents(); + method public int getConfigureAutoDetectionEnabledCapability(); + method public int getConfigureGeoDetectionEnabledCapability(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPABILITY_NOT_ALLOWED = 20; // 0x14 + field public static final int CAPABILITY_NOT_APPLICABLE = 30; // 0x1e + field public static final int CAPABILITY_NOT_SUPPORTED = 10; // 0xa + field public static final int CAPABILITY_POSSESSED = 40; // 0x28 + field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilities> CREATOR; + } + + public final class TimeZoneCapabilitiesAndConfig implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.app.time.TimeZoneCapabilities getCapabilities(); + method @NonNull public android.app.time.TimeZoneConfiguration getConfiguration(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilitiesAndConfig> CREATOR; + } + + public final class TimeZoneConfiguration implements android.os.Parcelable { + method public int describeContents(); + method public boolean isAutoDetectionEnabled(); + method public boolean isGeoDetectionEnabled(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneConfiguration> CREATOR; + } + + public static final class TimeZoneConfiguration.Builder { + ctor public TimeZoneConfiguration.Builder(); + ctor public TimeZoneConfiguration.Builder(@NonNull android.app.time.TimeZoneConfiguration); + method @NonNull public android.app.time.TimeZoneConfiguration build(); + method @NonNull public android.app.time.TimeZoneConfiguration.Builder setAutoDetectionEnabled(boolean); + method @NonNull public android.app.time.TimeZoneConfiguration.Builder setGeoDetectionEnabled(boolean); + } + +} + package android.app.usage { public final class CacheQuotaHint implements android.os.Parcelable { @@ -4118,7 +4171,7 @@ package android.location { method @Deprecated @NonNull public String getProvider(); method public int getQuality(); method @Deprecated public float getSmallestDisplacement(); - method @Nullable public android.os.WorkSource getWorkSource(); + method @NonNull public android.os.WorkSource getWorkSource(); method public boolean isHiddenFromAppOps(); method public boolean isLocationSettingsIgnored(); method public boolean isLowPower(); diff --git a/api/test-current.txt b/api/test-current.txt index 68d28233ade0..25a471086409 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -45,6 +45,7 @@ package android { field public static final int config_defaultDialer = 17039395; // 0x1040023 field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemGallery = 17039399; // 0x1040027 + field public static final int config_systemVideoCall = 17039401; // 0x1040029 } } @@ -135,12 +136,12 @@ package android.app { public class ActivityTaskManager { method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void clearLaunchParamsForPackages(java.util.List<java.lang.String>); method public static boolean currentUiModeSupportsErrorDialogs(@NonNull android.content.Context); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void moveTaskToStack(int, int, boolean); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException; - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException; + method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void moveTaskToRootTask(int, int, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean moveTopActivityToPinnedRootTask(int, @NonNull android.graphics.Rect); + method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeRootTasksInWindowingModes(@NonNull int[]); + method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeRootTasksWithActivityTypes(@NonNull int[]); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect); + method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePrimarySplitScreen(@NonNull android.graphics.Rect, @NonNull android.graphics.Rect); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException; @@ -1452,6 +1453,18 @@ package android.hardware.hdmi { } +package android.hardware.input { + + public final class InputManager { + method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context); + method public float getMaximumObscuringOpacityForTouch(@NonNull android.content.Context); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, float); + field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL + } + +} + package android.hardware.lights { public final class Light implements android.os.Parcelable { @@ -1716,7 +1729,7 @@ package android.location { } public final class LocationRequest implements android.os.Parcelable { - method @Nullable public android.os.WorkSource getWorkSource(); + method @NonNull public android.os.WorkSource getWorkSource(); method public boolean isHiddenFromAppOps(); method public boolean isLocationSettingsIgnored(); method public boolean isLowPower(); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 42b560d87dd0..bda9e24d31fb 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3777,6 +3777,7 @@ message AppStartOccurred { LAUNCHER = 1; NOTIFICATION = 2; LOCKSCREEN = 3; + RECENTS_ANIMATION = 4; } // The type of the startup source. optional SourceType source_type = 16; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 3dbb6ed47ff8..a8ef54a335c4 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -24,6 +24,7 @@ #include <stdlib.h> #include "guardrail/StatsdStats.h" +#include "metrics/parsing_utils/metrics_manager_util.h" #include "stats_log_util.h" #include "stats_util.h" @@ -122,6 +123,47 @@ CountMetricProducer::~CountMetricProducer() { VLOG("~CountMetricProducer() called"); } +bool CountMetricProducer::onConfigUpdatedLocked( + const StatsdConfig& config, const int configIndex, const int metricIndex, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const sp<EventMatcherWizard>& matcherWizard, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, + const unordered_map<int64_t, int>& metricToActivationMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { + if (!MetricProducer::onConfigUpdatedLocked( + config, configIndex, metricIndex, allAtomMatchingTrackers, + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, + allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, + trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation)) { + return false; + } + + const CountMetric& metric = config.count_metric(configIndex); + int trackerIndex; + // Update appropriate indices, specifically mConditionIndex and MetricsManager maps. + if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false, + allAtomMatchingTrackers, newAtomMatchingTrackerMap, + trackerToMetricMap, trackerIndex)) { + return false; + } + + if (metric.has_condition() && + !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, mConditionTrackerIndex, + conditionToMetricMap)) { + return false; + } + return true; +} + void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId, const HashableDimensionKey& primaryKey, const FieldValue& oldState, const FieldValue& newState) { diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 6b2f2ca61ecc..0769ac459b8c 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -97,6 +97,22 @@ private: void flushCurrentBucketLocked(const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) override; + bool onConfigUpdatedLocked( + const StatsdConfig& config, const int configIndex, const int metricIndex, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const sp<EventMatcherWizard>& matcherWizard, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionTrackerMap, + const sp<ConditionWizard>& wizard, + const std::unordered_map<int64_t, int>& metricToActivationMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation) override; + std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets; // The current bucket (may be a partial bucket). diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 320b28581a38..4360010746ae 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -566,7 +566,9 @@ protected: FRIEND_TEST(MetricsManagerTest, TestInitialConditions); FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations); + FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics); FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics); + FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp index 12d3b9487635..5ff69753a4a3 100644 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp @@ -587,6 +587,51 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 // Now, perform the update. Must iterate the metric types in the same order int metricIndex = 0; + for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) { + newMetricProducerMap[config.count_metric(i).id()] = metricIndex; + const CountMetric& metric = config.count_metric(i); + switch (metricsToUpdate[metricIndex]) { + case UPDATE_PRESERVE: { + const auto& oldMetricProducerIt = oldMetricProducerMap.find(metric.id()); + if (oldMetricProducerIt == oldMetricProducerMap.end()) { + ALOGE("Could not find Metric %lld in the previous config, but expected it " + "to be there", + (long long)metric.id()); + return false; + } + const int oldIndex = oldMetricProducerIt->second; + sp<MetricProducer> producer = oldMetricProducers[oldIndex]; + producer->onConfigUpdated( + config, i, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap, + newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers, + conditionTrackerMap, wizard, metricToActivationMap, trackerToMetricMap, + conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation); + newMetricProducers.push_back(producer); + break; + } + case UPDATE_REPLACE: + case UPDATE_NEW: { + sp<MetricProducer> producer = createCountMetricProducerAndUpdateMetadata( + key, config, timeBaseNs, currentTimeNs, metric, metricIndex, + allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, + conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, + allStateGroupMaps, metricToActivationMap, trackerToMetricMap, + conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation); + if (producer == nullptr) { + return false; + } + newMetricProducers.push_back(producer); + break; + } + default: { + ALOGE("Metric \"%lld\" update state is unknown. This should never happen", + (long long)metric.id()); + return false; + } + } + } for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { newMetricProducerMap[config.event_metric(i).id()] = metricIndex; const EventMetric& metric = config.event_metric(i); diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp index 51df7df5174e..98c6222c4326 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -348,6 +348,81 @@ bool handleMetricActivationOnConfigUpdate( return true; } +sp<MetricProducer> createCountMetricProducerAndUpdateMetadata( + const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, + const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& conditionTrackerMap, + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const unordered_map<int64_t, int>& stateAtomIdMap, + const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + const unordered_map<int64_t, int>& metricToActivationMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { + if (!metric.has_id() || !metric.has_what()) { + ALOGW("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id()); + return nullptr; + } + int trackerIndex; + if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, trackerIndex)) { + return nullptr; + } + + int conditionIndex = -1; + if (metric.has_condition()) { + if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, conditionIndex, + conditionToMetricMap)) { + return nullptr; + } + } else { + if (metric.links_size() > 0) { + ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); + return nullptr; + } + } + + std::vector<int> slicedStateAtoms; + unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; + if (metric.slice_by_state_size() > 0) { + if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, + allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { + return nullptr; + } + } else { + if (metric.state_link_size() > 0) { + ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state"); + return nullptr; + } + } + + unordered_map<int, shared_ptr<Activation>> eventActivationMap; + unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, + atomMatchingTrackerMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + eventActivationMap, eventDeactivationMap)) { + return nullptr; + } + + uint64_t metricHash; + if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + return nullptr; + } + + return new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard, + metricHash, timeBaseNs, currentTimeNs, eventActivationMap, + eventDeactivationMap, slicedStateAtoms, stateGroupMap); +} + sp<MetricProducer> createEventMetricProducerAndUpdateMetadata( const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, const EventMetric& metric, const int metricIndex, @@ -550,68 +625,20 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t // Build MetricProducers for each metric defined in config. // build CountMetricProducer for (int i = 0; i < config.count_metric_size(); i++) { - const CountMetric& metric = config.count_metric(i); - if (!metric.has_what()) { - ALOGW("cannot find \"what\" in CountMetric \"%lld\"", (long long)metric.id()); - return false; - } - int metricIndex = allMetricProducers.size(); + const CountMetric& metric = config.count_metric(i); metricMap.insert({metric.id(), metricIndex}); - int trackerIndex; - if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndex)) { - return false; - } - - int conditionIndex = -1; - if (metric.has_condition()) { - if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap)) { - return false; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return false; - } - } - - std::vector<int> slicedStateAtoms; - unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; - if (metric.slice_by_state_size() > 0) { - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { - return false; - } - } else { - if (metric.state_link_size() > 0) { - ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state"); - return false; - } - } - - unordered_map<int, shared_ptr<Activation>> eventActivationMap; - unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - bool success = handleMetricActivation( - config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, + sp<MetricProducer> producer = createCountMetricProducerAndUpdateMetadata( + key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex, + allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers, + conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, + allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap); - if (!success) return false; - - uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + metricsWithActivation); + if (producer == nullptr) { return false; } - - sp<MetricProducer> countProducer = new CountMetricProducer( - key, metric, conditionIndex, initialConditionCache, wizard, metricHash, - timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap, - slicedStateAtoms, stateGroupMap); - allMetricProducers.push_back(countProducer); + allMetricProducers.push_back(producer); } // build DurationMetricProducer diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h index fdfe5cfa6491..f6b2b7d1602c 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h @@ -93,6 +93,27 @@ bool handleMetricActivationOnConfigUpdate( std::unordered_map<int, shared_ptr<Activation>>& newEventActivationMap, std::unordered_map<int, std::vector<shared_ptr<Activation>>>& newEventDeactivationMap); +// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with +// the appropriate indices. Returns an sp to the producer, or null if there was an error. +sp<MetricProducer> createCountMetricProducerAndUpdateMetadata( + const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, + const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionTrackerMap, + const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const std::unordered_map<int64_t, int>& stateAtomIdMap, + const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps, + const std::unordered_map<int64_t, int>& metricToActivationMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation); + +// Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with +// the appropriate indices. Returns an sp to the producer, or null if there was an error. sp<MetricProducer> createEventMetricProducerAndUpdateMetadata( const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, const EventMetric& metric, const int metricIndex, diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp index 3b346c11e831..9e6758785911 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp @@ -97,6 +97,7 @@ public: metricsWithActivation.clear(); oldStateHashes.clear(); noReportMetricIds.clear(); + StateManager::getInstance().clear(); } }; @@ -121,6 +122,21 @@ EventMetric createEventMetric(string name, int64_t what, optional<int64_t> condi return metric; } +CountMetric createCountMetric(string name, int64_t what, optional<int64_t> condition, + vector<int64_t> states) { + CountMetric metric; + metric.set_id(StringToId(name)); + metric.set_what(what); + metric.set_bucket(TEN_MINUTES); + if (condition) { + metric.set_condition(condition.value()); + } + for (const int64_t state : states) { + metric.add_slice_by_state(state); + } + return metric; +} + } // anonymous namespace TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) { @@ -1419,7 +1435,7 @@ TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) { EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(event1Id)], newMetricProducers[newMetricProducerMap.at(event1Id)]); - // Make sure replaced conditions are different and included in replacedConditions. + // Make sure replaced metrics are different. EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event2Id)], newMetricProducers[newMetricProducerMap.at(event2Id)]); EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event3Id)], @@ -1478,6 +1494,239 @@ TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) { EXPECT_EQ(oldConditionWizard->getStrongCount(), 1); } +TEST_F(ConfigUpdateTest, TestUpdateCountMetrics) { + StatsdConfig config; + + // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig. + AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); + int64_t matcher1Id = matcher1.id(); + *config.add_atom_matcher() = matcher1; + + AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); + int64_t matcher2Id = matcher2.id(); + *config.add_atom_matcher() = matcher2; + + AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher(); + int64_t matcher3Id = matcher3.id(); + *config.add_atom_matcher() = matcher3; + + AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher(); + int64_t matcher4Id = matcher4.id(); + *config.add_atom_matcher() = matcher4; + + AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher(); + int64_t matcher5Id = matcher5.id(); + *config.add_atom_matcher() = matcher5; + + Predicate predicate1 = CreateScreenIsOnPredicate(); + int64_t predicate1Id = predicate1.id(); + *config.add_predicate() = predicate1; + + State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321); + int64_t state1Id = state1.id(); + *config.add_state() = state1; + + State state2 = CreateScreenState(); + int64_t state2Id = state2.id(); + *config.add_state() = state2; + + // Add a few count metrics. + // Will be preserved. + CountMetric count1 = createCountMetric("COUNT1", matcher1Id, predicate1Id, {state1Id}); + int64_t count1Id = count1.id(); + *config.add_count_metric() = count1; + + // Will be replaced. + CountMetric count2 = createCountMetric("COUNT2", matcher2Id, nullopt, {}); + int64_t count2Id = count2.id(); + *config.add_count_metric() = count2; + + // Will be replaced. + CountMetric count3 = createCountMetric("COUNT3", matcher3Id, nullopt, {}); + int64_t count3Id = count3.id(); + *config.add_count_metric() = count3; + + // Will be replaced. + CountMetric count4 = createCountMetric("COUNT4", matcher4Id, nullopt, {state2Id}); + int64_t count4Id = count4.id(); + *config.add_count_metric() = count4; + + // Will be deleted. + CountMetric count5 = createCountMetric("COUNT5", matcher5Id, nullopt, {}); + int64_t count5Id = count5.id(); + *config.add_count_metric() = count5; + + EXPECT_TRUE(initConfig(config)); + + // Change bucket size of count2, causing it to be replaced. + count2.set_bucket(ONE_HOUR); + + // Mark matcher 3 as replaced. Causes count3 to be replaced. + set<int64_t> replacedMatchers; + replacedMatchers.insert(matcher3Id); + + // Mark state 2 as replaced and change the state to be about a different atom. + // Causes count4 to be replaced. + set<int64_t> replacedStates; + replacedStates.insert(state2Id); + state2.set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED); + + // Fake that predicate 1 is true for count metric 1. + ASSERT_EQ(oldMetricProducers[0]->getMetricId(), count1Id); + oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0); + EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue); + + EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 1); + // Tell the StateManager that the screen is on. + unique_ptr<LogEvent> event = + CreateScreenStateChangedEvent(0, android::view::DisplayStateEnum::DISPLAY_STATE_ON); + StateManager::getInstance().onLogEvent(*event); + + // New count metric. Should have an initial condition of true since it depends on predicate1. + CountMetric count6 = createCountMetric("EVENT6", matcher2Id, predicate1Id, {state1Id}); + int64_t count6Id = count6.id(); + + // Map the matchers and predicates in reverse order to force the indices to change. + std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; + const int matcher5Index = 0; + newAtomMatchingTrackerMap[matcher5Id] = 0; + const int matcher4Index = 1; + newAtomMatchingTrackerMap[matcher4Id] = 1; + const int matcher3Index = 2; + newAtomMatchingTrackerMap[matcher3Id] = 2; + const int matcher2Index = 3; + newAtomMatchingTrackerMap[matcher2Id] = 3; + const int matcher1Index = 4; + newAtomMatchingTrackerMap[matcher1Id] = 4; + // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5); + std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), + newAtomMatchingTrackers.begin()); + + std::unordered_map<int64_t, int> newConditionTrackerMap; + const int predicate1Index = 0; + newConditionTrackerMap[predicate1Id] = 0; + // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them. + vector<sp<ConditionTracker>> newConditionTrackers(1); + std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(), + newConditionTrackers.begin()); + // Fake that predicate1 is true for all new metrics. + vector<ConditionState> conditionCache = {ConditionState::kTrue}; + + StatsdConfig newConfig; + *newConfig.add_count_metric() = count6; + const int count6Index = 0; + *newConfig.add_count_metric() = count3; + const int count3Index = 1; + *newConfig.add_count_metric() = count1; + const int count1Index = 2; + *newConfig.add_count_metric() = count4; + const int count4Index = 3; + *newConfig.add_count_metric() = count2; + const int count2Index = 4; + + *newConfig.add_state() = state1; + *newConfig.add_state() = state2; + + unordered_map<int64_t, int> stateAtomIdMap; + unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; + map<int64_t, uint64_t> stateProtoHashes; + EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)); + EXPECT_EQ(stateAtomIdMap[state2Id], util::BATTERY_SAVER_MODE_STATE_CHANGED); + + // Output data structures to validate. + unordered_map<int64_t, int> newMetricProducerMap; + vector<sp<MetricProducer>> newMetricProducers; + unordered_map<int, vector<int>> conditionToMetricMap; + unordered_map<int, vector<int>> trackerToMetricMap; + set<int64_t> noReportMetricIds; + unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; + unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; + vector<int> metricsWithActivation; + EXPECT_TRUE(updateMetrics( + key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, + newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{}, + newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, + oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers, + conditionToMetricMap, trackerToMetricMap, noReportMetricIds, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation)); + + unordered_map<int64_t, int> expectedMetricProducerMap = { + {count1Id, count1Index}, {count2Id, count2Index}, {count3Id, count3Index}, + {count4Id, count4Index}, {count6Id, count6Index}, + }; + EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); + + // Make sure preserved metrics are the same. + ASSERT_EQ(newMetricProducers.size(), 5); + EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(count1Id)], + newMetricProducers[newMetricProducerMap.at(count1Id)]); + + // Make sure replaced metrics are different. + EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count2Id)], + newMetricProducers[newMetricProducerMap.at(count2Id)]); + EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count3Id)], + newMetricProducers[newMetricProducerMap.at(count3Id)]); + EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count4Id)], + newMetricProducers[newMetricProducerMap.at(count4Id)]); + + // Verify the conditionToMetricMap. + ASSERT_EQ(conditionToMetricMap.size(), 1); + const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index]; + EXPECT_THAT(condition1Metrics, UnorderedElementsAre(count1Index, count6Index)); + + // Verify the trackerToMetricMap. + ASSERT_EQ(trackerToMetricMap.size(), 4); + const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; + EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(count1Index)); + const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index]; + EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(count2Index, count6Index)); + const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index]; + EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(count3Index)); + const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index]; + EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(count4Index)); + + // Verify event activation/deactivation maps. + ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0); + ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0); + ASSERT_EQ(metricsWithActivation.size(), 0); + + // Verify tracker indices/ids/conditions/states are correct. + EXPECT_EQ(newMetricProducers[count1Index]->getMetricId(), count1Id); + EXPECT_EQ(newMetricProducers[count1Index]->mConditionTrackerIndex, predicate1Index); + EXPECT_EQ(newMetricProducers[count1Index]->mCondition, ConditionState::kTrue); + EXPECT_THAT(newMetricProducers[count1Index]->getSlicedStateAtoms(), + UnorderedElementsAre(util::SCREEN_STATE_CHANGED)); + EXPECT_EQ(newMetricProducers[count2Index]->getMetricId(), count2Id); + EXPECT_EQ(newMetricProducers[count2Index]->mConditionTrackerIndex, -1); + EXPECT_EQ(newMetricProducers[count2Index]->mCondition, ConditionState::kTrue); + EXPECT_TRUE(newMetricProducers[count2Index]->getSlicedStateAtoms().empty()); + EXPECT_EQ(newMetricProducers[count3Index]->getMetricId(), count3Id); + EXPECT_EQ(newMetricProducers[count3Index]->mConditionTrackerIndex, -1); + EXPECT_EQ(newMetricProducers[count3Index]->mCondition, ConditionState::kTrue); + EXPECT_TRUE(newMetricProducers[count3Index]->getSlicedStateAtoms().empty()); + EXPECT_EQ(newMetricProducers[count4Index]->getMetricId(), count4Id); + EXPECT_EQ(newMetricProducers[count4Index]->mConditionTrackerIndex, -1); + EXPECT_EQ(newMetricProducers[count4Index]->mCondition, ConditionState::kTrue); + EXPECT_THAT(newMetricProducers[count4Index]->getSlicedStateAtoms(), + UnorderedElementsAre(util::BATTERY_SAVER_MODE_STATE_CHANGED)); + EXPECT_EQ(newMetricProducers[count6Index]->getMetricId(), count6Id); + EXPECT_EQ(newMetricProducers[count6Index]->mConditionTrackerIndex, predicate1Index); + EXPECT_EQ(newMetricProducers[count6Index]->mCondition, ConditionState::kTrue); + EXPECT_THAT(newMetricProducers[count6Index]->getSlicedStateAtoms(), + UnorderedElementsAre(util::SCREEN_STATE_CHANGED)); + + oldMetricProducers.clear(); + // Ensure that the screen state StateTracker did not get deleted and replaced. + EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 2); + FieldValue screenState; + StateManager::getInstance().getStateValue(util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY, + &screenState); + EXPECT_EQ(screenState.mValue.int_value, android::view::DisplayStateEnum::DISPLAY_STATE_ON); +} + TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) { StatsdConfig config; // Add atom matchers @@ -1605,6 +1854,135 @@ TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) { EXPECT_THAT(producer->mEventDeactivationMap[matcher2Index], UnorderedElementsAre(matcher4Activation)); } + +TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) { + StatsdConfig config; + // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig + AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); + int64_t matcher1Id = matcher1.id(); + *config.add_atom_matcher() = matcher1; + + AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); + int64_t matcher2Id = matcher2.id(); + *config.add_atom_matcher() = matcher2; + + Predicate predicate1 = CreateScreenIsOnPredicate(); + int64_t predicate1Id = predicate1.id(); + *config.add_predicate() = predicate1; + + // Add a few count metrics. + // Will be preserved. + CountMetric countMetric = createCountMetric("COUNT1", matcher1Id, predicate1Id, {}); + int64_t countMetricId = countMetric.id(); + *config.add_count_metric() = countMetric; + + // Will be replaced since matcher2 is replaced. + EventMetric eventMetric = createEventMetric("EVENT1", matcher2Id, nullopt); + int64_t eventMetricId = eventMetric.id(); + *config.add_event_metric() = eventMetric; + + EXPECT_TRUE(initConfig(config)); + + // Used later to ensure the condition wizard is replaced. Get it before doing the update. + sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard; + EXPECT_EQ(oldConditionWizard->getStrongCount(), 3); + + // Mark matcher 2 as replaced. Causes eventMetric to be replaced. + set<int64_t> replacedMatchers; + replacedMatchers.insert(matcher2Id); + + // Map the matchers and predicates in reverse order to force the indices to change. + std::unordered_map<int64_t, int> newAtomMatchingTrackerMap; + const int matcher2Index = 0; + newAtomMatchingTrackerMap[matcher2Id] = 0; + const int matcher1Index = 1; + newAtomMatchingTrackerMap[matcher1Id] = 1; + // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(2); + std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), + newAtomMatchingTrackers.begin()); + + std::unordered_map<int64_t, int> newConditionTrackerMap; + const int predicate1Index = 0; + newConditionTrackerMap[predicate1Id] = 0; + // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them. + vector<sp<ConditionTracker>> newConditionTrackers(1); + std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(), + newConditionTrackers.begin()); + vector<ConditionState> conditionCache = {ConditionState::kUnknown}; + + StatsdConfig newConfig; + *newConfig.add_count_metric() = countMetric; + const int countMetricIndex = 0; + *newConfig.add_event_metric() = eventMetric; + const int eventMetricIndex = 1; + + // Output data structures to validate. + unordered_map<int64_t, int> newMetricProducerMap; + vector<sp<MetricProducer>> newMetricProducers; + unordered_map<int, vector<int>> conditionToMetricMap; + unordered_map<int, vector<int>> trackerToMetricMap; + set<int64_t> noReportMetricIds; + unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; + unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; + vector<int> metricsWithActivation; + EXPECT_TRUE(updateMetrics( + key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers, + newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{}, + newConditionTrackers, conditionCache, /*stateAtomIdMap*/ {}, /*allStateGroupMaps=*/{}, + /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, + newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation)); + + unordered_map<int64_t, int> expectedMetricProducerMap = { + {countMetricId, countMetricIndex}, + {eventMetricId, eventMetricIndex}, + }; + EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); + + // Make sure preserved metrics are the same. + ASSERT_EQ(newMetricProducers.size(), 2); + EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)], + newMetricProducers[newMetricProducerMap.at(countMetricId)]); + + // Make sure replaced metrics are different. + EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)], + newMetricProducers[newMetricProducerMap.at(eventMetricId)]); + + // Verify the conditionToMetricMap. + ASSERT_EQ(conditionToMetricMap.size(), 1); + const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index]; + EXPECT_THAT(condition1Metrics, UnorderedElementsAre(countMetricIndex)); + + // Verify the trackerToMetricMap. + ASSERT_EQ(trackerToMetricMap.size(), 2); + const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; + EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex)); + const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index]; + EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex)); + + // Verify event activation/deactivation maps. + ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0); + ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0); + ASSERT_EQ(metricsWithActivation.size(), 0); + + // Verify tracker indices/ids/conditions are correct. + EXPECT_EQ(newMetricProducers[countMetricIndex]->getMetricId(), countMetricId); + EXPECT_EQ(newMetricProducers[countMetricIndex]->mConditionTrackerIndex, predicate1Index); + EXPECT_EQ(newMetricProducers[countMetricIndex]->mCondition, ConditionState::kUnknown); + EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId); + EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1); + EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue); + + sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard; + EXPECT_NE(newConditionWizard, oldConditionWizard); + EXPECT_EQ(newConditionWizard->getStrongCount(), 3); + oldMetricProducers.clear(); + // Only reference to the old wizard should be the one in the test. + EXPECT_EQ(oldConditionWizard->getStrongCount(), 1); +} } // namespace statsd } // namespace os } // namespace android diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index a61159a23c2c..85cb120ecb69 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -1910,6 +1910,8 @@ public class ActivityOptions { public static final int TYPE_NOTIFICATION = 2; /** Launched from lockscreen, including notification while the device is locked. */ public static final int TYPE_LOCKSCREEN = 3; + /** Launched from recents gesture handler. */ + public static final int TYPE_RECENTS_ANIMATION = 4; @IntDef(flag = true, prefix = { "TYPE_" }, value = { TYPE_LAUNCHER, diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 3e4d5eee34fe..d0a0e30bad18 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -211,23 +211,23 @@ public class ActivityTaskManager { } /** - * Removes stacks in the windowing modes from the system if they are of activity type + * Removes root tasks in the windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public void removeStacksInWindowingModes(int[] windowingModes) throws SecurityException { + public void removeRootTasksInWindowingModes(@NonNull int[] windowingModes) { try { - getService().removeStacksInWindowingModes(windowingModes); + getService().removeRootTasksInWindowingModes(windowingModes); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - /** Removes stack of the activity types from the system. */ + /** Removes root tasks of the activity types from the system. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public void removeStacksWithActivityTypes(int[] activityTypes) throws SecurityException { + public void removeRootTasksWithActivityTypes(@NonNull int[] activityTypes) { try { - getService().removeStacksWithActivityTypes(activityTypes); + getService().removeRootTasksWithActivityTypes(activityTypes); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -310,15 +310,15 @@ public class ActivityTaskManager { } /** - * Moves the top activity in the input stackId to the pinned stack. - * @param stackId Id of stack to move the top activity to pinned stack. - * @param bounds Bounds to use for pinned stack. - * @return True if the top activity of stack was successfully moved to the pinned stack. + * Moves the top activity in the input rootTaskId to the pinned root task. + * @param rootTaskId Id of root task to move the top activity to pinned root task. + * @param bounds Bounds to use for pinned root task. + * @return True if the top activity of root task was successfully moved to the pinned root task. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) { + public boolean moveTopActivityToPinnedRootTask(int rootTaskId, @NonNull Rect bounds) { try { - return getService().moveTopActivityToPinnedStack(stackId, bounds); + return getService().moveTopActivityToPinnedRootTask(rootTaskId, bounds); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -350,15 +350,15 @@ public class ActivityTaskManager { } /** - * Move task to stack with given id. + * Move task to root task with given id. * @param taskId Id of the task to move. - * @param stackId Id of the stack for task moving. + * @param rootTaskId Id of the rootTask for task moving. * @param toTop Whether the given task should shown to top of stack. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public void moveTaskToStack(int taskId, int stackId, boolean toTop) { + public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) { try { - getService().moveTaskToStack(taskId, stackId, toTop); + getService().moveTaskToRootTask(taskId, rootTaskId, toTop); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -380,13 +380,13 @@ public class ActivityTaskManager { /** * Resize docked stack & its task to given stack & task bounds. - * @param stackBounds Bounds to resize stack. + * @param rootTaskBounds Bounds to resize stack. * @param taskBounds Bounds to resize task. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public void resizeDockedStack(Rect stackBounds, Rect taskBounds) { + public void resizePrimarySplitScreen(@NonNull Rect rootTaskBounds, @NonNull Rect taskBounds) { try { - getService().resizeDockedStack(stackBounds, taskBounds, null, null, null); + getService().resizePrimarySplitScreen(rootTaskBounds, taskBounds, null, null, null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 3d966c7fbc38..7b25e25f3ff4 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -16,13 +16,13 @@ package android.app; -import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA256; -import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA512; -import static android.content.pm.Checksum.WHOLE_MD5; -import static android.content.pm.Checksum.WHOLE_MERKLE_ROOT_4K_SHA256; -import static android.content.pm.Checksum.WHOLE_SHA1; -import static android.content.pm.Checksum.WHOLE_SHA256; -import static android.content.pm.Checksum.WHOLE_SHA512; +import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256; +import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512; +import static android.content.pm.Checksum.TYPE_WHOLE_MD5; +import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256; +import static android.content.pm.Checksum.TYPE_WHOLE_SHA1; +import static android.content.pm.Checksum.TYPE_WHOLE_SHA256; +import static android.content.pm.Checksum.TYPE_WHOLE_SHA512; import android.annotation.DrawableRes; import android.annotation.NonNull; @@ -117,7 +117,6 @@ import dalvik.system.VMRuntime; import libcore.util.EmptyArray; -import java.io.IOException; import java.lang.ref.WeakReference; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; @@ -150,10 +149,11 @@ public class ApplicationPackageManager extends PackageManager { private static final int sDefaultFlags = GET_SHARED_LIBRARY_FILES; /** Default set of checksums - includes all available checksums. - * @see PackageManager#getChecksums */ + * @see PackageManager#requestChecksums */ private static final int DEFAULT_CHECKSUMS = - WHOLE_MERKLE_ROOT_4K_SHA256 | WHOLE_MD5 | WHOLE_SHA1 | WHOLE_SHA256 | WHOLE_SHA512 - | PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512; + TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 | TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA1 | TYPE_WHOLE_SHA256 + | TYPE_WHOLE_SHA512 | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 + | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512; // Name of the resource which provides background permission button string public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS = @@ -994,14 +994,24 @@ public class ApplicationPackageManager extends PackageManager { } @Override - public void getChecksums(@NonNull String packageName, boolean includeSplits, - @Checksum.Kind int required, @Nullable List<Certificate> trustedInstallers, + public void requestChecksums(@NonNull String packageName, boolean includeSplits, + @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers, @NonNull IntentSender statusReceiver) - throws CertificateEncodingException, IOException, NameNotFoundException { + throws CertificateEncodingException, NameNotFoundException { Objects.requireNonNull(packageName); Objects.requireNonNull(statusReceiver); - try { - mPM.getChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required, + Objects.requireNonNull(trustedInstallers); + try { + if (trustedInstallers == TRUST_ALL) { + trustedInstallers = null; + } else if (trustedInstallers == TRUST_NONE) { + trustedInstallers = Collections.emptyList(); + } else if (trustedInstallers.isEmpty()) { + throw new IllegalArgumentException( + "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty " + + "list of certificates."); + } + mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required, encodeCertificates(trustedInstallers), statusReceiver, getUserId()); } catch (ParcelableException e) { e.maybeRethrow(PackageManager.NameNotFoundException.class); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 1a4db4e541cc..357b26c3083d 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -25,7 +25,6 @@ import android.app.GrantedUriPermission; import android.app.IApplicationThread; import android.app.IActivityController; import android.app.IAppTask; -import android.app.IAssistDataReceiver; import android.app.IInstrumentationWatcher; import android.app.IProcessObserver; import android.app.IServiceConnection; @@ -70,7 +69,6 @@ import android.os.RemoteCallback; import android.os.StrictMode; import android.os.WorkSource; import android.service.voice.IVoiceInteractionSession; -import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationAdapter; import com.android.internal.app.IVoiceInteractor; @@ -449,9 +447,8 @@ interface IActivityManager { void hang(in IBinder who, boolean allowRestart); List<ActivityTaskManager.RootTaskInfo> getAllRootTaskInfos(); - @UnsupportedAppUsage - void moveTaskToStack(int taskId, int stackId, boolean toTop); - void setFocusedStack(int stackId); + void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop); + void setFocusedRootTask(int taskId); ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo(); @UnsupportedAppUsage void restart(); @@ -470,11 +467,6 @@ interface IActivityManager { @UnsupportedAppUsage boolean isInLockTaskMode(); @UnsupportedAppUsage - void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver, - in IRecentsAnimationRunner recentsAnimationRunner); - @UnsupportedAppUsage - void cancelRecentsAnimation(boolean restoreHomeStackPosition); - @UnsupportedAppUsage int startActivityFromRecents(int taskId, in Bundle options); @UnsupportedAppUsage void startSystemLockTaskMode(int taskId); @@ -514,15 +506,12 @@ interface IActivityManager { boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd); @UnsupportedAppUsage void suppressResizeConfigChanges(boolean suppress); - @UnsupportedAppUsage - boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds); + boolean moveTopActivityToPinnedRootTask(int rootTaskId, in Rect bounds); boolean isAppStartModeDisabled(int uid, in String packageName); @UnsupportedAppUsage boolean unlockUser(int userid, in byte[] token, in byte[] secret, in IProgressListener listener); void killPackageDependents(in String packageName, int userId); - @UnsupportedAppUsage - void removeStack(int stackId); void makePackageIdle(String packageName, int userId); int getMemoryTrimLevel(); boolean isVrModePackageEnabled(in ComponentName packageName); diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index e21ef8ee23ad..66007e5c17d6 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -123,7 +123,7 @@ interface IActivityTaskManager { in ProfilerInfo profilerInfo, in Bundle options, int userId); int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid, int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId); - void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver, + void startRecentsActivity(in Intent intent, in long eventTime, in IRecentsAnimationRunner recentsAnimationRunner); int startActivityFromRecents(int taskId, in Bundle options); int startActivityAsCaller(in IApplicationThread caller, in String callingPackage, @@ -186,11 +186,11 @@ interface IActivityTaskManager { void reportAssistContextExtras(in IBinder token, in Bundle extras, in AssistStructure structure, in AssistContent content, in Uri referrer); - void setFocusedStack(int stackId); + void setFocusedRootTask(int taskId); ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo(); Rect getTaskBounds(int taskId); - void cancelRecentsAnimation(boolean restoreHomeStackPosition); + void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition); void startLockTaskModeByToken(in IBinder token); void stopLockTaskModeByToken(in IBinder token); void updateLockTaskPackages(int userId, in String[] packages); @@ -239,8 +239,7 @@ interface IActivityTaskManager { * @return Return true on success. Otherwise false. */ boolean resizeTask(int taskId, in Rect bounds, int resizeMode); - void moveStackToDisplay(int stackId, int displayId); - void removeStack(int stackId); + void moveRootTaskToDisplay(int taskId, int displayId); /** * Sets the windowing mode for a specific task. Only works on tasks of type @@ -251,15 +250,15 @@ interface IActivityTaskManager { * @return Whether the task was successfully put into the specified windowing mode. */ boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop); - void moveTaskToStack(int taskId, int stackId, boolean toTop); + void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop); boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop); /** - * Removes stacks in the input windowing modes from the system if they are of activity type + * Removes root tasks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED */ - void removeStacksInWindowingModes(in int[] windowingModes); - /** Removes stack of the activity types from the system. */ - void removeStacksWithActivityTypes(in int[] activityTypes); + void removeRootTasksInWindowingModes(in int[] windowingModes); + /** Removes root tasks of the activity types from the system. */ + void removeRootTasksWithActivityTypes(in int[] activityTypes); List<ActivityTaskManager.RootTaskInfo> getAllRootTaskInfos(); ActivityTaskManager.RootTaskInfo getRootTaskInfo(int windowingMode, int activityType); @@ -299,7 +298,7 @@ interface IActivityTaskManager { in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations); void suppressResizeConfigChanges(boolean suppress); - boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds); + boolean moveTopActivityToPinnedRootTask(int rootTaskId, in Rect bounds); boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params); void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params); void requestPictureInPictureMode(in IBinder token); @@ -325,7 +324,7 @@ interface IActivityTaskManager { * stacks. * @throws RemoteException */ - void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds, + void resizePrimarySplitScreen(in Rect dockedBounds, in Rect tempDockedTaskBounds, in Rect tempDockedTaskInsetBounds, in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds); diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index 19242ba9bdb5..cb879fce9c10 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -76,16 +76,24 @@ public abstract class DevicePolicyManagerInternal { OnCrossProfileWidgetProvidersChangeListener listener); /** - * Checks if an app with given uid is an active device admin of its user and has the policy - * specified. + * Checks if an app with given uid is an active device owner of its user. * * <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held. * * @param uid App uid. - * @param reqPolicy Required policy, for policies see {@link DevicePolicyManager}. - * @return true if the uid is an active admin with the given policy. + * @return true if the uid is an active device owner. */ - public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy); + public abstract boolean isActiveDeviceOwner(int uid); + + /** + * Checks if an app with given uid is an active profile owner of its user. + * + * <p>This takes the DPMS lock. DO NOT call from PM/UM/AM with their lock held. + * + * @param uid App uid. + * @return true if the uid is an active profile owner. + */ + public abstract boolean isActiveProfileOwner(int uid); /** * Checks if an app with given uid is the active supervision admin. diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java index 9864afba534a..4796d8db3e96 100644 --- a/core/java/android/app/time/TimeManager.java +++ b/core/java/android/app/time/TimeManager.java @@ -18,6 +18,7 @@ package android.app.time; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.timezonedetector.ITimeZoneDetectorService; import android.content.Context; @@ -36,7 +37,7 @@ import java.util.concurrent.Executor; * * @hide */ -// @SystemApi +@SystemApi @SystemService(Context.TIME_MANAGER) public final class TimeManager { private static final String TAG = "time.TimeManager"; diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java index c62c2b34f35d..df89c285d5bd 100644 --- a/core/java/android/app/time/TimeZoneCapabilities.java +++ b/core/java/android/app/time/TimeZoneCapabilities.java @@ -19,6 +19,7 @@ package android.app.time; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TimeZoneDetector; import android.os.Parcel; @@ -50,7 +51,7 @@ import java.util.Objects; * * @hide */ -// @SystemApi +@SystemApi public final class TimeZoneCapabilities implements Parcelable { /** @hide */ diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java index 6a04f3f277ed..b339e53b8be3 100644 --- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java +++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java @@ -17,6 +17,7 @@ package android.app.time; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -27,7 +28,7 @@ import java.util.Objects; * * @hide */ -// @SystemApi +@SystemApi public final class TimeZoneCapabilitiesAndConfig implements Parcelable { public static final @NonNull Creator<TimeZoneCapabilitiesAndConfig> CREATOR = diff --git a/core/java/android/app/time/TimeZoneConfiguration.java b/core/java/android/app/time/TimeZoneConfiguration.java index 488818a528ef..c0a0c21b546d 100644 --- a/core/java/android/app/time/TimeZoneConfiguration.java +++ b/core/java/android/app/time/TimeZoneConfiguration.java @@ -18,6 +18,7 @@ package android.app.time; import android.annotation.NonNull; import android.annotation.StringDef; +import android.annotation.SystemApi; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -41,7 +42,7 @@ import java.util.Objects; * * @hide */ -// @SystemApi +@SystemApi public final class TimeZoneConfiguration implements Parcelable { public static final @NonNull Creator<TimeZoneConfiguration> CREATOR = @@ -188,7 +189,7 @@ public final class TimeZoneConfiguration implements Parcelable { * * @hide */ - // @SystemApi + @SystemApi public static final class Builder { private final Bundle mBundle = new Bundle(); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 6a6879626c9c..573892bcf014 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2563,7 +2563,7 @@ public final class BluetoothAdapter { * Create a listening, insecure RFCOMM Bluetooth socket with Service Record. * <p>The link key is not required to be authenticated, i.e the communication may be * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices, - * the link will be encrypted, as encryption is mandartory. + * the link will be encrypted, as encryption is mandatory. * For legacy devices (pre Bluetooth 2.1 devices) the link will not * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an * encrypted and authenticated communication channel is desired. @@ -2602,7 +2602,7 @@ public final class BluetoothAdapter { * an input and output capability or just has the ability to display a numeric key, * a secure socket connection is not possible and this socket can be used. * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required. - * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandartory. + * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandatory. * For more details, refer to the Security Model section 5.2 (vol 3) of * Bluetooth Core Specification version 2.1 + EDR. * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index c2beab507da0..ea4b76257ab7 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1109,6 +1109,34 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { } /** + * Returns the reversed orientation. + * @hide + */ + @ActivityInfo.ScreenOrientation + public static int reverseOrientation(@ActivityInfo.ScreenOrientation int orientation) { + switch (orientation) { + case SCREEN_ORIENTATION_LANDSCAPE: + return SCREEN_ORIENTATION_PORTRAIT; + case SCREEN_ORIENTATION_PORTRAIT: + return SCREEN_ORIENTATION_LANDSCAPE; + case SCREEN_ORIENTATION_SENSOR_LANDSCAPE: + return SCREEN_ORIENTATION_SENSOR_PORTRAIT; + case SCREEN_ORIENTATION_SENSOR_PORTRAIT: + return SCREEN_ORIENTATION_SENSOR_LANDSCAPE; + case SCREEN_ORIENTATION_REVERSE_LANDSCAPE: + return SCREEN_ORIENTATION_REVERSE_PORTRAIT; + case SCREEN_ORIENTATION_REVERSE_PORTRAIT: + return SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + case SCREEN_ORIENTATION_USER_LANDSCAPE: + return SCREEN_ORIENTATION_USER_PORTRAIT; + case SCREEN_ORIENTATION_USER_PORTRAIT: + return SCREEN_ORIENTATION_USER_LANDSCAPE; + default: + return orientation; + } + } + + /** * Returns true if the activity supports picture-in-picture. * @hide */ diff --git a/core/java/android/content/pm/ApkChecksum.java b/core/java/android/content/pm/ApkChecksum.java index bf678419cbef..eca48eca9e4b 100644 --- a/core/java/android/content/pm/ApkChecksum.java +++ b/core/java/android/content/pm/ApkChecksum.java @@ -18,7 +18,6 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.IntentSender; import android.os.Parcel; import android.os.Parcelable; @@ -31,12 +30,11 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.util.List; /** * A typed checksum of an APK. * - * @see PackageManager#getChecksums(String, boolean, int, List, IntentSender) + * @see PackageManager#requestChecksums */ @DataClass(genHiddenConstructor = true) @DataClass.Suppress({"getChecksum"}) @@ -52,20 +50,20 @@ public final class ApkChecksum implements Parcelable { /** * For Installer-provided checksums, package name of the Installer. */ - private final @Nullable String mSourcePackageName; + private final @Nullable String mInstallerPackageName; /** * For Installer-provided checksums, certificate of the Installer. */ - private final @Nullable byte[] mSourceCertificate; + private final @Nullable byte[] mInstallerCertificate; /** * Constructor, internal use only. * * @hide */ - public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind, + public ApkChecksum(@Nullable String splitName, @Checksum.Type int type, @NonNull byte[] value) { - this(splitName, new Checksum(kind, value), (String) null, (byte[]) null); + this(splitName, new Checksum(type, value), (String) null, (byte[]) null); } /** @@ -73,19 +71,19 @@ public final class ApkChecksum implements Parcelable { * * @hide */ - public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind, @NonNull byte[] value, + public ApkChecksum(@Nullable String splitName, @Checksum.Type int type, @NonNull byte[] value, @Nullable String sourcePackageName, @Nullable Certificate sourceCertificate) throws CertificateEncodingException { - this(splitName, new Checksum(kind, value), sourcePackageName, + this(splitName, new Checksum(type, value), sourcePackageName, (sourceCertificate != null) ? sourceCertificate.getEncoded() : null); } /** - * Checksum kind. + * Checksum type. */ - public @Checksum.Kind int getKind() { - return mChecksum.getKind(); + public @Checksum.Type int getType() { + return mChecksum.getType(); } /** @@ -96,24 +94,24 @@ public final class ApkChecksum implements Parcelable { } /** - * Returns raw bytes representing encoded certificate of the source of this checksum. + * Returns raw bytes representing encoded certificate of the Installer. * @hide */ - public @Nullable byte[] getSourceCertificateBytes() { - return mSourceCertificate; + public @Nullable byte[] getInstallerCertificateBytes() { + return mInstallerCertificate; } /** - * Certificate of the source of this checksum. + * For Installer-provided checksums, certificate of the Installer. * @throws CertificateException in case when certificate can't be re-created from serialized * data. */ - public @Nullable Certificate getSourceCertificate() throws CertificateException { - if (mSourceCertificate == null) { + public @Nullable Certificate getInstallerCertificate() throws CertificateException { + if (mInstallerCertificate == null) { return null; } final CertificateFactory cf = CertificateFactory.getInstance("X.509"); - final InputStream is = new ByteArrayInputStream(mSourceCertificate); + final InputStream is = new ByteArrayInputStream(mInstallerCertificate); final X509Certificate cert = (X509Certificate) cf.generateCertificate(is); return cert; } @@ -140,9 +138,9 @@ public final class ApkChecksum implements Parcelable { * Checksum for which split. Null indicates base.apk. * @param checksum * Checksum. - * @param sourcePackageName + * @param installerPackageName * For Installer-provided checksums, package name of the Installer. - * @param sourceCertificate + * @param installerCertificate * For Installer-provided checksums, certificate of the Installer. * @hide */ @@ -150,14 +148,14 @@ public final class ApkChecksum implements Parcelable { public ApkChecksum( @Nullable String splitName, @NonNull Checksum checksum, - @Nullable String sourcePackageName, - @Nullable byte[] sourceCertificate) { + @Nullable String installerPackageName, + @Nullable byte[] installerCertificate) { this.mSplitName = splitName; this.mChecksum = checksum; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mChecksum); - this.mSourcePackageName = sourcePackageName; - this.mSourceCertificate = sourceCertificate; + this.mInstallerPackageName = installerPackageName; + this.mInstallerCertificate = installerCertificate; // onConstructed(); // You can define this method to get a callback } @@ -174,8 +172,8 @@ public final class ApkChecksum implements Parcelable { * For Installer-provided checksums, package name of the Installer. */ @DataClass.Generated.Member - public @Nullable String getSourcePackageName() { - return mSourcePackageName; + public @Nullable String getInstallerPackageName() { + return mInstallerPackageName; } @Override @@ -186,13 +184,13 @@ public final class ApkChecksum implements Parcelable { byte flg = 0; if (mSplitName != null) flg |= 0x1; - if (mSourcePackageName != null) flg |= 0x4; - if (mSourceCertificate != null) flg |= 0x8; + if (mInstallerPackageName != null) flg |= 0x4; + if (mInstallerCertificate != null) flg |= 0x8; dest.writeByte(flg); if (mSplitName != null) dest.writeString(mSplitName); dest.writeTypedObject(mChecksum, flags); - if (mSourcePackageName != null) dest.writeString(mSourcePackageName); - if (mSourceCertificate != null) dest.writeByteArray(mSourceCertificate); + if (mInstallerPackageName != null) dest.writeString(mInstallerPackageName); + if (mInstallerCertificate != null) dest.writeByteArray(mInstallerCertificate); } @Override @@ -209,15 +207,15 @@ public final class ApkChecksum implements Parcelable { byte flg = in.readByte(); String splitName = (flg & 0x1) == 0 ? null : in.readString(); Checksum checksum = (Checksum) in.readTypedObject(Checksum.CREATOR); - String sourcePackageName = (flg & 0x4) == 0 ? null : in.readString(); - byte[] sourceCertificate = (flg & 0x8) == 0 ? null : in.createByteArray(); + String installerPackageName = (flg & 0x4) == 0 ? null : in.readString(); + byte[] installerCertificate = (flg & 0x8) == 0 ? null : in.createByteArray(); this.mSplitName = splitName; this.mChecksum = checksum; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mChecksum); - this.mSourcePackageName = sourcePackageName; - this.mSourceCertificate = sourceCertificate; + this.mInstallerPackageName = installerPackageName; + this.mInstallerCertificate = installerCertificate; // onConstructed(); // You can define this method to get a callback } @@ -237,10 +235,10 @@ public final class ApkChecksum implements Parcelable { }; @DataClass.Generated( - time = 1600407436287L, + time = 1601589269293L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/content/pm/ApkChecksum.java", - inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.NonNull android.content.pm.Checksum mChecksum\nprivate final @android.annotation.Nullable java.lang.String mSourcePackageName\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.content.pm.Checksum.Kind int getKind()\npublic @android.annotation.NonNull byte[] getValue()\npublic @android.annotation.Nullable byte[] getSourceCertificateBytes()\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass ApkChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)") + inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.NonNull android.content.pm.Checksum mChecksum\nprivate final @android.annotation.Nullable java.lang.String mInstallerPackageName\nprivate final @android.annotation.Nullable byte[] mInstallerCertificate\npublic @android.content.pm.Checksum.Type int getType()\npublic @android.annotation.NonNull byte[] getValue()\npublic @android.annotation.Nullable byte[] getInstallerCertificateBytes()\npublic @android.annotation.Nullable java.security.cert.Certificate getInstallerCertificate()\nclass ApkChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/Checksum.java b/core/java/android/content/pm/Checksum.java index 10ca5cac5276..318d3635ede1 100644 --- a/core/java/android/content/pm/Checksum.java +++ b/core/java/android/content/pm/Checksum.java @@ -25,12 +25,12 @@ import com.android.internal.util.DataClass; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.List; /** * A typed checksum. * - * @see PackageInstaller.Session#addChecksums(String, List) + * @see ApkChecksum + * @see PackageManager#requestChecksums */ @DataClass(genConstDefs = false) public final class Checksum implements Parcelable { @@ -43,88 +43,89 @@ public final class Checksum implements Parcelable { * Can be used by kernel to enforce authenticity and integrity of the APK. * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst#">See fs-verity for details</a> * - * @see PackageManager#getChecksums - * @see PackageInstaller.Session#addChecksums + * @see PackageManager#requestChecksums */ - public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001; + public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001; /** * MD5 hash computed over all file bytes. * - * @see PackageManager#getChecksums - * @see PackageInstaller.Session#addChecksums - * @deprecated Use SHA2 family of hashes (SHA256/SHA512) instead. - * MD5 is cryptographically broken and unsuitable for further use. + * @see PackageManager#requestChecksums + * @deprecated Not platform enforced. Cryptographically broken and unsuitable for further use. + * Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}. * Provided for completeness' sake and to support legacy usecases. */ @Deprecated - public static final int WHOLE_MD5 = 0x00000002; + public static final int TYPE_WHOLE_MD5 = 0x00000002; /** * SHA1 hash computed over all file bytes. * - * @see PackageManager#getChecksums - * @see PackageInstaller.Session#addChecksums - * @deprecated Use SHA2 family of hashes (SHA256/SHA512) instead. - * SHA1 is broken and should not be used. + * @see PackageManager#requestChecksums + * @deprecated Not platform enforced. Broken and should not be used. + * Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}. * Provided for completeness' sake and to support legacy usecases. */ @Deprecated - public static final int WHOLE_SHA1 = 0x00000004; + public static final int TYPE_WHOLE_SHA1 = 0x00000004; /** * SHA256 hash computed over all file bytes. + * @deprecated Not platform enforced. + * Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}. + * Provided for completeness' sake and to support legacy usecases. * - * @see PackageManager#getChecksums - * @see PackageInstaller.Session#addChecksums + * @see PackageManager#requestChecksums */ - public static final int WHOLE_SHA256 = 0x00000008; + @Deprecated + public static final int TYPE_WHOLE_SHA256 = 0x00000008; /** * SHA512 hash computed over all file bytes. + * @deprecated Not platform enforced. + * Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}. + * Provided for completeness' sake and to support legacy usecases. * - * @see PackageManager#getChecksums - * @see PackageInstaller.Session#addChecksums + * @see PackageManager#requestChecksums */ - public static final int WHOLE_SHA512 = 0x00000010; + @Deprecated + public static final int TYPE_WHOLE_SHA512 = 0x00000010; /** * Root SHA256 hash of a 1M Merkle tree computed over protected content. * Excludes signing block. * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>. * - * @see PackageManager#getChecksums - * @see PackageInstaller.Session#addChecksums + * @see PackageManager#requestChecksums */ - public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020; + public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020; /** * Root SHA512 hash of a 1M Merkle tree computed over protected content. * Excludes signing block. * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>. * - * @see PackageManager#getChecksums - * @see PackageInstaller.Session#addChecksums + * @see PackageManager#requestChecksums */ - public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040; + public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040; /** @hide */ - @IntDef(flag = true, prefix = {"WHOLE_", "PARTIAL_"}, value = { - WHOLE_MERKLE_ROOT_4K_SHA256, - WHOLE_MD5, - WHOLE_SHA1, - WHOLE_SHA256, - WHOLE_SHA512, - PARTIAL_MERKLE_ROOT_1M_SHA256, - PARTIAL_MERKLE_ROOT_1M_SHA512, + @IntDef(flag = true, prefix = {"TYPE_"}, value = { + TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, + TYPE_WHOLE_MD5, + TYPE_WHOLE_SHA1, + TYPE_WHOLE_SHA256, + TYPE_WHOLE_SHA512, + TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, + TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, }) @Retention(RetentionPolicy.SOURCE) - public @interface Kind {} + public @interface Type {} /** - * Checksum kind. + * Checksum type. */ - private final @Checksum.Kind int mKind; + private final @Checksum.Type int mType; /** * Checksum value. */ @@ -132,7 +133,6 @@ public final class Checksum implements Parcelable { - // Code below generated by codegen v1.0.15. // // DO NOT MODIFY! @@ -149,18 +149,18 @@ public final class Checksum implements Parcelable { /** * Creates a new Checksum. * - * @param kind - * Checksum kind. + * @param type + * Checksum type. * @param value * Checksum value. */ @DataClass.Generated.Member public Checksum( - @Checksum.Kind int kind, + @Checksum.Type int type, @NonNull byte[] value) { - this.mKind = kind; + this.mType = type; com.android.internal.util.AnnotationValidations.validate( - Checksum.Kind.class, null, mKind); + Checksum.Type.class, null, mType); this.mValue = value; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mValue); @@ -169,11 +169,11 @@ public final class Checksum implements Parcelable { } /** - * Checksum kind. + * Checksum type. */ @DataClass.Generated.Member - public @Checksum.Kind int getKind() { - return mKind; + public @Checksum.Type int getType() { + return mType; } /** @@ -190,7 +190,7 @@ public final class Checksum implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - dest.writeInt(mKind); + dest.writeInt(mType); dest.writeByteArray(mValue); } @@ -205,12 +205,12 @@ public final class Checksum implements Parcelable { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } - int kind = in.readInt(); + int type = in.readInt(); byte[] value = in.createByteArray(); - this.mKind = kind; + this.mType = type; com.android.internal.util.AnnotationValidations.validate( - Checksum.Kind.class, null, mKind); + Checksum.Type.class, null, mType); this.mValue = value; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mValue); @@ -233,10 +233,10 @@ public final class Checksum implements Parcelable { }; @DataClass.Generated( - time = 1600717052366L, + time = 1601955017774L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/content/pm/Checksum.java", - inputSignatures = "public static final int WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int WHOLE_MD5\npublic static final @java.lang.Deprecated int WHOLE_SHA1\npublic static final int WHOLE_SHA256\npublic static final int WHOLE_SHA512\npublic static final int PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final int PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Kind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)") + inputSignatures = "public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index ba894ae72017..79e3eead1234 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -749,7 +749,7 @@ interface IPackageManager { void notifyPackagesReplacedReceived(in String[] packages); - void getChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId); + void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId); //------------------------------------------------------------------------ // diff --git a/core/java/android/content/pm/IncrementalStatesInfo.aidl b/core/java/android/content/pm/IncrementalStatesInfo.aidl index b5aad1269c60..7064fbfa8b6a 100644 --- a/core/java/android/content/pm/IncrementalStatesInfo.aidl +++ b/core/java/android/content/pm/IncrementalStatesInfo.aidl @@ -15,6 +15,6 @@ ** limitations under the License. */ -package com.android.server.pm; +package android.content.pm; -parcelable IncrementalStatesInfo;
\ No newline at end of file +parcelable IncrementalStatesInfo; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index a9f143987cd4..f03425b9e117 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1222,13 +1222,15 @@ public class PackageInstaller { * Adds installer-provided checksums for the APK file in session. * * @param name previously written as part of this session. + * {@link #openWrite} * @param checksums installer intends to make available via - * {@link PackageManager#getChecksums(String, boolean, int, List, - * IntentSender)}. + * {@link PackageManager#requestChecksums}. * @throws SecurityException if called after the session has been * committed or abandoned. - * @deprecated use platform-enforced checksums e.g. - * {@link Checksum#WHOLE_MERKLE_ROOT_4K_SHA256} + * @deprecated do not use installer-provided checksums, + * use platform-enforced checksums + * e.g. {@link Checksum#TYPE_WHOLE_MERKLE_ROOT_4K_SHA256} + * in {@link PackageManager#requestChecksums}. */ @Deprecated public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums) diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c293e4ad6821..1a992f519286 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -79,7 +79,6 @@ import com.android.internal.util.ArrayUtils; import dalvik.system.VMRuntime; import java.io.File; -import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.security.cert.Certificate; @@ -3309,7 +3308,7 @@ public abstract class PackageManager { /** * Extra field name for the ID of a package pending verification. Passed to * a package verifier and is used to call back to - * @see #getChecksums + * @see #requestChecksums */ public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS"; @@ -7987,19 +7986,20 @@ public abstract class PackageManager { /** * Trust any Installer to provide checksums for the package. - * @see #getChecksums + * @see #requestChecksums */ - public static final @Nullable List<Certificate> TRUST_ALL = null; + public static final @Nullable List<Certificate> TRUST_ALL = Collections.singletonList(null); /** * Don't trust any Installer to provide checksums for the package. * This effectively disables optimized Installer-enforced checksums. - * @see #getChecksums + * @see #requestChecksums */ - public static final @NonNull List<Certificate> TRUST_NONE = Collections.emptyList(); + public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null); /** - * Returns the checksums for APKs within a package. + * Requesting the checksums for APKs within a package. + * The checksums will be returned asynchronously via statusReceiver. * * By default returns all readily available checksums: * - enforced by platform, @@ -8011,21 +8011,24 @@ public abstract class PackageManager { * * @param packageName whose checksums to return. * @param includeSplits whether to include checksums for non-base splits. - * @param required explicitly request the checksum kinds. Will incur significant + * @param required explicitly request the checksum types. May incur significant * CPU/memory/disk usage. - * @param trustedInstallers for checksums enforced by Installer, which ones to be trusted. - * {@link #TRUST_ALL} will return checksums from any Installer, - * {@link #TRUST_NONE} disables optimized Installer-enforced checksums. + * @param trustedInstallers for checksums enforced by installer, which installers are to be + * trusted. + * {@link #TRUST_ALL} will return checksums from any installer, + * {@link #TRUST_NONE} disables optimized installer-enforced checksums, + * otherwise the list has to be non-empty list of certificates. * @param statusReceiver called once when the results are available as - * {@link #EXTRA_CHECKSUMS} of type ApkChecksum[]. + * {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[]. * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers. + * @throws IllegalArgumentException if the list of trusted installer certificates is empty. * @throws NameNotFoundException if a package with the given name cannot be found on the system. */ - public void getChecksums(@NonNull String packageName, boolean includeSplits, - @Checksum.Kind int required, @Nullable List<Certificate> trustedInstallers, + public void requestChecksums(@NonNull String packageName, boolean includeSplits, + @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers, @NonNull IntentSender statusReceiver) - throws CertificateEncodingException, IOException, NameNotFoundException { - throw new UnsupportedOperationException("getChecksums not implemented in subclass"); + throws CertificateEncodingException, NameNotFoundException { + throw new UnsupportedOperationException("requestChecksums not implemented in subclass"); } /** diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 9197020e1fce..b936c6323a80 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -1080,14 +1080,57 @@ public class ParsingPackageUtils { } } - final String requiredFeature = sa.getNonConfigurationString( - R.styleable.AndroidManifestUsesPermission_requiredFeature, 0); + final ArraySet<String> requiredFeatures = new ArraySet<>(); + String feature = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature, + 0); + if (feature != null) { + requiredFeatures.add(feature); + } - final String requiredNotfeature = sa.getNonConfigurationString( - R.styleable.AndroidManifestUsesPermission_requiredNotFeature, + final ArraySet<String> requiredNotFeatures = new ArraySet<>(); + feature = sa.getNonConfigurationString( + com.android.internal.R.styleable + .AndroidManifestUsesPermission_requiredNotFeature, 0); + if (feature != null) { + requiredNotFeatures.add(feature); + } + + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + final ParseResult<?> result; + switch (parser.getName()) { + case "required-feature": + result = parseRequiredFeature(input, res, parser); + if (result.isSuccess()) { + requiredFeatures.add((String) result.getResult()); + } + break; + + case "required-not-feature": + result = parseRequiredNotFeature(input, res, parser); + if (result.isSuccess()) { + requiredNotFeatures.add((String) result.getResult()); + } + break; - XmlUtils.skipCurrentTag(parser); + default: + result = ParsingUtils.unknownTag("<uses-permission>", pkg, parser, input); + break; + } + + if (result.isError()) { + return input.error(result); + } + } // Can only succeed from here on out ParseResult<ParsingPackage> success = input.success(pkg); @@ -1100,17 +1143,22 @@ public class ParsingPackageUtils { return success; } - // Only allow requesting this permission if the platform supports the given feature. - if (requiredFeature != null && mCallback != null && !mCallback.hasFeature( - requiredFeature)) { - return success; - } + if (mCallback != null) { + // Only allow requesting this permission if the platform supports all of the + // "required-feature"s. + for (int i = requiredFeatures.size() - 1; i >= 0; i--) { + if (!mCallback.hasFeature(requiredFeatures.valueAt(i))) { + return success; + } + } - // Only allow requesting this permission if the platform doesn't support the given - // feature. - if (requiredNotfeature != null && mCallback != null - && mCallback.hasFeature(requiredNotfeature)) { - return success; + // Only allow requesting this permission if the platform does not supports any of + // the "required-not-feature"s. + for (int i = requiredNotFeatures.size() - 1; i >= 0; i--) { + if (mCallback.hasFeature(requiredNotFeatures.valueAt(i))) { + return success; + } + } } if (!pkg.getRequestedPermissions().contains(name)) { @@ -1127,6 +1175,36 @@ public class ParsingPackageUtils { } } + private ParseResult<String> parseRequiredFeature(ParseInput input, Resources res, + AttributeSet attrs) { + final TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestRequiredFeature); + try { + final String featureName = sa.getString( + R.styleable.AndroidManifestRequiredFeature_name); + return TextUtils.isEmpty(featureName) + ? input.error("Feature name is missing from <required-feature> tag.") + : input.success(featureName); + } finally { + sa.recycle(); + } + } + + private ParseResult<String> parseRequiredNotFeature(ParseInput input, Resources res, + AttributeSet attrs) { + final TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestRequiredNotFeature); + try { + final String featureName = sa.getString( + R.styleable.AndroidManifestRequiredNotFeature_name); + return TextUtils.isEmpty(featureName) + ? input.error("Feature name is missing from <required-not-feature> tag.") + : input.success(featureName); + } finally { + sa.recycle(); + } + } + private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) { ConfigurationInfo cPref = new ConfigurationInfo(); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index dd820fae3f2d..4f46160a71e1 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -16,17 +16,22 @@ package android.hardware.input; +import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; +import android.annotation.TestApi; +import android.compat.annotation.ChangeId; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.media.AudioAttributes; import android.os.Binder; +import android.os.BlockUntrustedTouchesMode; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -48,8 +53,10 @@ import android.view.InputMonitor; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.VerifiedInputEvent; +import android.view.WindowManager.LayoutParams; import com.android.internal.os.SomeArgs; +import com.android.internal.util.ArrayUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -69,6 +76,13 @@ public final class InputManager { private static final int MSG_DEVICE_REMOVED = 2; private static final int MSG_DEVICE_CHANGED = 3; + /** @hide */ + public static final int[] BLOCK_UNTRUSTED_TOUCHES_MODES = { + BlockUntrustedTouchesMode.DISABLED, + BlockUntrustedTouchesMode.PERMISSIVE, + BlockUntrustedTouchesMode.BLOCK + }; + private static InputManager sInstance; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -168,6 +182,32 @@ public final class InputManager { public static final int DEFAULT_POINTER_SPEED = 0; /** + * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1). + * @hide + */ + public static final float DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = .8f; + + /** + * Default mode of the block untrusted touches mode feature. + * @hide + */ + @BlockUntrustedTouchesMode + public static final int DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE = + BlockUntrustedTouchesMode.DISABLED; + + /** + * Prevent touches from being consumed by apps if these touches passed through a non-trusted + * window from a different UID and are considered unsafe. + * + * TODO(b/158002302): Turn the feature on by default + * + * @hide + */ + @TestApi + @ChangeId + public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; + + /** * Input Event Injection Synchronization Mode: None. * Never blocks. Injection is asynchronous and is assumed always to be successful. * @hide @@ -832,6 +872,103 @@ public final class InputManager { } /** + * Returns the maximum allowed obscuring opacity by UID to propagate touches. + * + * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams + * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows + * above the touch-consuming window. + * + * @see #setMaximumObscuringOpacityForTouch(Context, float) + * + * @hide + */ + @TestApi + public float getMaximumObscuringOpacityForTouch(@NonNull Context context) { + return Settings.Global.getFloat(context.getContentResolver(), + Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, + DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH); + } + + /** + * Sets the maximum allowed obscuring opacity by UID to propagate touches. + * + * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams + * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows + * above the touch-consuming window. + * + * For a certain UID: + * <ul> + * <li>If it's the same as the UID of the touch-consuming window, allow it to propagate + * the touch. + * <li>Otherwise take all its windows of eligible window types above the touch-consuming + * window, compute their combined obscuring opacity considering that {@code + * opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is + * lesser than or equal to this setting and there are no other windows preventing the + * touch, allow the UID to propagate the touch. + * </ul> + * + * This value should be between 0 (inclusive) and 1 (inclusive). + * + * @see #getMaximumObscuringOpacityForTouch(Context) + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + public void setMaximumObscuringOpacityForTouch(@NonNull Context context, float opacity) { + if (opacity < 0 || opacity > 1) { + throw new IllegalArgumentException( + "Maximum obscuring opacity for touch should be >= 0 and <= 1"); + } + Settings.Global.putFloat(context.getContentResolver(), + Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity); + } + + /** + * Returns the current mode of the block untrusted touches feature, one of: + * <ul> + * <li>{@link BlockUntrustedTouchesMode#DISABLED} + * <li>{@link BlockUntrustedTouchesMode#PERMISSIVE} + * <li>{@link BlockUntrustedTouchesMode#BLOCK} + * </ul> + * + * @hide + */ + @TestApi + @BlockUntrustedTouchesMode + public int getBlockUntrustedTouchesMode(@NonNull Context context) { + int mode = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE); + if (!ArrayUtils.contains(BLOCK_UNTRUSTED_TOUCHES_MODES, mode)) { + Log.w(TAG, "Unknown block untrusted touches feature mode " + mode + ", using " + + "default " + DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE); + return DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE; + } + return mode; + } + + /** + * Sets the mode of the block untrusted touches feature to one of: + * <ul> + * <li>{@link BlockUntrustedTouchesMode#DISABLED} + * <li>{@link BlockUntrustedTouchesMode#PERMISSIVE} + * <li>{@link BlockUntrustedTouchesMode#BLOCK} + * </ul> + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + public void setBlockUntrustedTouchesMode(@NonNull Context context, + @BlockUntrustedTouchesMode int mode) { + if (!ArrayUtils.contains(BLOCK_UNTRUSTED_TOUCHES_MODES, mode)) { + throw new IllegalArgumentException("Invalid feature mode " + mode); + } + Settings.Global.putInt(context.getContentResolver(), + Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, mode); + } + + /** * Queries the framework about whether any physical keys exist on the * any keyboard attached to the device that are capable of producing the given * array of key codes. diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 9e332e9b0456..06203ff15094 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -657,16 +657,8 @@ public class ZygoteProcess { argsForZygote.add("--runtime-flags=" + runtimeFlags); if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) { argsForZygote.add("--mount-external-default"); - } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) { - argsForZygote.add("--mount-external-read"); - } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) { - argsForZygote.add("--mount-external-write"); - } else if (mountExternal == Zygote.MOUNT_EXTERNAL_FULL) { - argsForZygote.add("--mount-external-full"); } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) { argsForZygote.add("--mount-external-installer"); - } else if (mountExternal == Zygote.MOUNT_EXTERNAL_LEGACY) { - argsForZygote.add("--mount-external-legacy"); } else if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) { argsForZygote.add("--mount-external-pass-through"); } else if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) { diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java index a79a1cfcd64f..4379ce1055ef 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -70,13 +70,6 @@ public abstract class StorageManagerInternal { public abstract void addExternalStoragePolicy(ExternalStorageMountPolicy policy); /** - * Notify the mount service that the mount policy for a UID changed. - * @param uid The UID for which policy changed. - * @param packageName The package in the UID for making the call. - */ - public abstract void onExternalStoragePolicyChanged(int uid, String packageName); - - /** * Gets the mount mode to use for a given UID as determined by consultin all * policies. * diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1dbf95f7db86..4ff063879d02 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -81,6 +81,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.MemoryIntArray; import android.view.Display; +import android.view.WindowManager.LayoutParams; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; @@ -3567,6 +3568,9 @@ public final class Settings { if (outConfig.fontScale < 0) { outConfig.fontScale = DEFAULT_FONT_SCALE; } + outConfig.forceBoldText = Settings.Secure.getIntForUser( + cr, Settings.Secure.FORCE_BOLD_TEXT, Configuration.FORCE_BOLD_TEXT_NO, + userHandle); final String localeValue = Settings.System.getStringForUser(cr, SYSTEM_LOCALES, userHandle); @@ -3597,6 +3601,7 @@ public final class Settings { if (!inoutConfig.userSetLocale && !inoutConfig.getLocales().isEmpty()) { inoutConfig.clearLocales(); } + inoutConfig.forceBoldText = Configuration.FORCE_BOLD_TEXT_UNDEFINED; } /** @@ -3620,7 +3625,11 @@ public final class Settings { DEFAULT_OVERRIDEABLE_BY_RESTORE); } - /** @hide */ + /** + * Convenience function for checking if settings should be overwritten with config changes. + * @see #putConfigurationForUser(ContentResolver, Configuration, int) + * @hide + */ public static boolean hasInterestingConfigurationChanges(int changes) { return (changes & ActivityInfo.CONFIG_FONT_SCALE) != 0 || (changes & ActivityInfo.CONFIG_LOCALE) != 0; @@ -13407,15 +13416,6 @@ public final class Settings { "power_button_very_long_press"; /** - * Global settings that shouldn't be persisted. - * - * @hide - */ - public static final String[] TRANSIENT_SETTINGS = { - LOCATION_GLOBAL_KILL_SWITCH, - }; - - /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. * @@ -14486,6 +14486,48 @@ public final class Settings { * @hide */ public static final String SHOW_PEOPLE_SPACE = "show_people_space"; + + /** + * Block untrusted touches mode. + * + * Can be one of: + * <ul> + * <li>0 = {@link BlockUntrustedTouchesMode#DISABLED}: Feature is off. + * <li>1 = {@link BlockUntrustedTouchesMode#PERMISSIVE}: Untrusted touches are flagged + * but not blocked + * <li>2 = {@link BlockUntrustedTouchesMode#BLOCK}: Untrusted touches are blocked + * </ul> + * + * @hide + */ + public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches"; + + /** + * The maximum allowed obscuring opacity by UID to propagate touches. + * + * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams + * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows + * above the touch-consuming window. + * + * For a certain UID: + * <ul> + * <li>If it's the same as the UID of the touch-consuming window, allow it to propagate + * the touch. + * <li>Otherwise take all its windows of eligible window types above the touch-consuming + * window, compute their combined obscuring opacity considering that {@code + * opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is + * lesser than or equal to this setting and there are no other windows preventing the + * touch, allow the UID to propagate the touch. + * </ul> + * + * @see android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch(Context) + * @see android.hardware.input.InputManager#setMaximumObscuringOpacityForTouch(Context, + * float) + * + * @hide + */ + public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = + "maximum_obscuring_opacity_for_touch"; } /** diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 70224f94d556..e520d7ccaa7a 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -673,8 +673,6 @@ public final class Choreographer { ThreadedRenderer.setFPSDivisor(divisor); } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link " - + "#postFrameCallback} instead") void doFrame(long frameTimeNanos, int frame, long frameTimelineVsyncId) { final long startNanos; synchronized (mLock) { diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index 430e2cf2bf21..51474d3c39c8 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -17,7 +17,6 @@ package android.view; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.Looper; import android.os.MessageQueue; import android.util.Log; @@ -158,8 +157,6 @@ public abstract class DisplayEventReceiver { * @param frameTimelineVsyncId The frame timeline vsync id, used to correlate a frame * produced by HWUI with the timeline data stored in Surface Flinger. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link " - + "Choreographer#postFrameCallback} instead") public void onVsync(long timestampNanos, long physicalDisplayId, int frame, long frameTimelineVsyncId) { } @@ -203,8 +200,6 @@ public abstract class DisplayEventReceiver { // Called from native code. @SuppressWarnings("unused") - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link " - + "Choreographer#postFrameCallback} instead") private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame, long frameTimelineVsyncId) { onVsync(timestampNanos, physicalDisplayId, frame, frameTimelineVsyncId); diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 1ef701f732ff..d1a9a05d5bf1 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -21,6 +21,7 @@ import static android.view.Display.INVALID_DISPLAY; import android.annotation.Nullable; import android.graphics.Region; import android.os.IBinder; +import android.os.TouchOcclusionMode; import java.lang.ref.WeakReference; @@ -82,10 +83,18 @@ public final class InputWindowHandle { // Window is trusted overlay. public boolean trustedOverlay; + // What effect this window has on touch occlusion if it lets touches pass through + // By default windows will block touches if they are untrusted and from a different UID due to + // security concerns + public int touchOcclusionMode = TouchOcclusionMode.BLOCK_UNTRUSTED; + // Id of process and user that owns the window. public int ownerPid; public int ownerUid; + // Owner package of the window + public String packageName; + // Window input features. public int inputFeatures; diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 44e603ea4900..0c64eeac2ec7 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -182,9 +182,6 @@ public final class SurfaceControl implements Parcelable { IBinder displayToken, int mode); private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject, long barrierObject, long frame); - private static native void nativeDeferTransactionUntilSurface(long transactionObj, - long nativeObject, - long surfaceObject, long frame); private static native void nativeReparentChildren(long transactionObj, long nativeObject, long newParentObject); private static native void nativeReparent(long transactionObj, long nativeObject, @@ -2947,22 +2944,6 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - @Deprecated - @UnsupportedAppUsage - public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface, - long frameNumber) { - if (frameNumber < 0) { - return this; - } - checkPreconditions(sc); - nativeDeferTransactionUntilSurface(mNativeObject, sc.mNativeObject, - barrierSurface.mNativeObject, frameNumber); - return this; - } - - /** - * @hide - */ public Transaction reparentChildren(SurfaceControl sc, SurfaceControl newParent) { checkPreconditions(sc); nativeReparentChildren(mNativeObject, sc.mNativeObject, newParent.mNativeObject); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b653d219b90a..e00ff7e00888 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -78,6 +78,7 @@ 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; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; +import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.IME_FOCUS_CONTROLLER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.INSETS_CONTROLLER; @@ -2707,7 +2708,13 @@ public final class ViewRootImpl implements ViewParent, updateColorModeIfNeeded(lp.getColorMode()); surfaceCreated = !hadSurface && mSurface.isValid(); surfaceDestroyed = hadSurface && !mSurface.isValid(); - surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId()) + // When using Blast, the surface generation id may not change when there's a new + // SurfaceControl. In that case, we also check relayout flag + // RELAYOUT_RES_SURFACE_CHANGED since it should indicate that WMS created a new + // SurfaceControl. + surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId() + || (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED) + == RELAYOUT_RES_SURFACE_CHANGED) && mSurface.isValid(); if (cutoutChanged) { diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java index 5bcfa8bb6439..2e5ee041e54c 100644 --- a/core/java/android/webkit/UserPackage.java +++ b/core/java/android/webkit/UserPackage.java @@ -34,7 +34,7 @@ public class UserPackage { private final UserInfo mUserInfo; private final PackageInfo mPackageInfo; - public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.S; + public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.R; public UserPackage(UserInfo user, PackageInfo packageInfo) { this.mUserInfo = user; diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 5fc93443f4b8..8790bbdcd8f7 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -47,7 +47,7 @@ public final class WebViewFactory { // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote. /** @hide */ private static final String CHROMIUM_WEBVIEW_FACTORY = - "com.android.webview.chromium.WebViewChromiumFactoryProviderForS"; + "com.android.webview.chromium.WebViewChromiumFactoryProviderForR"; private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create"; diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index d8f2bb248fd1..a7cb642b83f9 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -39,13 +39,14 @@ public class TaskOrganizer extends WindowOrganizer { private ITaskOrganizerController mTaskOrganizerController; public TaskOrganizer() { - mTaskOrganizerController = getController(); + this(null); } /** @hide */ @VisibleForTesting public TaskOrganizer(ITaskOrganizerController taskOrganizerController) { - mTaskOrganizerController = taskOrganizerController; + mTaskOrganizerController = taskOrganizerController != null + ? taskOrganizerController : getController(); } /** diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java index 900e18d468bb..28a96013e1c0 100644 --- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java +++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java @@ -166,7 +166,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo { // Now fetch app icon and raster with no badging even in work profile Bitmap appIcon = mSelectableTargetInfoCommunicator.makePresentationGetter(info) - .getIconBitmap(UserHandle.getUserHandleForUid(UserHandle.myUserId())); + .getIconBitmap(android.os.Process.myUserHandle()); // Raster target drawable with appIcon as a badge SimpleIconFactory sif = SimpleIconFactory.obtain(mContext); diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index e60f7fc70757..dd4409c23437 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -44,26 +44,37 @@ public class InteractionJankMonitor { private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L); // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE. - public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 1; - public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 0; - public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 0; - public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 0; - public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 0; - public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 0; - public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 0; - public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 0; - public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 0; - public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 0; - public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 0; - public static final int CUJ_LAUNCHER_QUICK_SWITCH = 0; + public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0; + public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1; + public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2; + public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3; + public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4; + public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 5; + public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 6; + public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 7; + public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 8; + public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 9; + public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 10; + public static final int CUJ_LAUNCHER_QUICK_SWITCH = 11; private static final int NO_STATSD_LOGGING = -1; // Used to convert CujType to InteractionType enum value for statsd logging. // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd. - private static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = { - NO_STATSD_LOGGING, + @VisibleForTesting + public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE, + NO_STATSD_LOGGING, + NO_STATSD_LOGGING, + NO_STATSD_LOGGING, + NO_STATSD_LOGGING, + NO_STATSD_LOGGING, + NO_STATSD_LOGGING, + NO_STATSD_LOGGING, + NO_STATSD_LOGGING, + NO_STATSD_LOGGING, + NO_STATSD_LOGGING, + NO_STATSD_LOGGING, }; private static volatile InteractionJankMonitor sInstance; diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java index 2ba372a47cf3..0843741d237c 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java @@ -18,10 +18,10 @@ package com.android.internal.os; import android.annotation.Nullable; import android.os.Process; +import android.util.IntArray; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import java.io.IOException; @@ -456,14 +456,14 @@ public class KernelCpuThreadReader { * cluster has started. */ private static int[] getClusterStartIndices(long[] frequencies) { - ArrayList<Integer> indices = new ArrayList<>(); + IntArray indices = new IntArray(); indices.add(0); for (int i = 0; i < frequencies.length - 1; i++) { if (frequencies[i] >= frequencies[i + 1]) { indices.add(i + 1); } } - return ArrayUtils.convertToIntArray(indices); + return indices.toArray(); } /** Get the index in frequencies where each bucket starts */ @@ -477,7 +477,7 @@ public class KernelCpuThreadReader { return Arrays.copyOfRange(clusterStartIndices, 0, targetNumBuckets); } - ArrayList<Integer> bucketStartIndices = new ArrayList<>(); + IntArray bucketStartIndices = new IntArray(); for (int clusterIdx = 0; clusterIdx < numClusters; clusterIdx++) { final int clusterStartIdx = getLowerBound(clusterIdx, clusterStartIndices); final int clusterEndIdx = @@ -509,7 +509,7 @@ public class KernelCpuThreadReader { bucketStartIndices.add(bucketStartIdx); } } - return ArrayUtils.convertToIntArray(bucketStartIndices); + return bucketStartIndices.toArray(); } private static int getLowerBound(int index, int[] startIndices) { diff --git a/core/java/com/android/internal/os/ProcTimeInStateReader.java b/core/java/com/android/internal/os/ProcTimeInStateReader.java index 2318473354e2..d54a9c7f3a46 100644 --- a/core/java/com/android/internal/os/ProcTimeInStateReader.java +++ b/core/java/com/android/internal/os/ProcTimeInStateReader.java @@ -18,16 +18,11 @@ package com.android.internal.os; import android.annotation.Nullable; import android.os.Process; - -import com.android.internal.util.ArrayUtils; +import android.util.IntArray; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; /** * Reads and parses {@code time_in_state} files in the {@code proc} filesystem. @@ -68,24 +63,25 @@ public class ProcTimeInStateReader { * The format of a single line of the {@code time_in_state} file that exports the frequency * values */ - private static final List<Integer> TIME_IN_STATE_LINE_FREQUENCY_FORMAT = Arrays.asList( + private static final int[] TIME_IN_STATE_LINE_FREQUENCY_FORMAT = new int[] { Process.PROC_OUT_LONG | Process.PROC_SPACE_TERM, Process.PROC_NEWLINE_TERM - ); + }; /** * The format of a single line of the {@code time_in_state} file that exports the time values */ - private static final List<Integer> TIME_IN_STATE_LINE_TIME_FORMAT = Arrays.asList( + private static final int[] TIME_IN_STATE_LINE_TIME_FORMAT = new int[] { Process.PROC_SPACE_TERM, Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM - ); + }; /** * The format of a header line of the {@code time_in_state} file */ - private static final List<Integer> TIME_IN_STATE_HEADER_LINE_FORMAT = - Collections.singletonList(Process.PROC_NEWLINE_TERM); + private static final int[] TIME_IN_STATE_HEADER_LINE_FORMAT = new int[] { + Process.PROC_NEWLINE_TERM + }; /** * The format of the {@code time_in_state} file to extract times, defined using {@link @@ -166,8 +162,8 @@ public class ProcTimeInStateReader { // formats. These formats are used to extract either the frequencies or the times from a // time_in_state file // Also check if each line is a header, and handle this in the created format arrays - ArrayList<Integer> timeInStateFrequencyFormat = new ArrayList<>(); - ArrayList<Integer> timeInStateTimeFormat = new ArrayList<>(); + IntArray timeInStateFrequencyFormat = new IntArray(); + IntArray timeInStateTimeFormat = new IntArray(); int numFrequencies = 0; for (int i = 0; i < timeInStateBytes.length; i++) { // If the first character of the line is not a digit, we treat it as a header @@ -194,12 +190,12 @@ public class ProcTimeInStateReader { final long[] readLongs = new long[numFrequencies]; final boolean readSuccess = Process.parseProcLine( timeInStateBytes, 0, timeInStateBytes.length, - ArrayUtils.convertToIntArray(timeInStateFrequencyFormat), null, readLongs, null); + timeInStateFrequencyFormat.toArray(), null, readLongs, null); if (!readSuccess) { throw new IOException("Failed to parse time_in_state file"); } - mTimeInStateTimeFormat = ArrayUtils.convertToIntArray(timeInStateTimeFormat); + mTimeInStateTimeFormat = timeInStateTimeFormat.toArray(); mFrequenciesKhz = readLongs; } } diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index a985965afe17..5e20cd067589 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -172,23 +172,11 @@ public final class Zygote { public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; /** Default external storage should be mounted. */ public static final int MOUNT_EXTERNAL_DEFAULT = IVold.REMOUNT_MODE_DEFAULT; - /** Read-only external storage should be mounted. */ - public static final int MOUNT_EXTERNAL_READ = IVold.REMOUNT_MODE_READ; - /** Read-write external storage should be mounted. */ - public static final int MOUNT_EXTERNAL_WRITE = IVold.REMOUNT_MODE_WRITE; - /** - * Mount mode for apps that are already installed on the device before the isolated_storage - * feature is enabled. - */ - public static final int MOUNT_EXTERNAL_LEGACY = IVold.REMOUNT_MODE_LEGACY; /** * Mount mode for package installers which should give them access to * all obb dirs in addition to their package sandboxes */ public static final int MOUNT_EXTERNAL_INSTALLER = IVold.REMOUNT_MODE_INSTALLER; - /** Read-write external storage should be mounted instead of package sandbox */ - public static final int MOUNT_EXTERNAL_FULL = IVold.REMOUNT_MODE_FULL; - /** The lower file system should be bind mounted directly on external storage */ public static final int MOUNT_EXTERNAL_PASS_THROUGH = IVold.REMOUNT_MODE_PASS_THROUGH; diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index ed074327c3c5..32b808ab2528 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -380,16 +380,8 @@ class ZygoteArguments { mNiceName = getAssignmentValue(arg); } else if (arg.equals("--mount-external-default")) { mMountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT; - } else if (arg.equals("--mount-external-read")) { - mMountExternal = Zygote.MOUNT_EXTERNAL_READ; - } else if (arg.equals("--mount-external-write")) { - mMountExternal = Zygote.MOUNT_EXTERNAL_WRITE; - } else if (arg.equals("--mount-external-full")) { - mMountExternal = Zygote.MOUNT_EXTERNAL_FULL; - } else if (arg.equals("--mount-external-installer")) { + } else if (arg.equals("--mount-external-installer")) { mMountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER; - } else if (arg.equals("--mount-external-legacy")) { - mMountExternal = Zygote.MOUNT_EXTERNAL_LEGACY; } else if (arg.equals("--mount-external-pass-through")) { mMountExternal = Zygote.MOUNT_EXTERNAL_PASS_THROUGH; } else if (arg.equals("--mount-external-android-writable")) { diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index f317f3f4cffa..c6dea18d77d1 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -310,6 +310,10 @@ public class ArrayUtils { return total; } + /** + * @deprecated use {@code IntArray} instead + */ + @Deprecated public static int[] convertToIntArray(List<Integer> list) { int[] array = new int[list.size()]; for (int i = 0; i < list.size(); i++) { diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index a0638207a841..463d909821b1 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -60,8 +60,10 @@ static struct { jfieldID hasWallpaper; jfieldID paused; jfieldID trustedOverlay; + jfieldID touchOcclusionMode; jfieldID ownerPid; jfieldID ownerUid; + jfieldID packageName; jfieldID inputFeatures; jfieldID displayId; jfieldID portalToDisplayId; @@ -150,10 +152,13 @@ bool NativeInputWindowHandle::updateInfo() { mInfo.paused = env->GetBooleanField(obj, gInputWindowHandleClassInfo.paused); mInfo.trustedOverlay = env->GetBooleanField(obj, gInputWindowHandleClassInfo.trustedOverlay); + mInfo.touchOcclusionMode = static_cast<TouchOcclusionMode>( + env->GetIntField(obj, gInputWindowHandleClassInfo.touchOcclusionMode)); mInfo.ownerPid = env->GetIntField(obj, gInputWindowHandleClassInfo.ownerPid); mInfo.ownerUid = env->GetIntField(obj, gInputWindowHandleClassInfo.ownerUid); + mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>"); mInfo.inputFeatures = static_cast<InputWindowInfo::Feature>( env->GetIntField(obj, gInputWindowHandleClassInfo.inputFeatures)); mInfo.displayId = env->GetIntField(obj, @@ -326,12 +331,17 @@ int register_android_view_InputWindowHandle(JNIEnv* env) { GET_FIELD_ID(gInputWindowHandleClassInfo.trustedOverlay, clazz, "trustedOverlay", "Z"); + GET_FIELD_ID(gInputWindowHandleClassInfo.touchOcclusionMode, clazz, "touchOcclusionMode", "I"); + GET_FIELD_ID(gInputWindowHandleClassInfo.ownerPid, clazz, "ownerPid", "I"); GET_FIELD_ID(gInputWindowHandleClassInfo.ownerUid, clazz, "ownerUid", "I"); + GET_FIELD_ID(gInputWindowHandleClassInfo.packageName, clazz, "packageName", + "Ljava/lang/String;"); + GET_FIELD_ID(gInputWindowHandleClassInfo.inputFeatures, clazz, "inputFeatures", "I"); diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index 0892b70b9651..355ef0cd6b3f 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -612,50 +612,77 @@ static jboolean android_os_Parcel_hasFileDescriptors(jlong nativePtr) return ret; } +// String tries to allocate itself on the stack, within a known size, but will +// make a heap allocation if not. +template <size_t StackReserve> +class StackString { +public: + StackString(JNIEnv* env, jstring str) : mEnv(env), mJStr(str) { + LOG_ALWAYS_FATAL_IF(str == nullptr); + mSize = env->GetStringLength(str); + if (mSize > StackReserve) { + mStr = new jchar[mSize]; + } else { + mStr = &mBuffer[0]; + } + mEnv->GetStringRegion(str, 0, mSize, mStr); + } + ~StackString() { + if (mStr != &mBuffer[0]) { + delete[] mStr; + } + } + const jchar* str() { return mStr; } + jsize size() { return mSize; } + +private: + JNIEnv* mEnv; + jstring mJStr; + + jchar mBuffer[StackReserve]; + // pointer to &mBuffer[0] if string fits in mBuffer, otherwise owned + jchar* mStr; + jsize mSize; +}; + +// This size is chosen to be longer than most interface descriptors. +// Ones longer than this will be allocated on the heap. +typedef StackString<64> InterfaceDescriptorString; + static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jclass clazz, jlong nativePtr, jstring name) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); - if (parcel != NULL) { - // In the current implementation, the token is just the serialized interface name that - // the caller expects to be invoking - const jchar* str = env->GetStringCritical(name, 0); - if (str != NULL) { - parcel->writeInterfaceToken(String16( - reinterpret_cast<const char16_t*>(str), - env->GetStringLength(name))); - env->ReleaseStringCritical(name, str); - } + if (parcel != nullptr) { + InterfaceDescriptorString descriptor(env, name); + parcel->writeInterfaceToken(reinterpret_cast<const char16_t*>(descriptor.str()), + descriptor.size()); } } static void android_os_Parcel_enforceInterface(JNIEnv* env, jclass clazz, jlong nativePtr, jstring name) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); - if (parcel != NULL) { - const jchar* str = env->GetStringCritical(name, 0); - if (str) { - IPCThreadState* threadState = IPCThreadState::self(); - const int32_t oldPolicy = threadState->getStrictModePolicy(); - const bool isValid = parcel->enforceInterface( - reinterpret_cast<const char16_t*>(str), - env->GetStringLength(name), - threadState); - env->ReleaseStringCritical(name, str); - if (isValid) { - const int32_t newPolicy = threadState->getStrictModePolicy(); - if (oldPolicy != newPolicy) { - // Need to keep the Java-level thread-local strict - // mode policy in sync for the libcore - // enforcements, which involves an upcall back - // into Java. (We can't modify the - // Parcel.enforceInterface signature, as it's - // pseudo-public, and used via AIDL - // auto-generation...) - set_dalvik_blockguard_policy(env, newPolicy); - } - return; // everything was correct -> return silently + if (parcel != nullptr) { + InterfaceDescriptorString descriptor(env, name); + IPCThreadState* threadState = IPCThreadState::self(); + const int32_t oldPolicy = threadState->getStrictModePolicy(); + const bool isValid = + parcel->enforceInterface(reinterpret_cast<const char16_t*>(descriptor.str()), + descriptor.size(), threadState); + if (isValid) { + const int32_t newPolicy = threadState->getStrictModePolicy(); + if (oldPolicy != newPolicy) { + // Need to keep the Java-level thread-local strict + // mode policy in sync for the libcore + // enforcements, which involves an upcall back + // into Java. (We can't modify the + // Parcel.enforceInterface signature, as it's + // pseudo-public, and used via AIDL + // auto-generation...) + set_dalvik_blockguard_policy(env, newPolicy); } + return; // everything was correct -> return silently } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 1bf8efb103d4..14198559ef9c 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1333,17 +1333,6 @@ static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong transac transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber); } -static void nativeDeferTransactionUntilSurface(JNIEnv* env, jclass clazz, jlong transactionObj, - jlong nativeObject, - jlong surfaceObject, jlong frameNumber) { - auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); - - auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); - sp<Surface> barrier = reinterpret_cast<Surface *>(surfaceObject); - - transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber); -} - static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jlong newParentObject) { @@ -1699,8 +1688,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetProtectedContentSupport }, {"nativeDeferTransactionUntil", "(JJJJ)V", (void*)nativeDeferTransactionUntil }, - {"nativeDeferTransactionUntilSurface", "(JJJJ)V", - (void*)nativeDeferTransactionUntilSurface }, {"nativeReparentChildren", "(JJJ)V", (void*)nativeReparentChildren } , {"nativeReparent", "(JJJ)V", diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index e6bfeccfa8ae..42aab6ad6918 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -326,31 +326,15 @@ static std::array<UsapTableEntry, USAP_POOL_SIZE_MAX_LIMIT> gUsapTable; static FileDescriptorTable* gOpenFdTable = nullptr; // Must match values in com.android.internal.os.Zygote. -// The order of entries here must be kept in sync with ExternalStorageViews array values. +// Note that there are gaps in the constants: +// This is to further keep the values consistent with IVold.aidl enum MountExternalKind { - MOUNT_EXTERNAL_NONE = 0, - MOUNT_EXTERNAL_DEFAULT = 1, - MOUNT_EXTERNAL_READ = 2, - MOUNT_EXTERNAL_WRITE = 3, - MOUNT_EXTERNAL_LEGACY = 4, - MOUNT_EXTERNAL_INSTALLER = 5, - MOUNT_EXTERNAL_FULL = 6, - MOUNT_EXTERNAL_PASS_THROUGH = 7, - MOUNT_EXTERNAL_ANDROID_WRITABLE = 8, - MOUNT_EXTERNAL_COUNT = 9 -}; - -// The order of entries here must be kept in sync with MountExternalKind enum values. -static const std::array<const std::string, MOUNT_EXTERNAL_COUNT> ExternalStorageViews = { - "", // MOUNT_EXTERNAL_NONE - "/mnt/runtime/default", // MOUNT_EXTERNAL_DEFAULT - "/mnt/runtime/read", // MOUNT_EXTERNAL_READ - "/mnt/runtime/write", // MOUNT_EXTERNAL_WRITE - "/mnt/runtime/write", // MOUNT_EXTERNAL_LEGACY - "/mnt/runtime/write", // MOUNT_EXTERNAL_INSTALLER - "/mnt/runtime/full", // MOUNT_EXTERNAL_FULL - "/mnt/runtime/full", // MOUNT_EXTERNAL_PASS_THROUGH (only used w/ FUSE) - "/mnt/runtime/full", // MOUNT_EXTERNAL_ANDROID_WRITABLE (only used w/ FUSE) + MOUNT_EXTERNAL_NONE = 0, + MOUNT_EXTERNAL_DEFAULT = 1, + MOUNT_EXTERNAL_INSTALLER = 5, + MOUNT_EXTERNAL_PASS_THROUGH = 7, + MOUNT_EXTERNAL_ANDROID_WRITABLE = 8, + MOUNT_EXTERNAL_COUNT = 9 }; // Must match values in com.android.internal.os.Zygote. diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index d4d8772f3f81..d315ff27ef9a 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -205,9 +205,8 @@ message DisplayContentProto { optional WindowStateProto input_method_input_target = 28; optional WindowStateProto input_method_control_target = 29; optional WindowStateProto current_focus = 30; - optional InsetsSourceProviderProto insets_source_provider = 31; - optional ImeInsetsSourceProviderProto ime_insets_source_provider = 32; - optional bool can_show_ime = 33; + optional ImeInsetsSourceProviderProto ime_insets_source_provider = 31; + optional bool can_show_ime = 32; } /* represents DisplayArea object */ @@ -527,6 +526,7 @@ message InsetsSourceProviderProto { message ImeInsetsSourceProviderProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; - optional WindowStateProto ime_target_from_ime = 1; - optional bool is_ime_layout_drawn = 2; + optional InsetsSourceProviderProto insets_source_provider = 1; + optional WindowStateProto ime_target_from_ime = 2; + optional bool is_ime_layout_drawn = 3; }
\ No newline at end of file diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto index 2729e82768c3..856d8da1a1ea 100644 --- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto +++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto @@ -65,8 +65,6 @@ message InputMethodEditorProto { repeated ClientSideProto client = 1; } - /* todo: extract as a separate message to allow other dumps to use this as their client side - proto */ /* groups together the dump from ime related client side classes */ message ClientSideProto { optional int32 display_id = 1; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 57c25f141f27..c8b8ef252c2e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2722,7 +2722,7 @@ <!-- Allows applications like settings to manage configuration associated with automatic time and time zone detection. <p>Not for use by third-party applications. - @hide + @SystemApi @hide --> <permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" android:protectionLevel="signature|privileged" /> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 60f7a0993b92..fac2f2223c99 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -432,23 +432,17 @@ <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"change your audio settings"</string> <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string> <string name="permlab_recordAudio" msgid="1208457423054219147">"record audio"</string> - <!-- no translation found for permdesc_recordAudio (5857246765327514062) --> - <skip /> - <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) --> - <skip /> - <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) --> - <skip /> + <string name="permdesc_recordAudio" msgid="5857246765327514062">"This app can record audio using the microphone while the app is in use."</string> + <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"record audio in the background"</string> + <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"This app can record audio using the microphone at any time."</string> <string name="permlab_sim_communication" msgid="176788115994050692">"send commands to the SIM"</string> <string name="permdesc_sim_communication" msgid="4179799296415957960">"Allows the app to send commands to the SIM. This is very dangerous."</string> <string name="permlab_activityRecognition" msgid="1782303296053990884">"recognise physical activity"</string> <string name="permdesc_activityRecognition" msgid="8667484762991357519">"This app can recognise your physical activity."</string> <string name="permlab_camera" msgid="6320282492904119413">"take pictures and videos"</string> - <!-- no translation found for permdesc_camera (5240801376168647151) --> - <skip /> - <!-- no translation found for permlab_backgroundCamera (7549917926079731681) --> - <skip /> - <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) --> - <skip /> + <string name="permdesc_camera" msgid="5240801376168647151">"This app can take pictures and record videos using the camera while the app is in use."</string> + <string name="permlab_backgroundCamera" msgid="7549917926079731681">"take pictures and videos in the background"</string> + <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"This app can take pictures and record videos using the camera at any time."</string> <string name="permlab_systemCamera" msgid="3642917457796210580">"Grant an application or service access to system cameras to take pictures and videos"</string> <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index ae3aaab89de2..8497658c52bb 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -432,23 +432,17 @@ <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"change your audio settings"</string> <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string> <string name="permlab_recordAudio" msgid="1208457423054219147">"record audio"</string> - <!-- no translation found for permdesc_recordAudio (5857246765327514062) --> - <skip /> - <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) --> - <skip /> - <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) --> - <skip /> + <string name="permdesc_recordAudio" msgid="5857246765327514062">"This app can record audio using the microphone while the app is in use."</string> + <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"record audio in the background"</string> + <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"This app can record audio using the microphone at any time."</string> <string name="permlab_sim_communication" msgid="176788115994050692">"send commands to the SIM"</string> <string name="permdesc_sim_communication" msgid="4179799296415957960">"Allows the app to send commands to the SIM. This is very dangerous."</string> <string name="permlab_activityRecognition" msgid="1782303296053990884">"recognise physical activity"</string> <string name="permdesc_activityRecognition" msgid="8667484762991357519">"This app can recognise your physical activity."</string> <string name="permlab_camera" msgid="6320282492904119413">"take pictures and videos"</string> - <!-- no translation found for permdesc_camera (5240801376168647151) --> - <skip /> - <!-- no translation found for permlab_backgroundCamera (7549917926079731681) --> - <skip /> - <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) --> - <skip /> + <string name="permdesc_camera" msgid="5240801376168647151">"This app can take pictures and record videos using the camera while the app is in use."</string> + <string name="permlab_backgroundCamera" msgid="7549917926079731681">"take pictures and videos in the background"</string> + <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"This app can take pictures and record videos using the camera at any time."</string> <string name="permlab_systemCamera" msgid="3642917457796210580">"Grant an application or service access to system cameras to take pictures and videos"</string> <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 52dcf979a45f..12881e7e70f1 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -432,23 +432,17 @@ <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"change your audio settings"</string> <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string> <string name="permlab_recordAudio" msgid="1208457423054219147">"record audio"</string> - <!-- no translation found for permdesc_recordAudio (5857246765327514062) --> - <skip /> - <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) --> - <skip /> - <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) --> - <skip /> + <string name="permdesc_recordAudio" msgid="5857246765327514062">"This app can record audio using the microphone while the app is in use."</string> + <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"record audio in the background"</string> + <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"This app can record audio using the microphone at any time."</string> <string name="permlab_sim_communication" msgid="176788115994050692">"send commands to the SIM"</string> <string name="permdesc_sim_communication" msgid="4179799296415957960">"Allows the app to send commands to the SIM. This is very dangerous."</string> <string name="permlab_activityRecognition" msgid="1782303296053990884">"recognise physical activity"</string> <string name="permdesc_activityRecognition" msgid="8667484762991357519">"This app can recognise your physical activity."</string> <string name="permlab_camera" msgid="6320282492904119413">"take pictures and videos"</string> - <!-- no translation found for permdesc_camera (5240801376168647151) --> - <skip /> - <!-- no translation found for permlab_backgroundCamera (7549917926079731681) --> - <skip /> - <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) --> - <skip /> + <string name="permdesc_camera" msgid="5240801376168647151">"This app can take pictures and record videos using the camera while the app is in use."</string> + <string name="permlab_backgroundCamera" msgid="7549917926079731681">"take pictures and videos in the background"</string> + <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"This app can take pictures and record videos using the camera at any time."</string> <string name="permlab_systemCamera" msgid="3642917457796210580">"Grant an application or service access to system cameras to take pictures and videos"</string> <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 2f7f271678e9..cb2eb7ab17ca 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -432,23 +432,17 @@ <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"change your audio settings"</string> <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string> <string name="permlab_recordAudio" msgid="1208457423054219147">"record audio"</string> - <!-- no translation found for permdesc_recordAudio (5857246765327514062) --> - <skip /> - <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) --> - <skip /> - <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) --> - <skip /> + <string name="permdesc_recordAudio" msgid="5857246765327514062">"This app can record audio using the microphone while the app is in use."</string> + <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"record audio in the background"</string> + <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"This app can record audio using the microphone at any time."</string> <string name="permlab_sim_communication" msgid="176788115994050692">"send commands to the SIM"</string> <string name="permdesc_sim_communication" msgid="4179799296415957960">"Allows the app to send commands to the SIM. This is very dangerous."</string> <string name="permlab_activityRecognition" msgid="1782303296053990884">"recognise physical activity"</string> <string name="permdesc_activityRecognition" msgid="8667484762991357519">"This app can recognise your physical activity."</string> <string name="permlab_camera" msgid="6320282492904119413">"take pictures and videos"</string> - <!-- no translation found for permdesc_camera (5240801376168647151) --> - <skip /> - <!-- no translation found for permlab_backgroundCamera (7549917926079731681) --> - <skip /> - <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) --> - <skip /> + <string name="permdesc_camera" msgid="5240801376168647151">"This app can take pictures and record videos using the camera while the app is in use."</string> + <string name="permlab_backgroundCamera" msgid="7549917926079731681">"take pictures and videos in the background"</string> + <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"This app can take pictures and record videos using the camera at any time."</string> <string name="permlab_systemCamera" msgid="3642917457796210580">"Grant an application or service access to system cameras to take pictures and videos"</string> <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index f09e7b04bd44..450fddb025e2 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -432,23 +432,17 @@ <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"alterar as suas configurações de áudio"</string> <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string> <string name="permlab_recordAudio" msgid="1208457423054219147">"gravar áudio"</string> - <!-- no translation found for permdesc_recordAudio (5857246765327514062) --> - <skip /> - <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) --> - <skip /> - <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) --> - <skip /> + <string name="permdesc_recordAudio" msgid="5857246765327514062">"Enquanto está sendo usado, este app pode gravar áudio usando o microfone."</string> + <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"gravar áudio em segundo plano"</string> + <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Este app pode gravar áudio usando o microfone a qualquer momento."</string> <string name="permlab_sim_communication" msgid="176788115994050692">"enviar comandos para o chip"</string> <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permite que o app envie comandos ao chip. Muito perigoso."</string> <string name="permlab_activityRecognition" msgid="1782303296053990884">"reconhecer atividade física"</string> <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Este app pode reconhecer sua atividade física."</string> <string name="permlab_camera" msgid="6320282492904119413">"tirar fotos e gravar vídeos"</string> - <!-- no translation found for permdesc_camera (5240801376168647151) --> - <skip /> - <!-- no translation found for permlab_backgroundCamera (7549917926079731681) --> - <skip /> - <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) --> - <skip /> + <string name="permdesc_camera" msgid="5240801376168647151">"Enquanto está sendo usado, este app pode tirar fotos e gravar vídeos usando a câmera."</string> + <string name="permlab_backgroundCamera" msgid="7549917926079731681">"tirar fotos e gravar vídeos em segundo plano"</string> + <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Este app pode tirar fotos e gravar vídeos usando a câmera a qualquer momento."</string> <string name="permlab_systemCamera" msgid="3642917457796210580">"Permitir que um aplicativo ou serviço acesse as câmeras do sistema para tirar fotos e gravar vídeos"</string> <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esse app do sistema ou com privilégios pode tirar fotos e gravar vídeos a qualquer momento usando a câmera do sistema. É necessário que o app tenha também a permissão android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que um aplicativo ou serviço receba callbacks sobre dispositivos de câmera sendo abertos ou fechados."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index f09e7b04bd44..450fddb025e2 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -432,23 +432,17 @@ <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"alterar as suas configurações de áudio"</string> <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string> <string name="permlab_recordAudio" msgid="1208457423054219147">"gravar áudio"</string> - <!-- no translation found for permdesc_recordAudio (5857246765327514062) --> - <skip /> - <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) --> - <skip /> - <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) --> - <skip /> + <string name="permdesc_recordAudio" msgid="5857246765327514062">"Enquanto está sendo usado, este app pode gravar áudio usando o microfone."</string> + <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"gravar áudio em segundo plano"</string> + <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Este app pode gravar áudio usando o microfone a qualquer momento."</string> <string name="permlab_sim_communication" msgid="176788115994050692">"enviar comandos para o chip"</string> <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permite que o app envie comandos ao chip. Muito perigoso."</string> <string name="permlab_activityRecognition" msgid="1782303296053990884">"reconhecer atividade física"</string> <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Este app pode reconhecer sua atividade física."</string> <string name="permlab_camera" msgid="6320282492904119413">"tirar fotos e gravar vídeos"</string> - <!-- no translation found for permdesc_camera (5240801376168647151) --> - <skip /> - <!-- no translation found for permlab_backgroundCamera (7549917926079731681) --> - <skip /> - <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) --> - <skip /> + <string name="permdesc_camera" msgid="5240801376168647151">"Enquanto está sendo usado, este app pode tirar fotos e gravar vídeos usando a câmera."</string> + <string name="permlab_backgroundCamera" msgid="7549917926079731681">"tirar fotos e gravar vídeos em segundo plano"</string> + <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Este app pode tirar fotos e gravar vídeos usando a câmera a qualquer momento."</string> <string name="permlab_systemCamera" msgid="3642917457796210580">"Permitir que um aplicativo ou serviço acesse as câmeras do sistema para tirar fotos e gravar vídeos"</string> <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esse app do sistema ou com privilégios pode tirar fotos e gravar vídeos a qualquer momento usando a câmera do sistema. É necessário que o app tenha também a permissão android.permission.CAMERA"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que um aplicativo ou serviço receba callbacks sobre dispositivos de câmera sendo abertos ou fechados."</string> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 96ebc127e9ba..25c64a9f8781 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2016,6 +2016,22 @@ <attr name="requiredNotFeature" format="string" /> </declare-styleable> + <!-- <code>required-feature</code> and <code>required-not-feature</code> elements inside + <code>uses-permission<code/> can be used to request the permission based on the fact + whether the system supports or does not support certain features. + If multiple <code>required-feature</code> and/or <code>required-not-feature</code> elements + are present, the permission will be “requested” only if the system supports all of the + listed "required-features" and does not support any of the "required-not-features". + --> + <declare-styleable name="AndroidManifestRequiredFeature"> + <!-- The name of the feature. --> + <attr name="name" /> + </declare-styleable> + <declare-styleable name="AndroidManifestRequiredNotFeature"> + <!-- The name of the feature. --> + <attr name="name" /> + </declare-styleable> + <!-- The <code>uses-configuration</code> tag specifies a specific hardware configuration value used by the application. For example an application might specify that it requires diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 71e9ed7bee58..0cfbf6d415b9 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1588,6 +1588,16 @@ config_enablePrimaryLocationTimeZoneOverlay is false. --> <string name="config_primaryLocationTimeZoneProviderPackageName" translatable="false">@null</string> + <!-- Whether to enable secondary location time zone provider overlay which allows the secondary + location time zone provider to be replaced by an app at run-time. When disabled, only the + config_secondaryLocationTimeZoneProviderPackageName package will be searched for the + secondary location time zone provider, otherwise any system package is eligible. Anyone who + wants to disable the overlay mechanism can set it to false. --> + <bool name="config_enableSecondaryLocationTimeZoneOverlay" translatable="false">false</bool> + <!-- Package name providing the secondary location time zone provider. Used only when + config_enableSecondaryLocationTimeZoneOverlay is false. --> + <string name="config_secondaryLocationTimeZoneProviderPackageName" translatable="false">@null</string> + <!-- Whether to enable network location overlay which allows network location provider to be replaced by an app at run-time. When disabled, only the config_networkLocationProviderPackageName package will be searched for network location @@ -1831,6 +1841,8 @@ <string name="config_systemGallery" translatable="false">com.android.gallery3d</string> <!-- The name of the package that will hold the system cluster service role. --> <string name="config_systemAutomotiveCluster" translatable="false"></string> + <!-- The name of the package that will hold the system video call role. --> + <string name="config_systemVideoCall" translatable="false"></string> <!-- The name of the package that will be allowed to change its components' label/icon. --> <string name="config_overrideComponentUiPackage" translatable="false"></string> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 1e2d554a088b..fe17eca35545 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3071,6 +3071,8 @@ <public-group type="string" first-id="0x01040028"> <!-- @hide @SystemApi @TestApi --> <public name="config_systemAutomotiveCluster" /> + <!-- @hide @SystemApi @TestApi --> + <public name="config_systemVideoCall" /> </public-group> <public-group type="id" first-id="0x01020055"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3e595493d07d..d896a265a755 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2160,6 +2160,8 @@ <java-symbol type="string" name="config_deviceConfiguratorPackageName" /> <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" /> <java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" /> + <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneOverlay" /> + <java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" /> <java-symbol type="layout" name="resolver_list" /> <java-symbol type="id" name="resolver_list" /> @@ -3545,6 +3547,7 @@ <java-symbol type="id" name="bubble_button" /> <java-symbol type="dimen" name="bubble_visible_padding_end" /> <java-symbol type="dimen" name="bubble_gone_padding_end" /> + <java-symbol type="dimen" name="text_size_body_2_material" /> <java-symbol type="dimen" name="messaging_avatar_size" /> <java-symbol type="dimen" name="messaging_group_sending_progress_size" /> <java-symbol type="dimen" name="messaging_image_rounding" /> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 8b25afba228e..cb20130e742d 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -51,12 +51,14 @@ import android.content.res.Resources; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.IBinder; +import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.util.MergedConfiguration; import android.view.Display; import android.view.View; import androidx.test.InstrumentationRegistry; +import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; @@ -78,6 +80,8 @@ import java.util.concurrent.TimeUnit; */ @RunWith(AndroidJUnit4.class) @MediumTest +@Presubmit +@FlakyTest(detail = "Promote once confirmed non-flaky") public class ActivityThreadTest { private static final int TIMEOUT_SEC = 10; @@ -545,14 +549,15 @@ public class ActivityThreadTest { rIntents.add(new ReferrerIntent(new Intent(), "android.app.activity")); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, false)); + activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, true)); }); - assertThat(activity.isResumed()).isFalse(); + assertThat(activity.isResumed()).isTrue(); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, true)); + activityThread.executeTransaction(newStopTransaction(activity)); + activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, false)); }); - assertThat(activity.isResumed()).isTrue(); + assertThat(activity.isResumed()).isFalse(); } @Test diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java index b669cc609baf..a9cfd286688b 100644 --- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java @@ -17,8 +17,10 @@ package com.android.internal.jank; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; @@ -45,6 +47,13 @@ import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + @SmallTest public class InteractionJankMonitorTest { private ViewAttachTestActivity mActivity; @@ -138,4 +147,27 @@ public class InteractionJankMonitorTest { verify(tracker).cancel(); } + @Test + public void testCujTypeEnumCorrectlyDefined() throws Exception { + List<Field> cujEnumFields = + Arrays.stream(InteractionJankMonitor.class.getDeclaredFields()) + .filter(field -> field.getName().startsWith("CUJ_") + && Modifier.isStatic(field.getModifiers()) + && field.getType() == int.class) + .collect(Collectors.toList()); + + HashSet<Integer> allValues = new HashSet<>(); + for (Field field : cujEnumFields) { + int fieldValue = field.getInt(null); + assertWithMessage( + "Field %s must have a mapping to a value in CUJ_TO_STATSD_INTERACTION_TYPE", + field.getName()) + .that(fieldValue < CUJ_TO_STATSD_INTERACTION_TYPE.length) + .isTrue(); + assertWithMessage("All CujType values must be unique. Field %s repeats existing value.", + field.getName()) + .that(allValues.add(fieldValue)) + .isTrue(); + } + } } diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp index 4b43b4136076..ca655f18149c 100644 --- a/core/xsd/vts/Android.bp +++ b/core/xsd/vts/Android.bp @@ -40,7 +40,3 @@ cc_test { ], test_config: "vts_permission_validate_test.xml", } - -vts_config { - name: "VtsValidatePermission", -} diff --git a/core/xsd/vts/AndroidTest.xml b/core/xsd/vts/AndroidTest.xml deleted file mode 100644 index e5cc9a0f74ee..000000000000 --- a/core/xsd/vts/AndroidTest.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Config for VTS VtsValidatePermission."> - <option name="config-descriptor:metadata" key="plan" value="vts-treble" /> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher"> - <option name="abort-on-push-failure" value="false"/> - <option name="push-group" value="HostDrivenTest.push"/> - <option name="push" value="DATA/etc/permission.xsd->/data/local/tmp/permission.xsd"/> - </target_preparer> - <test class="com.android.tradefed.testtype.VtsMultiDeviceTest"> - <option name="test-module-name" value="VtsValidatePermission"/> - <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_permission_validate_test/vts_permission_validate_test" /> - <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_permission_validate_test/vts_permission_validate_test" /> - <option name="binary-test-type" value="gtest"/> - <option name="test-timeout" value="30s"/> - </test> -</configuration> diff --git a/data/etc/com.android.storagemanager.xml b/data/etc/com.android.storagemanager.xml index e85a82c983df..a1635fe5548b 100644 --- a/data/etc/com.android.storagemanager.xml +++ b/data/etc/com.android.storagemanager.xml @@ -22,5 +22,6 @@ <permission name="android.permission.PACKAGE_USAGE_STATS"/> <permission name="android.permission.USE_RESERVED_DISK"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 29c8de63fc31..b94721e37138 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -115,12 +115,6 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" }, - "-1977793524": { - "message": "moveStackToDisplay: moving stackId=%d to displayId=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, "-1976930686": { "message": "Attempted to add Accessibility overlay window with bad token %s. Aborting.", "level": "WARN", @@ -583,6 +577,12 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1419762046": { + "message": "moveRootTaskToDisplay: moving taskId=%d to displayId=%d", + "level": "DEBUG", + "group": "WM_DEBUG_TASKS", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "-1413901262": { "message": "startRecentsActivity(): intent=%s", "level": "DEBUG", @@ -1183,6 +1183,12 @@ "group": "WM_DEBUG_SYNC_ENGINE", "at": "com\/android\/server\/wm\/BLASTSyncEngine.java" }, + "-677449371": { + "message": "moveTaskToRootTask: moving task=%d to rootTaskId=%d toTop=%b", + "level": "DEBUG", + "group": "WM_DEBUG_TASKS", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "-672228342": { "message": "resumeTopActivityLocked: Top activity resumed %s", "level": "DEBUG", @@ -1555,12 +1561,6 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, - "-300896109": { - "message": "moveTaskToStack: moving task=%d to stackId=%d toTop=%b", - "level": "DEBUG", - "group": "WM_DEBUG_TASKS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, "-279436615": { "message": "Moving to PAUSING: %s", "level": "VERBOSE", @@ -1999,6 +1999,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "255339989": { + "message": "setFocusedRootTask: taskId=%d", + "level": "DEBUG", + "group": "WM_DEBUG_FOCUS", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "255692476": { "message": "**** GOOD TO GO", "level": "VERBOSE", @@ -3361,12 +3367,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, - "1975793405": { - "message": "setFocusedStack: stackId=%d", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" - }, "1984470582": { "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d", "level": "DEBUG", diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java new file mode 100644 index 000000000000..68477edf97d1 --- /dev/null +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java @@ -0,0 +1,108 @@ +/* + * 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.google.errorprone.bugpatterns.android; + +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static com.google.errorprone.matchers.Matchers.contains; +import static com.google.errorprone.matchers.Matchers.methodInvocation; +import static com.google.errorprone.matchers.Matchers.staticMethod; + +import com.google.auto.service.AutoService; +import com.google.errorprone.BugPattern; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher; +import com.google.errorprone.matchers.Description; +import com.google.errorprone.matchers.Matcher; +import com.sun.source.tree.BlockTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.StatementTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.tree.TryTree; +import com.sun.source.tree.VariableTree; + +import java.util.List; + +import javax.lang.model.element.Modifier; + +/** + * Binder maintains thread-local identity information about any remote caller, + * which can be temporarily cleared while performing operations that need to be + * handled as the current process. However, it's important to restore the + * original remote calling identity after carefully scoping this work inside a + * try/finally block, to avoid obscure security vulnerabilities. + */ +@AutoService(BugChecker.class) +@BugPattern( + name = "AndroidFrameworkBinderIdentity", + summary = "Verifies that Binder.clearCallingIdentity() is always restored", + severity = WARNING) +public final class BinderIdentityChecker extends BugChecker implements MethodInvocationTreeMatcher { + private static final Matcher<ExpressionTree> CLEAR_CALL = methodInvocation(staticMethod() + .onClass("android.os.Binder").withSignature("clearCallingIdentity()")); + private static final Matcher<ExpressionTree> RESTORE_CALL = methodInvocation(staticMethod() + .onClass("android.os.Binder").withSignature("restoreCallingIdentity(long)")); + + @Override + public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { + if (CLEAR_CALL.matches(tree, state)) { + // First, make sure we're recording the token for later + final VariableTree token = state.findEnclosing(VariableTree.class); + if (token == null || !token.getModifiers().getFlags().contains(Modifier.FINAL)) { + return buildDescription(tree) + .setMessage("Must store Binder.clearCallingIdentity() token as final" + + " variable to support safe restore") + .build(); + } + + // Next, verify the very next block is try-finally; any other calls + // between the clearing and try risk throwing an exception without + // doing a safe restore + final Tree next = nextStatement(token, state); + if (next == null || next.getKind() != Kind.TRY) { + return buildDescription(tree) + .setMessage("Must immediately define a try-finally block after" + + " Binder.clearCallingIdentity() to support safe restore") + .build(); + } + + // Finally, verify that we restore inside the finally block + final TryTree tryTree = (TryTree) next; + final BlockTree finallyTree = tryTree.getFinallyBlock(); + if (finallyTree == null + || !contains(ExpressionTree.class, RESTORE_CALL).matches(finallyTree, state)) { + return buildDescription(tree) + .setMessage("Must call Binder.restoreCallingIdentity() in finally" + + " block to support safe restore") + .build(); + } + } + return Description.NO_MATCH; + } + + private static Tree nextStatement(Tree tree, VisitorState state) { + final BlockTree block = state.findEnclosing(BlockTree.class); + if (block == null) return null; + final List<? extends StatementTree> siblings = block.getStatements(); + if (siblings == null) return null; + final int index = siblings.indexOf(tree); + if (index == -1 || index + 1 >= siblings.size()) return null; + return siblings.get(index + 1); + } +} diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java index 8829b0d3c649..9c84f50b76bb 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java @@ -17,12 +17,13 @@ package com.google.errorprone.bugpatterns.android; import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static com.google.errorprone.bugpatterns.android.TargetSdkChecker.binaryTreeExact; import static com.google.errorprone.matchers.FieldMatchers.anyFieldInClass; import static com.google.errorprone.matchers.FieldMatchers.staticField; import static com.google.errorprone.matchers.Matchers.allOf; import static com.google.errorprone.matchers.Matchers.anyOf; import static com.google.errorprone.matchers.Matchers.anything; -import static com.google.errorprone.matchers.Matchers.binaryTree; +import static com.google.errorprone.matchers.Matchers.kindIs; import static com.google.errorprone.matchers.Matchers.not; import com.google.auto.service.AutoService; @@ -34,6 +35,7 @@ import com.google.errorprone.matchers.Description; import com.google.errorprone.matchers.Matcher; import com.sun.source.tree.BinaryTree; import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.Tree.Kind; /** * Each SDK level often has dozens of different behavior changes, which can be @@ -85,14 +87,28 @@ public final class CompatChangeChecker extends BugChecker implements BinaryTreeM staticField("android.os.Build.VERSION_CODES", "O"), staticField("android.os.Build.VERSION_CODES", "O_MR1"), staticField("android.os.Build.VERSION_CODES", "P"), - staticField("android.os.Build.VERSION_CODES", "Q")); + staticField("android.os.Build.VERSION_CODES", "Q"), + staticField("android.os.Build.VERSION_CODES", "R")); + + private static final Matcher<ExpressionTree> R_VERSION_CODE = + staticField("android.os.Build.VERSION_CODES", "R"); + + private static final Matcher<ExpressionTree> CUR_DEVELOPMENT_VERSION_CODE = + staticField("android.os.Build.VERSION_CODES", "CUR_DEVELOPMENT"); private static final Matcher<ExpressionTree> MODERN_VERSION_CODE = - allOf(VERSION_CODE, not(LEGACY_VERSION_CODE)); + allOf(VERSION_CODE, not(LEGACY_VERSION_CODE), not(CUR_DEVELOPMENT_VERSION_CODE)); + + private static final Matcher<ExpressionTree> BOOLEAN_OPERATOR = anyOf( + kindIs(Kind.LESS_THAN), kindIs(Kind.LESS_THAN_EQUAL), + kindIs(Kind.GREATER_THAN), kindIs(Kind.GREATER_THAN_EQUAL), + kindIs(Kind.EQUAL_TO), kindIs(Kind.NOT_EQUAL_TO)); private static final Matcher<BinaryTree> INVALID = anyOf( - binaryTree(MODERN_VERSION_CODE, anything()), - binaryTree(anything(), MODERN_VERSION_CODE)); + allOf(BOOLEAN_OPERATOR, binaryTreeExact(MODERN_VERSION_CODE, anything())), + allOf(BOOLEAN_OPERATOR, binaryTreeExact(anything(), MODERN_VERSION_CODE)), + allOf(kindIs(Kind.GREATER_THAN), binaryTreeExact(anything(), R_VERSION_CODE)), + allOf(kindIs(Kind.LESS_THAN), binaryTreeExact(R_VERSION_CODE, anything()))); @Override public Description matchBinary(BinaryTree tree, VisitorState state) { diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java index 4f1af3e9bea2..3a1bc1eeb9ae 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java @@ -18,6 +18,7 @@ package com.google.errorprone.bugpatterns.android; import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; import static com.google.errorprone.bugpatterns.android.UidChecker.getFlavor; +import static com.google.errorprone.matchers.Matchers.anyOf; import static com.google.errorprone.matchers.Matchers.enclosingClass; import static com.google.errorprone.matchers.Matchers.hasAnnotation; import static com.google.errorprone.matchers.Matchers.instanceMethod; @@ -60,8 +61,13 @@ public final class ContextUserIdChecker extends BugChecker implements MethodInvo private static final Matcher<ExpressionTree> BINDER_CALL = methodInvocation( instanceMethod().onDescendantOf("android.os.IInterface").withAnyName()); - private static final Matcher<ExpressionTree> GET_USER_ID_CALL = methodInvocation( - instanceMethod().onDescendantOf("android.content.Context").named("getUserId")); + private static final Matcher<ExpressionTree> GET_USER_ID_CALL = methodInvocation(anyOf( + instanceMethod().onExactClass("android.app.admin.DevicePolicyManager") + .named("myUserId"), + instanceMethod().onExactClass("android.content.pm.ShortcutManager") + .named("injectMyUserId"), + instanceMethod().onDescendantOf("android.content.Context") + .named("getUserId"))); private static final Matcher<ExpressionTree> USER_ID_FIELD = new Matcher<ExpressionTree>() { @Override diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java new file mode 100644 index 000000000000..c4c1ab6482ee --- /dev/null +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java @@ -0,0 +1,98 @@ +/* + * 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.google.errorprone.bugpatterns.android; + +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static com.google.errorprone.matchers.Matchers.isSubtypeOf; + +import com.google.auto.service.AutoService; +import com.google.errorprone.BugPattern; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher; +import com.google.errorprone.matchers.Description; +import com.google.errorprone.matchers.Matcher; +import com.google.errorprone.util.ASTHelpers; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Tree; +import com.sun.tools.javac.code.Type; + +import java.util.Collections; +import java.util.List; + +/** + * Android offers several efficient alternatives to some upstream + * {@link Collections} containers, such as {@code SparseIntArray} instead of + * {@code Map<Integer, Integer>}. + */ +@AutoService(BugChecker.class) +@BugPattern( + name = "AndroidFrameworkEfficientCollections", + summary = "Verifies efficient collections best-practices", + severity = WARNING) +public final class EfficientCollectionsChecker extends BugChecker implements NewClassTreeMatcher { + private static final Matcher<Tree> IS_LIST = isSubtypeOf("java.util.List"); + private static final Matcher<Tree> IS_MAP = isSubtypeOf("java.util.Map"); + + private static final String INTEGER = "java.lang.Integer"; + private static final String LONG = "java.lang.Long"; + private static final String BOOLEAN = "java.lang.Boolean"; + + @Override + public Description matchNewClass(NewClassTree tree, VisitorState state) { + final List<Type> types = ASTHelpers.getType(tree).getTypeArguments(); + if (IS_LIST.matches(tree, state) && types != null && types.size() == 1) { + final Type first = types.get(0); + if (ASTHelpers.isSameType(first, state.getTypeFromString(INTEGER), state)) { + return buildDescription(tree) + .setMessage("Consider replacing with IntArray for efficiency") + .build(); + } else if (ASTHelpers.isSameType(first, state.getTypeFromString(LONG), state)) { + return buildDescription(tree) + .setMessage("Consider replacing with LongArray for efficiency") + .build(); + } + } else if (IS_MAP.matches(tree, state) && types != null && types.size() == 2) { + final Type first = types.get(0); + final Type second = types.get(1); + if (ASTHelpers.isSameType(first, state.getTypeFromString(INTEGER), state)) { + if (ASTHelpers.isSameType(second, state.getTypeFromString(INTEGER), state)) { + return buildDescription(tree) + .setMessage("Consider replacing with SparseIntArray for efficiency") + .build(); + } else if (ASTHelpers.isSameType(second, state.getTypeFromString(LONG), state)) { + return buildDescription(tree) + .setMessage("Consider replacing with SparseLongArray for efficiency") + .build(); + } else if (ASTHelpers.isSameType(second, state.getTypeFromString(BOOLEAN), state)) { + return buildDescription(tree) + .setMessage("Consider replacing with SparseBooleanArray for efficiency") + .build(); + } else { + return buildDescription(tree) + .setMessage("Consider replacing with SparseArray for efficiency") + .build(); + } + } else if (ASTHelpers.isSameType(first, state.getTypeFromString(LONG), state)) { + return buildDescription(tree) + .setMessage("Consider replacing with LongSparseArray for efficiency") + .build(); + } + } + return Description.NO_MATCH; + } +} diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java index 48123abd26cb..130b256e6622 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java @@ -17,11 +17,14 @@ package com.google.errorprone.bugpatterns.android; import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static com.google.errorprone.matchers.Matchers.allOf; +import static com.google.errorprone.matchers.Matchers.contains; import static com.google.errorprone.matchers.Matchers.enclosingClass; import static com.google.errorprone.matchers.Matchers.hasAnnotation; import static com.google.errorprone.matchers.Matchers.instanceMethod; import static com.google.errorprone.matchers.Matchers.isSameType; import static com.google.errorprone.matchers.Matchers.methodInvocation; +import static com.google.errorprone.matchers.Matchers.not; import static com.google.errorprone.matchers.Matchers.throwStatement; import static com.google.errorprone.matchers.Matchers.variableType; @@ -29,13 +32,17 @@ import com.google.auto.service.AutoService; import com.google.errorprone.BugPattern; import com.google.errorprone.VisitorState; import com.google.errorprone.bugpatterns.BugChecker; -import com.google.errorprone.bugpatterns.BugChecker.CatchTreeMatcher; +import com.google.errorprone.bugpatterns.BugChecker.TryTreeMatcher; import com.google.errorprone.matchers.Description; import com.google.errorprone.matchers.Matcher; +import com.google.errorprone.predicates.TypePredicate; import com.sun.source.tree.CatchTree; +import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.StatementTree; import com.sun.source.tree.Tree; +import com.sun.source.tree.TryTree; import com.sun.source.tree.VariableTree; +import com.sun.tools.javac.code.Type; import java.util.List; @@ -54,9 +61,17 @@ import java.util.List; name = "AndroidFrameworkRethrowFromSystem", summary = "Verifies that system_server calls use rethrowFromSystemServer()", severity = WARNING) -public final class RethrowFromSystemChecker extends BugChecker implements CatchTreeMatcher { +public final class RethrowFromSystemChecker extends BugChecker implements TryTreeMatcher { private static final Matcher<Tree> INSIDE_MANAGER = enclosingClass(hasAnnotation("android.annotation.SystemService")); + + // Purposefully exclude telephony Binder interfaces, since we know they + // always run under the separate AID_RADIO + private static final Matcher<ExpressionTree> SYSTEM_BINDER_CALL = methodInvocation(allOf( + instanceMethod().onDescendantOf("android.os.IInterface").withAnyName(), + not(instanceMethod().onClass(inPackage("com.android.internal.telephony"))), + not(instanceMethod().onClass(inPackage("com.android.internal.telecom"))))); + private static final Matcher<VariableTree> REMOTE_EXCEPTION = variableType( isSameType("android.os.RemoteException")); private static final Matcher<StatementTree> RETHROW_FROM_SYSTEM = throwStatement( @@ -64,17 +79,33 @@ public final class RethrowFromSystemChecker extends BugChecker implements CatchT .named("rethrowFromSystemServer"))); @Override - public Description matchCatch(CatchTree tree, VisitorState state) { + public Description matchTry(TryTree tree, VisitorState state) { if (INSIDE_MANAGER.matches(tree, state) - && REMOTE_EXCEPTION.matches(tree.getParameter(), state)) { - final List<? extends StatementTree> statements = tree.getBlock().getStatements(); - if (statements.size() != 1 || !RETHROW_FROM_SYSTEM.matches(statements.get(0), state)) { - return buildDescription(tree) - .setMessage("Must contain single " - + "'throw e.rethrowFromSystemServer()' statement") - .build(); + && contains(ExpressionTree.class, SYSTEM_BINDER_CALL) + .matches(tree.getBlock(), state)) { + for (CatchTree catchTree : tree.getCatches()) { + if (REMOTE_EXCEPTION.matches(catchTree.getParameter(), state)) { + final List<? extends StatementTree> statements = catchTree.getBlock() + .getStatements(); + if (statements.size() != 1 + || !RETHROW_FROM_SYSTEM.matches(statements.get(0), state)) { + return buildDescription(catchTree) + .setMessage("Must contain single " + + "'throw e.rethrowFromSystemServer()' statement") + .build(); + } + } } } return Description.NO_MATCH; } + + private static TypePredicate inPackage(final String filter) { + return new TypePredicate() { + @Override + public boolean apply(Type type, VisitorState state) { + return type.tsym.packge().fullname.toString().startsWith(filter); + } + }; + } } diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java index 232cf3f0d677..e1ebf42fec19 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java @@ -89,7 +89,7 @@ public final class TargetSdkChecker extends BugChecker implements BinaryTreeMatc return Description.NO_MATCH; } - private static Matcher<BinaryTree> binaryTreeExact(Matcher<ExpressionTree> left, + static Matcher<BinaryTree> binaryTreeExact(Matcher<ExpressionTree> left, Matcher<ExpressionTree> right) { return new Matcher<BinaryTree>() { @Override diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/BinderIdentityCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/BinderIdentityCheckerTest.java new file mode 100644 index 000000000000..9448344f7abb --- /dev/null +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/BinderIdentityCheckerTest.java @@ -0,0 +1,111 @@ +/* + * 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.google.errorprone.bugpatterns.android; + +import com.google.errorprone.CompilationTestHelper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class BinderIdentityCheckerTest { + private CompilationTestHelper compilationHelper; + + @Before + public void setUp() { + compilationHelper = CompilationTestHelper.newInstance( + BinderIdentityChecker.class, getClass()); + } + + @Test + public void testValid() { + compilationHelper + .addSourceFile("/android/os/Binder.java") + .addSourceLines("FooService.java", + "import android.os.Binder;", + "public class FooService {", + " void bar() {", + " final long token = Binder.clearCallingIdentity();", + " try {", + " FooService.class.toString();", + " } finally {", + " Binder.restoreCallingIdentity(token);", + " }", + " }", + "}") + .doTest(); + } + + @Test + public void testInvalid() { + compilationHelper + .addSourceFile("/android/os/Binder.java") + .addSourceLines("FooService.java", + "import android.os.Binder;", + "public class FooService {", + " void noRestore() {", + " // BUG: Diagnostic contains:", + " final long token = Binder.clearCallingIdentity();", + " FooService.class.toString();", + " }", + " void noTry() {", + " // BUG: Diagnostic contains:", + " final long token = Binder.clearCallingIdentity();", + " FooService.class.toString();", + " Binder.restoreCallingIdentity(token);", + " }", + " void noImmediateTry() {", + " // BUG: Diagnostic contains:", + " final long token = Binder.clearCallingIdentity();", + " FooService.class.toString();", + " try {", + " FooService.class.toString();", + " } finally {", + " Binder.restoreCallingIdentity(token);", + " }", + " }", + " void noFinally() {", + " // BUG: Diagnostic contains:", + " final long token = Binder.clearCallingIdentity();", + " try {", + " FooService.class.toString();", + " } catch (Exception ignored) { }", + " }", + " void noFinal() {", + " // BUG: Diagnostic contains:", + " long token = Binder.clearCallingIdentity();", + " try {", + " FooService.class.toString();", + " } finally {", + " Binder.restoreCallingIdentity(token);", + " }", + " }", + " void noRecording() {", + " // BUG: Diagnostic contains:", + " Binder.clearCallingIdentity();", + " FooService.class.toString();", + " }", + " void noWork() {", + " // BUG: Diagnostic contains:", + " final long token = Binder.clearCallingIdentity();", + " }", + "}") + .doTest(); + } +} diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/CompatChangeCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/CompatChangeCheckerTest.java index 105633e4de28..4625d43a1648 100644 --- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/CompatChangeCheckerTest.java +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/CompatChangeCheckerTest.java @@ -42,17 +42,17 @@ public class CompatChangeCheckerTest { "public class Example {", " void test(int targetSdkVersion) {", " // BUG: Diagnostic contains:", - " if (targetSdkVersion < Build.VERSION_CODES.R) { }", + " if (targetSdkVersion < Build.VERSION_CODES.S) { }", " // BUG: Diagnostic contains:", - " if (targetSdkVersion <= Build.VERSION_CODES.R) { }", + " if (targetSdkVersion <= Build.VERSION_CODES.S) { }", " // BUG: Diagnostic contains:", - " if (targetSdkVersion > Build.VERSION_CODES.R) { }", + " if (targetSdkVersion > Build.VERSION_CODES.S) { }", " // BUG: Diagnostic contains:", - " if (targetSdkVersion >= Build.VERSION_CODES.R) { }", + " if (targetSdkVersion >= Build.VERSION_CODES.S) { }", " // BUG: Diagnostic contains:", - " if (targetSdkVersion == Build.VERSION_CODES.R) { }", + " if (targetSdkVersion == Build.VERSION_CODES.S) { }", " // BUG: Diagnostic contains:", - " if (targetSdkVersion != Build.VERSION_CODES.R) { }", + " if (targetSdkVersion != Build.VERSION_CODES.S) { }", " }", "}") .doTest(); @@ -63,18 +63,29 @@ public class CompatChangeCheckerTest { compilationHelper .addSourceFile("/android/os/Build.java") .addSourceLines("Example.java", - "import static android.os.Build.VERSION_CODES.R;", + "import android.os.Build;", + "import static android.os.Build.VERSION_CODES.S;", "public class Example {", " void test(int targetSdkVersion) {", " // BUG: Diagnostic contains:", - " boolean indirect = R > targetSdkVersion;", + " boolean indirect = S >= targetSdkVersion;", + " // BUG: Diagnostic contains:", + " if (targetSdkVersion > Build.VERSION_CODES.R) { }", + " if (targetSdkVersion >= Build.VERSION_CODES.R) { }", + " if (targetSdkVersion < Build.VERSION_CODES.R) { }", + " if (targetSdkVersion <= Build.VERSION_CODES.R) { }", + " // BUG: Diagnostic contains:", + " if (Build.VERSION_CODES.R < targetSdkVersion) { }", + " if (Build.VERSION_CODES.R <= targetSdkVersion) { }", + " if (Build.VERSION_CODES.R > targetSdkVersion) { }", + " if (Build.VERSION_CODES.R >= targetSdkVersion) { }", " }", "}") .doTest(); } @Test - public void testLegacyIgnored() { + public void testIgnored() { compilationHelper .addSourceFile("/android/os/Build.java") .addSourceLines("Example.java", @@ -82,6 +93,8 @@ public class CompatChangeCheckerTest { "public class Example {", " void test(int targetSdkVersion) {", " if (targetSdkVersion < Build.VERSION_CODES.DONUT) { }", + " String result = \"test\" + Build.VERSION_CODES.S;", + " if (targetSdkVersion != Build.VERSION_CODES.CUR_DEVELOPMENT) { }", " }", "}") .doTest(); diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java index c0b8cd745afc..c8772306a59b 100644 --- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java @@ -92,4 +92,56 @@ public class ContextUserIdCheckerTest { "}") .doTest(); } + + @Test + public void testDevicePolicyManager() { + compilationHelper + .addSourceFile("/android/annotation/SystemService.java") + .addSourceFile("/android/content/Context.java") + .addSourceFile("/android/foo/IFooService.java") + .addSourceFile("/android/os/IInterface.java") + .addSourceFile("/android/os/UserHandle.java") + .addSourceFile("/android/os/RemoteException.java") + .addSourceLines("DevicePolicyManager.java", + "package android.app.admin;", + "import android.annotation.SystemService;", + "import android.content.Context;", + "import android.foo.IFooService;", + "import android.os.UserHandle;", + "import android.os.RemoteException;", + "@SystemService(\"dp\") public class DevicePolicyManager {", + " IFooService mService;", + " int myUserId() { return 0; }", + " void bar() throws RemoteException {", + " mService.baz(null, myUserId());", + " }", + "}") + .doTest(); + } + + @Test + public void testShortcutManager() { + compilationHelper + .addSourceFile("/android/annotation/SystemService.java") + .addSourceFile("/android/content/Context.java") + .addSourceFile("/android/foo/IFooService.java") + .addSourceFile("/android/os/IInterface.java") + .addSourceFile("/android/os/UserHandle.java") + .addSourceFile("/android/os/RemoteException.java") + .addSourceLines("ShortcutManager.java", + "package android.content.pm;", + "import android.annotation.SystemService;", + "import android.content.Context;", + "import android.foo.IFooService;", + "import android.os.UserHandle;", + "import android.os.RemoteException;", + "@SystemService(\"shortcut\") public class ShortcutManager {", + " IFooService mService;", + " int injectMyUserId() { return 0; }", + " void bar() throws RemoteException {", + " mService.baz(null, injectMyUserId());", + " }", + "}") + .doTest(); + } } diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java new file mode 100644 index 000000000000..e128b6ad2cfd --- /dev/null +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java @@ -0,0 +1,96 @@ +/* + * 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.google.errorprone.bugpatterns.android; + +import com.google.errorprone.CompilationTestHelper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class EfficientCollectionsCheckerTest { + private CompilationTestHelper compilationHelper; + + @Before + public void setUp() { + compilationHelper = CompilationTestHelper.newInstance( + EfficientCollectionsChecker.class, getClass()); + } + + @Test + public void testMap() { + compilationHelper + .addSourceLines("Example.java", + "import java.util.HashMap;", + "public class Example {", + " public void exampleInteger() {", + " // BUG: Diagnostic contains:", + " HashMap<Integer, Integer> a = new HashMap<>();", + " // BUG: Diagnostic contains:", + " HashMap<Integer, Long> b = new HashMap<>();", + " // BUG: Diagnostic contains:", + " HashMap<Integer, Boolean> c = new HashMap<>();", + " // BUG: Diagnostic contains:", + " HashMap<Integer, String> d = new HashMap<>();", + " }", + " public void exampleLong() {", + " // BUG: Diagnostic contains:", + " HashMap<Long, String> res = new HashMap<>();", + " }", + " public void exampleOther() {", + " HashMap<String, String> res = new HashMap<>();", + " }", + "}") + .doTest(); + } + + @Test + public void testList() { + compilationHelper + .addSourceLines("Example.java", + "import java.util.ArrayList;", + "public class Example {", + " public void exampleInteger() {", + " // BUG: Diagnostic contains:", + " ArrayList<Integer> res = new ArrayList<>();", + " }", + " public void exampleLong() {", + " // BUG: Diagnostic contains:", + " ArrayList<Long> res = new ArrayList<>();", + " }", + " public void exampleOther() {", + " ArrayList<String> res = new ArrayList<>();", + " }", + "}") + .doTest(); + } + + @Test + public void testErasure() { + compilationHelper + .addSourceLines("Example.java", + "import java.util.HashMap;", + "public class Example {", + " public void example() {", + " HashMap a = new HashMap();", + " }", + "}") + .doTest(); + } +} diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java index 32efbf206a45..0943bd65c06f 100644 --- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java @@ -105,4 +105,27 @@ public class RethrowFromSystemCheckerTest { "}") .doTest(); } + + @Test + public void testTelephony() { + compilationHelper + .addSourceFile("/android/annotation/SystemService.java") + .addSourceFile("/com/android/internal/telephony/ITelephony.java") + .addSourceFile("/android/os/IInterface.java") + .addSourceFile("/android/os/RemoteException.java") + .addSourceLines("TelephonyManager.java", + "import android.annotation.SystemService;", + "import com.android.internal.telephony.ITelephony;", + "import android.os.RemoteException;", + "@SystemService(\"telephony\") public class TelephonyManager {", + " ITelephony mService;", + " void bar() {", + " try {", + " mService.bar();", + " } catch (RemoteException ignored) {", + " }", + " }", + "}") + .doTest(); + } } diff --git a/errorprone/tests/res/android/os/Binder.java b/errorprone/tests/res/android/os/Binder.java index d388587c2f58..c969108bd425 100644 --- a/errorprone/tests/res/android/os/Binder.java +++ b/errorprone/tests/res/android/os/Binder.java @@ -20,4 +20,12 @@ public class Binder { public static int getCallingUid() { throw new UnsupportedOperationException(); } + + public static long clearCallingIdentity() { + throw new UnsupportedOperationException(); + } + + public static void restoreCallingIdentity(long token) { + throw new UnsupportedOperationException(); + } } diff --git a/errorprone/tests/res/android/os/Build.java b/errorprone/tests/res/android/os/Build.java index 0b51bdba0731..2d354e2ac370 100644 --- a/errorprone/tests/res/android/os/Build.java +++ b/errorprone/tests/res/android/os/Build.java @@ -18,7 +18,9 @@ package android.os; public class Build { public static class VERSION_CODES { + public static final int CUR_DEVELOPMENT = 10000; public static final int DONUT = 4; public static final int R = 30; + public static final int S = CUR_DEVELOPMENT; } } diff --git a/errorprone/tests/res/com/android/internal/telephony/ITelephony.java b/errorprone/tests/res/com/android/internal/telephony/ITelephony.java new file mode 100644 index 000000000000..61c4dd561b0b --- /dev/null +++ b/errorprone/tests/res/com/android/internal/telephony/ITelephony.java @@ -0,0 +1,23 @@ +/* + * 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.internal.telephony; + +import android.os.RemoteException; + +public interface ITelephony extends android.os.IInterface { + public void bar() throws RemoteException; +} 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 8f496d01c83b..f6edc073ac4f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -66,10 +66,7 @@ public class ShellTaskOrganizer extends TaskOrganizer { public ShellTaskOrganizer(SyncTransactionQueue syncQueue, TransactionPool transactionPool, ShellExecutor mainExecutor, ShellExecutor animExecutor) { - super(); - addListener(new FullscreenTaskListener(syncQueue), WINDOWING_MODE_FULLSCREEN); - mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor); - if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions); + this(null, syncQueue, transactionPool, mainExecutor, animExecutor); } @VisibleForTesting diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index 7c2625133cad..488f9092b7da 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -18,7 +18,10 @@ package com.android.wm.shell.pip; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.PictureInPictureParams; import android.content.ComponentName; +import android.content.pm.ActivityInfo; +import android.graphics.Rect; import android.media.session.MediaController; import com.android.wm.shell.pip.phone.PipTouchHandler; @@ -232,4 +235,28 @@ public interface Pip { */ default void suspendPipResizing(int reason) { } + + /** + * Called by Launcher when swiping an auto-pip enabled Activity to home starts + * @param componentName {@link ComponentName} represents the Activity entering PiP + * @param activityInfo {@link ActivityInfo} tied to the Activity + * @param pictureInPictureParams {@link PictureInPictureParams} tied to the Activity + * @param launcherRotation Rotation Launcher is in + * @param shelfHeight Shelf height when landing PiP window onto Launcher + * @return Destination bounds of PiP window based on the parameters passed in + */ + default Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) { + return null; + } + + /** + * Called by Launcher when swiping an auto-pip enable Activity to home finishes + * @param componentName {@link ComponentName} represents the Activity entering PiP + * @param destinationBounds Destination bounds of PiP window + */ + default void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + return; + } } 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 bb501fb050e6..05877d4b172d 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 @@ -58,6 +58,7 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.os.SomeArgs; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; @@ -88,7 +89,7 @@ import java.util.function.Consumer; * This class is also responsible for general resize/offset PiP operations within SysUI component, * see also {@link PipMotionHelper}. */ -public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganizer.TaskListener, +public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, DisplayController.OnDisplaysChangedListener { private static final String TAG = PipTaskOrganizer.class.getSimpleName(); private static final boolean DEBUG = false; @@ -104,7 +105,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize UNDEFINED(0), TASK_APPEARED(1), ENTERING_PIP(2), - EXITING_PIP(3); + ENTERED_PIP(3), + EXITING_PIP(4); private final int mStateValue; @@ -149,15 +151,25 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize new PipAnimationController.PipAnimationCallback() { @Override public void onPipAnimationStart(PipAnimationController.PipTransitionAnimator animator) { - sendOnPipTransitionStarted(animator.getTransitionDirection()); + final int direction = animator.getTransitionDirection(); + if (direction == TRANSITION_DIRECTION_TO_PIP) { + InteractionJankMonitor.getInstance().begin( + InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 2000); + } + sendOnPipTransitionStarted(direction); } @Override public void onPipAnimationEnd(SurfaceControl.Transaction tx, PipAnimationController.PipTransitionAnimator animator) { - finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection(), + final int direction = animator.getTransitionDirection(); + finishResize(tx, animator.getDestinationBounds(), direction, animator.getAnimationType()); - sendOnPipTransitionFinished(animator.getTransitionDirection()); + sendOnPipTransitionFinished(direction); + if (direction == TRANSITION_DIRECTION_TO_PIP) { + InteractionJankMonitor.getInstance().end( + InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP); + } } @Override @@ -241,6 +253,14 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize */ private boolean mShouldDeferEnteringPip; + /** + * If set to {@code true}, no entering PiP transition would be kicked off and most likely + * it's due to the fact that Launcher is handling the transition directly when swiping + * auto PiP-able Activity to home. + * See also {@link #startSwipePipToHome(ComponentName, ActivityInfo, PictureInPictureParams)}. + */ + private boolean mShouldIgnoreEnteringPipTransition; + public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, Optional<SplitScreen> splitScreenOptional, @@ -309,6 +329,27 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize } /** + * Callback when Launcher starts swipe-pip-to-home operation. + * @return {@link Rect} for destination bounds. + */ + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams) { + mShouldIgnoreEnteringPipTransition = true; + mState = State.ENTERING_PIP; + return mPipBoundsHandler.getDestinationBounds(componentName, + getAspectRatioOrDefault(pictureInPictureParams), + null /* bounds */, getMinimalSize(activityInfo)); + } + + /** + * Callback when launcher finishes swipe-pip-to-home operation. + * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards. + */ + public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + mLastReportedBounds.set(destinationBounds); + } + + /** * Expands PiP to the previous bounds, this is done in two phases using * {@link WindowContainerTransaction} * - setActivityWindowingMode to either fullscreen or split-secondary at beginning of the @@ -415,7 +456,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize wct.setBounds(mToken, null); mTaskOrganizer.applyTransaction(wct); - ActivityTaskManager.getService().removeStacksInWindowingModes( + ActivityTaskManager.getService().removeRootTasksInWindowingModes( new int[]{ WINDOWING_MODE_PINNED }); } catch (RemoteException e) { Log.e(TAG, "Failed to remove PiP", e); @@ -435,6 +476,16 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo); mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER); + if (mShouldIgnoreEnteringPipTransition) { + // Animation has been finished together with Recents, directly apply the sync + // transaction to PiP here. + applyEnterPipSyncTransaction(mLastReportedBounds, () -> { + mState = State.ENTERED_PIP; + }); + mShouldIgnoreEnteringPipTransition = false; + return; + } + if (mShouldDeferEnteringPip) { if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing"); // if deferred, hide the surface till fixed rotation is completed @@ -489,6 +540,20 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.apply(); + applyEnterPipSyncTransaction(destinationBounds, () -> { + mUpdateHandler.post(() -> mPipAnimationController + .getAnimator(mLeash, destinationBounds, 0f, 1f) + .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(durationMs) + .start()); + // mState is set right after the animation is kicked off to block any resize + // requests such as offsetPip that may have been called prior to the transition. + mState = State.ENTERING_PIP; + }); + } + + private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable) { final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setBounds(mToken, destinationBounds); @@ -497,15 +562,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize @Override public void onTransactionReady(int id, SurfaceControl.Transaction t) { t.apply(); - mUpdateHandler.post(() -> mPipAnimationController - .getAnimator(mLeash, destinationBounds, 0f, 1f) - .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) - .setPipAnimationCallback(mPipAnimationCallback) - .setDuration(durationMs) - .start()); - // mState is set right after the animation is kicked off to block any resize - // requests such as offsetPip that may have been called prior to the transition. - mState = State.ENTERING_PIP; + if (runnable != null) { + runnable.run(); + } } }); } @@ -523,6 +582,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize private void sendOnPipTransitionFinished( @PipAnimationController.TransitionDirection int direction) { + if (direction == TRANSITION_DIRECTION_TO_PIP) { + mState = State.ENTERED_PIP; + } runOnMainHandler(() -> { for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); @@ -606,7 +668,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganize /** * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}. * Meanwhile this callback is invoked whenever the task is removed. For instance: - * - as a result of removeStacksInWindowingModes from WM + * - as a result of removeRootTasksInWindowingModes from WM * - activity itself is died * Nevertheless, we simply update the internal state here as all the heavy lifting should * have been done in WM. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 5193656e8299..41c0a881c039 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -22,9 +22,11 @@ import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.PictureInPictureParams; import android.app.RemoteAction; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.pm.ParceledListSlice; import android.graphics.Rect; import android.os.Handler; @@ -87,7 +89,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } // If there is an animation running (ie. from a shelf offset), then ensure that we calculate // the bounds for the next orientation using the destination bounds of the animation - // TODO: Techincally this should account for movement animation bounds as well + // TODO: Technically this should account for movement animation bounds as well Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds(); final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mContext, mTmpNormalBounds, currentBounds, mTmpInsetBounds, displayId, fromRotation, @@ -351,16 +353,18 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac */ @Override public void setShelfHeight(boolean visible, int height) { - mHandler.post(() -> { - final int shelfHeight = visible ? height : 0; - final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight); - if (changed) { - mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight); - updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(), - false /* fromRotation */, false /* fromImeAdjustment */, - true /* fromShelfAdjustment */, null /* windowContainerTransaction */); - } - }); + mHandler.post(() -> setShelfHeightLocked(visible, height)); + } + + private void setShelfHeightLocked(boolean visible, int height) { + final int shelfHeight = visible ? height : 0; + final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight); + if (changed) { + mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight); + updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(), + false /* fromRotation */, false /* fromImeAdjustment */, + true /* fromShelfAdjustment */, null /* windowContainerTransaction */); + } } @Override @@ -374,6 +378,21 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } @Override + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) { + setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight); + mPipBoundsHandler.onDisplayRotationChangedNotInPip(mContext, launcherRotation); + return mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo, + pictureInPictureParams); + } + + @Override + public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds); + } + + @Override public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { if (isOutPipDirection(direction)) { // Exiting PIP, save the reentry bounds to restore to when re-entering. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java new file mode 100644 index 000000000000..bebe5f965251 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java @@ -0,0 +1,295 @@ +/* + * 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.wm.shell.pip.phone; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.TransitionDrawable; +import android.os.Handler; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.SpringForce; + +import com.android.wm.shell.R; +import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.common.DismissCircleView; +import com.android.wm.shell.common.magnetictarget.MagnetizedObject; +import com.android.wm.shell.pip.PipUiEventLogger; + +import kotlin.Unit; + +/** + * Handler of all Magnetized Object related code for PiP. + */ +public class PipDismissTargetHandler { + + /* The multiplier to apply scale the target size by when applying the magnetic field radius */ + private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f; + + /** Duration of the dismiss scrim fading in/out. */ + private static final int DISMISS_TRANSITION_DURATION_MS = 200; + + /** + * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move + * PIP. + */ + private final MagnetizedObject<Rect> mMagnetizedPip; + + /** + * Container for the dismiss circle, so that it can be animated within the container via + * translation rather than within the WindowManager via slow layout animations. + */ + private final ViewGroup mTargetViewContainer; + + /** Circle view used to render the dismiss target. */ + private final DismissCircleView mTargetView; + + /** + * MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius. + */ + private final MagnetizedObject.MagneticTarget mMagneticTarget; + + /** PhysicsAnimator instance for animating the dismiss target in/out. */ + private final PhysicsAnimator<View> mMagneticTargetAnimator; + + /** Default configuration to use for springing the dismiss target in/out. */ + private final PhysicsAnimator.SpringConfig mTargetSpringConfig = + new PhysicsAnimator.SpringConfig( + SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); + + /** + * Runnable that can be posted delayed to show the target. This needs to be saved as a member + * variable so we can pass it to removeCallbacks. + */ + private Runnable mShowTargetAction = this::showDismissTargetMaybe; + + // Allow dragging the PIP to a location to close it + private final boolean mEnableDismissDragToEdge; + + private int mDismissAreaHeight; + + private final Context mContext; + private final PipMotionHelper mMotionHelper; + private final PipUiEventLogger mPipUiEventLogger; + private final WindowManager mWindowManager; + private final Handler mHandler; + + public PipDismissTargetHandler(Context context, PipUiEventLogger pipUiEventLogger, + PipMotionHelper motionHelper, Handler handler) { + mContext = context; + mPipUiEventLogger = pipUiEventLogger; + mMotionHelper = motionHelper; + mHandler = handler; + mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + + Resources res = context.getResources(); + mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); + mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height); + + mTargetView = new DismissCircleView(context); + mTargetViewContainer = new FrameLayout(context); + mTargetViewContainer.setBackgroundDrawable( + context.getDrawable(R.drawable.floating_dismiss_gradient_transition)); + mTargetViewContainer.setClipChildren(false); + mTargetViewContainer.addView(mTargetView); + + mMagnetizedPip = mMotionHelper.getMagnetizedPip(); + mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0); + updateMagneticTargetSize(); + + mMagnetizedPip.setAnimateStuckToTarget( + (target, velX, velY, flung, after) -> { + if (mEnableDismissDragToEdge) { + mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after); + } + return Unit.INSTANCE; + }); + mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() { + @Override + public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) { + // Show the dismiss target, in case the initial touch event occurred within the + // magnetic field radius. + if (mEnableDismissDragToEdge) { + showDismissTargetMaybe(); + } + } + + @Override + public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target, + float velX, float velY, boolean wasFlungOut) { + if (wasFlungOut) { + mMotionHelper.flingToSnapTarget(velX, velY, null /* endAction */); + hideDismissTargetMaybe(); + } else { + mMotionHelper.setSpringingToTouch(true); + } + } + + @Override + public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { + mMotionHelper.notifyDismissalPending(); + + handler.post(() -> { + mMotionHelper.animateDismiss(); + hideDismissTargetMaybe(); + }); + + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE); + } + }); + + mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView); + } + + /** + * Potentially start consuming future motion events if PiP is currently near the magnetized + * object. + */ + public boolean maybeConsumeMotionEvent(MotionEvent ev) { + return mMagnetizedPip.maybeConsumeMotionEvent(ev); + } + + /** + * Update the magnet size. + */ + public void updateMagneticTargetSize() { + if (mTargetView == null) { + return; + } + + final Resources res = mContext.getResources(); + final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size); + mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height); + final FrameLayout.LayoutParams newParams = + new FrameLayout.LayoutParams(targetSize, targetSize); + newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; + newParams.bottomMargin = mContext.getResources().getDimensionPixelSize( + R.dimen.floating_dismiss_bottom_margin); + mTargetView.setLayoutParams(newParams); + + // Set the magnetic field radius equal to the target size from the center of the target + mMagneticTarget.setMagneticFieldRadiusPx( + (int) (targetSize * MAGNETIC_FIELD_RADIUS_MULTIPLIER)); + } + + /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */ + public void createOrUpdateDismissTarget() { + if (!mTargetViewContainer.isAttachedToWindow()) { + mHandler.removeCallbacks(mShowTargetAction); + mMagneticTargetAnimator.cancel(); + + mTargetViewContainer.setVisibility(View.INVISIBLE); + + try { + mWindowManager.addView(mTargetViewContainer, getDismissTargetLayoutParams()); + } catch (IllegalStateException e) { + // This shouldn't happen, but if the target is already added, just update its layout + // params. + mWindowManager.updateViewLayout( + mTargetViewContainer, getDismissTargetLayoutParams()); + } + } else { + mWindowManager.updateViewLayout(mTargetViewContainer, getDismissTargetLayoutParams()); + } + } + + /** Returns layout params for the dismiss target, using the latest display metrics. */ + private WindowManager.LayoutParams getDismissTargetLayoutParams() { + final Point windowSize = new Point(); + mWindowManager.getDefaultDisplay().getRealSize(windowSize); + + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + WindowManager.LayoutParams.MATCH_PARENT, + mDismissAreaHeight, + 0, windowSize.y - mDismissAreaHeight, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT); + + lp.setTitle("pip-dismiss-overlay"); + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; + lp.setFitInsetsTypes(0 /* types */); + + return lp; + } + + /** Makes the dismiss target visible and animates it in, if it isn't already visible. */ + public void showDismissTargetMaybe() { + if (!mEnableDismissDragToEdge) { + return; + } + + createOrUpdateDismissTarget(); + + if (mTargetViewContainer.getVisibility() != View.VISIBLE) { + + mTargetView.setTranslationY(mTargetViewContainer.getHeight()); + mTargetViewContainer.setVisibility(View.VISIBLE); + + // Cancel in case we were in the middle of animating it out. + mMagneticTargetAnimator.cancel(); + mMagneticTargetAnimator + .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig) + .start(); + + ((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition( + DISMISS_TRANSITION_DURATION_MS); + } + } + + /** Animates the magnetic dismiss target out and then sets it to GONE. */ + public void hideDismissTargetMaybe() { + if (!mEnableDismissDragToEdge) { + return; + } + + mHandler.removeCallbacks(mShowTargetAction); + mMagneticTargetAnimator + .spring(DynamicAnimation.TRANSLATION_Y, + mTargetViewContainer.getHeight(), + mTargetSpringConfig) + .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE)) + .start(); + + ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition( + DISMISS_TRANSITION_DURATION_MS); + } + + /** + * Removes the dismiss target and cancels any pending callbacks to show it. + */ + public void cleanUpDismissTarget() { + mHandler.removeCallbacks(mShowTargetAction); + + if (mTargetViewContainer.isAttachedToWindow()) { + mWindowManager.removeViewImmediate(mTargetViewContainer); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java index c53803a7f8cc..cd47d55da7f0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java @@ -156,20 +156,6 @@ public class PipMenuActivityController { } /** - * Updates the appearance of the menu and scrim on top of the PiP while dismissing. - */ - public void setDismissFraction(float fraction) { - final boolean isMenuVisible = isMenuVisible(); - if (DEBUG) { - Log.d(TAG, "setDismissFraction() isMenuVisible=" + isMenuVisible - + " fraction=" + fraction); - } - if (isMenuVisible) { - mPipMenuView.updateDismissFraction(fraction); - } - } - - /** * Similar to {@link #showMenu(int, Rect, boolean, boolean, boolean)} but only show the menu * upon PiP window transition is finished. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 24e49f8d9821..51951409f76c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -429,25 +429,6 @@ public class PipMenuView extends FrameLayout { } } - void updateDismissFraction(float fraction) { - int alpha; - final float menuAlpha = 1 - fraction; - if (mMenuState == MENU_STATE_FULL) { - mMenuContainer.setAlpha(menuAlpha); - mSettingsButton.setAlpha(menuAlpha); - mDismissButton.setAlpha(menuAlpha); - final float interpolatedAlpha = - MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction; - alpha = (int) (interpolatedAlpha * 255); - } else { - if (mMenuState == MENU_STATE_CLOSE) { - mDismissButton.setAlpha(menuAlpha); - } - alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255); - } - mBackgroundDrawable.setAlpha(alpha); - } - private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) { mMenuState = menuState; mController.onMenuStateChanged(menuState, resize, callback); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index cc86cf97104b..fe1d44c72845 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -385,23 +385,20 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, * Flings the PiP to the closest snap target. */ void flingToSnapTarget( - float velocityX, float velocityY, - @Nullable Runnable updateAction, @Nullable Runnable endAction) { - movetoTarget(velocityX, velocityY, updateAction, endAction, false /* isStash */); + float velocityX, float velocityY, @Nullable Runnable endAction) { + movetoTarget(velocityX, velocityY, endAction, false /* isStash */); } /** * Stash PiP to the closest edge. */ void stashToEdge( - float velocityX, float velocityY, - @Nullable Runnable updateAction, @Nullable Runnable endAction) { - movetoTarget(velocityX, velocityY, updateAction, endAction, true /* isStash */); + float velocityX, float velocityY, @Nullable Runnable endAction) { + movetoTarget(velocityX, velocityY, endAction, true /* isStash */); } private void movetoTarget( - float velocityX, float velocityY, - @Nullable Runnable updateAction, @Nullable Runnable endAction, boolean isStash) { + float velocityX, float velocityY, @Nullable Runnable endAction, boolean isStash) { // If we're flinging to a snap target now, we're not springing to catch up to the touch // location now. mSpringingToTouch = false; @@ -416,11 +413,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig) .withEndActions(endAction); - if (updateAction != null) { - mTemporaryBoundsPhysicsAnimator.addUpdateListener( - (target, values) -> updateAction.run()); - } - final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO); final float leftEdge = isStash ? mMovementBounds.left - offset : mMovementBounds.left; final float rightEdge = isStash ? mMovementBounds.right + offset : mMovementBounds.right; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 6b3177225a35..07beb43d4faa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -26,40 +26,26 @@ import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; -import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; -import android.graphics.drawable.TransitionDrawable; import android.os.Handler; import android.os.RemoteException; import android.provider.DeviceConfig; import android.util.Log; import android.util.Size; -import android.view.Gravity; import android.view.IPinnedStackController; import android.view.InputEvent; import android.view.MotionEvent; -import android.view.View; import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; -import android.widget.FrameLayout; - -import androidx.annotation.NonNull; -import androidx.dynamicanimation.animation.DynamicAnimation; -import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; -import com.android.wm.shell.animation.PhysicsAnimator; -import com.android.wm.shell.common.DismissCircleView; import com.android.wm.shell.common.FloatingContentCoordinator; -import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipTaskOrganizer; @@ -67,8 +53,6 @@ import com.android.wm.shell.pip.PipUiEventLogger; import java.io.PrintWriter; -import kotlin.Unit; - /** * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding * the PIP. @@ -82,14 +66,12 @@ public class PipTouchHandler { /* The multiplier to apply scale the target size by when applying the magnetic field radius */ private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f; - // Allow dragging the PIP to a location to close it - private final boolean mEnableDismissDragToEdge; // Allow PIP to resize to a slightly bigger state upon touch private final boolean mEnableResize; private final Context mContext; - private final WindowManager mWindowManager; private final PipBoundsHandler mPipBoundsHandler; private final PipUiEventLogger mPipUiEventLogger; + private final PipDismissTargetHandler mPipDismissTargetHandler; private PipResizeGestureHandler mPipResizeGestureHandler; private IPinnedStackController mPinnedStackController; @@ -105,34 +87,6 @@ public class PipTouchHandler { */ private boolean mEnableStash = false; - /** - * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move - * PIP. - */ - private MagnetizedObject<Rect> mMagnetizedPip; - - /** - * Container for the dismiss circle, so that it can be animated within the container via - * translation rather than within the WindowManager via slow layout animations. - */ - private ViewGroup mTargetViewContainer; - - /** Circle view used to render the dismiss target. */ - private DismissCircleView mTargetView; - - /** - * MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius. - */ - private MagnetizedObject.MagneticTarget mMagneticTarget; - - /** PhysicsAnimator instance for animating the dismiss target in/out. */ - private PhysicsAnimator<View> mMagneticTargetAnimator; - - /** Default configuration to use for springing the dismiss target in/out. */ - private final PhysicsAnimator.SpringConfig mTargetSpringConfig = - new PhysicsAnimator.SpringConfig( - SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); - // The current movement bounds private Rect mMovementBounds = new Rect(); @@ -150,12 +104,6 @@ public class PipTouchHandler { private int mDeferResizeToNormalBoundsUntilRotation = -1; private int mDisplayRotation; - /** - * Runnable that can be posted delayed to show the target. This needs to be saved as a member - * variable so we can pass it to removeCallbacks. - */ - private Runnable mShowTargetAction = this::showDismissTargetMaybe; - private Handler mHandler = new Handler(); // Behaviour states @@ -163,7 +111,6 @@ public class PipTouchHandler { private boolean mIsImeShowing; private int mImeHeight; private int mImeOffset; - private int mDismissAreaHeight; private boolean mIsShelfShowing; private int mShelfHeight; private int mMovementBoundsExtraOffsets; @@ -221,7 +168,6 @@ public class PipTouchHandler { mContext = context; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mPipBoundsHandler = pipBoundsHandler; - mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mMenuController = menuController; mMenuController.addListener(new PipMenuListener()); mGesture = new DefaultPipTouchGesture(); @@ -231,13 +177,14 @@ public class PipTouchHandler { new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, pipTaskOrganizer, this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger, menuController); + mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger, + mMotionHelper, mHandler); mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(), true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()), menuController::hideMenu); Resources res = context.getResources(); - mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu); reloadResources(); @@ -248,61 +195,6 @@ public class PipTouchHandler { mPipUiEventLogger = pipUiEventLogger; - mTargetView = new DismissCircleView(context); - mTargetViewContainer = new FrameLayout(context); - mTargetViewContainer.setBackgroundDrawable( - context.getDrawable(R.drawable.floating_dismiss_gradient_transition)); - mTargetViewContainer.setClipChildren(false); - mTargetViewContainer.addView(mTargetView); - - mMagnetizedPip = mMotionHelper.getMagnetizedPip(); - mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0); - updateMagneticTargetSize(); - - mMagnetizedPip.setAnimateStuckToTarget( - (target, velX, velY, flung, after) -> { - if (mEnableDismissDragToEdge) { - mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after); - } - return Unit.INSTANCE; - }); - mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() { - @Override - public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) { - // Show the dismiss target, in case the initial touch event occurred within the - // magnetic field radius. - if (mEnableDismissDragToEdge) { - showDismissTargetMaybe(); - } - } - - @Override - public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target, - float velX, float velY, boolean wasFlungOut) { - if (wasFlungOut) { - mMotionHelper.flingToSnapTarget(velX, velY, null, null); - hideDismissTarget(); - } else { - mMotionHelper.setSpringingToTouch(true); - } - } - - @Override - public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { - mMotionHelper.notifyDismissalPending(); - - mHandler.post(() -> { - mMotionHelper.animateDismiss(); - hideDismissTarget(); - }); - - mPipUiEventLogger.log( - PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE); - } - }); - - mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView); - mEnableStash = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, PIP_STASHING, @@ -323,27 +215,7 @@ public class PipTouchHandler { mExpandedShortestEdgeSize = res.getDimensionPixelSize( R.dimen.pip_expanded_shortest_edge_size); mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); - mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height); - updateMagneticTargetSize(); - } - - private void updateMagneticTargetSize() { - if (mTargetView == null) { - return; - } - - final Resources res = mContext.getResources(); - final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size); - final FrameLayout.LayoutParams newParams = - new FrameLayout.LayoutParams(targetSize, targetSize); - newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; - newParams.bottomMargin = mContext.getResources().getDimensionPixelSize( - R.dimen.floating_dismiss_bottom_margin); - mTargetView.setLayoutParams(newParams); - - // Set the magnetic field radius equal to the target size from the center of the target - mMagneticTarget.setMagneticFieldRadiusPx( - (int) (targetSize * MAGNETIC_FIELD_RADIUS_MULTIPLIER)); + mPipDismissTargetHandler.updateMagneticTargetSize(); } private boolean shouldShowResizeHandle() { @@ -368,7 +240,7 @@ public class PipTouchHandler { } public void onActivityPinned() { - createOrUpdateDismissTarget(); + mPipDismissTargetHandler.createOrUpdateDismissTarget(); mShowPipMenuOnAnimationEnd = true; mPipResizeGestureHandler.onActivityPinned(); @@ -378,7 +250,7 @@ public class PipTouchHandler { public void onActivityUnpinned(ComponentName topPipActivity) { if (topPipActivity == null) { // Clean up state after the last PiP activity is removed - cleanUpDismissTarget(); + mPipDismissTargetHandler.cleanUpDismissTarget(); mFloatingContentCoordinator.onContentRemoved(mMotionHelper); } @@ -409,7 +281,7 @@ public class PipTouchHandler { reloadResources(); // Recreate the dismiss target for the new orientation. - createOrUpdateDismissTarget(); + mPipDismissTargetHandler.createOrUpdateDismissTarget(); } public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { @@ -553,94 +425,6 @@ public class PipTouchHandler { } } - /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */ - private void createOrUpdateDismissTarget() { - if (!mTargetViewContainer.isAttachedToWindow()) { - mHandler.removeCallbacks(mShowTargetAction); - mMagneticTargetAnimator.cancel(); - - mTargetViewContainer.setVisibility(View.INVISIBLE); - - try { - mWindowManager.addView(mTargetViewContainer, getDismissTargetLayoutParams()); - } catch (IllegalStateException e) { - // This shouldn't happen, but if the target is already added, just update its layout - // params. - mWindowManager.updateViewLayout( - mTargetViewContainer, getDismissTargetLayoutParams()); - } - } else { - mWindowManager.updateViewLayout(mTargetViewContainer, getDismissTargetLayoutParams()); - } - } - - /** Returns layout params for the dismiss target, using the latest display metrics. */ - private WindowManager.LayoutParams getDismissTargetLayoutParams() { - final Point windowSize = new Point(); - mWindowManager.getDefaultDisplay().getRealSize(windowSize); - - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - WindowManager.LayoutParams.MATCH_PARENT, - mDismissAreaHeight, - 0, windowSize.y - mDismissAreaHeight, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, - PixelFormat.TRANSLUCENT); - - lp.setTitle("pip-dismiss-overlay"); - lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; - lp.setFitInsetsTypes(0 /* types */); - - return lp; - } - - /** Makes the dismiss target visible and animates it in, if it isn't already visible. */ - private void showDismissTargetMaybe() { - createOrUpdateDismissTarget(); - - if (mTargetViewContainer.getVisibility() != View.VISIBLE) { - - mTargetView.setTranslationY(mTargetViewContainer.getHeight()); - mTargetViewContainer.setVisibility(View.VISIBLE); - - // Cancel in case we were in the middle of animating it out. - mMagneticTargetAnimator.cancel(); - mMagneticTargetAnimator - .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig) - .start(); - - ((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition( - DISMISS_TRANSITION_DURATION_MS); - } - } - - /** Animates the magnetic dismiss target out and then sets it to GONE. */ - private void hideDismissTarget() { - mHandler.removeCallbacks(mShowTargetAction); - mMagneticTargetAnimator - .spring(DynamicAnimation.TRANSLATION_Y, - mTargetViewContainer.getHeight(), - mTargetSpringConfig) - .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE)) - .start(); - - ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition( - DISMISS_TRANSITION_DURATION_MS); - } - - /** - * Removes the dismiss target and cancels any pending callbacks to show it. - */ - private void cleanUpDismissTarget() { - mHandler.removeCallbacks(mShowTargetAction); - - if (mTargetViewContainer.isAttachedToWindow()) { - mWindowManager.removeViewImmediate(mTargetViewContainer); - } - } - /** * TODO Add appropriate description */ @@ -650,7 +434,7 @@ public class PipTouchHandler { if (!isRegistered && mTouchState.isUserInteracting()) { // If the input consumer is unregistered while the user is interacting, then we may not // get the final TOUCH_UP event, so clean up the dismiss target as well - cleanUpDismissTarget(); + mPipDismissTargetHandler.cleanUpDismissTarget(); } } @@ -683,7 +467,7 @@ public class PipTouchHandler { } if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting()) - && mMagnetizedPip.maybeConsumeMotionEvent(ev)) { + && mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) { // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event // to the touch state. Touch state needs a DOWN event in order to later process MOVE // events it'll receive if the object is dragged out of the magnetic field. @@ -793,25 +577,6 @@ public class PipTouchHandler { } /** - * Updates the appearance of the menu and scrim on top of the PiP while dismissing. - */ - private void updateDismissFraction() { - if (mMenuController != null) { - Rect bounds = mMotionHelper.getBounds(); - final float target = mInsetBounds.bottom; - float fraction = 0f; - if (bounds.bottom > target) { - final float distance = bounds.bottom - target; - fraction = Math.min(distance / bounds.height(), 1f); - } - if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) { - // Update if the fraction > 0, or if fraction == 0 and the menu was already visible - mMenuController.setDismissFraction(fraction); - } - } - } - - /** * Sets the controller to update the system of changes from user interaction. */ void setPinnedStackController(IPinnedStackController controller) { @@ -958,13 +723,7 @@ public class PipTouchHandler { if (touchState.startedDragging()) { mSavedSnapFraction = -1f; - - if (mEnableDismissDragToEdge) { - if (mTargetViewContainer.getVisibility() != View.VISIBLE) { - mHandler.removeCallbacks(mShowTargetAction); - showDismissTargetMaybe(); - } - } + mPipDismissTargetHandler.showDismissTargetMaybe(); } if (touchState.isDragging()) { @@ -995,9 +754,7 @@ public class PipTouchHandler { @Override public boolean onUp(PipTouchState touchState) { - if (mEnableDismissDragToEdge) { - hideDismissTarget(); - } + mPipDismissTargetHandler.hideDismissTargetMaybe(); if (!touchState.isUserInteracting()) { return false; @@ -1023,12 +780,9 @@ public class PipTouchHandler { if (mEnableStash && (animatingBounds.right > mPipBoundsHandler.getDisplayBounds().right || animatingBounds.left < mPipBoundsHandler.getDisplayBounds().left)) { - mMotionHelper.stashToEdge(vel.x, vel.y, - PipTouchHandler.this::updateDismissFraction /* updateAction */, - this::flingEndAction /* endAction */); + mMotionHelper.stashToEdge(vel.x, vel.y, this::flingEndAction /* endAction */); } else { mMotionHelper.flingToSnapTarget(vel.x, vel.y, - PipTouchHandler.this::updateDismissFraction /* updateAction */, this::flingEndAction /* endAction */); } } else if (mTouchState.isDoubleTap()) { @@ -1122,7 +876,6 @@ public class PipTouchHandler { pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing); pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight); pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction); - pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge); pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets); mPipBoundsHandler.dump(pw, innerPrefix); mTouchState.dump(pw, innerPrefix); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java index 3eec20f53d24..8eac005425bc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java @@ -315,9 +315,9 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener); if (removePipStack) { try { - mActivityTaskManager.removeStack(mPinnedStackId); + mActivityTaskManager.removeTask(mPinnedStackId); } catch (RemoteException e) { - Log.e(TAG, "removeStack failed", e); + Log.e(TAG, "removeTask failed", e); } finally { mPinnedStackId = INVALID_STACK_ID; } diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index ff004094ec59..f87988c1594c 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -89,6 +89,16 @@ import java.util.function.Consumer; public class LocationManager { /** + * For apps targeting Android S and above, LocationRequest system APIs may not be used with + * PendingIntent location requests. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + public static final long PREVENT_PENDING_INTENT_SYSTEM_API_USAGE = 169887240L; + + /** * For apps targeting Android S and above, location clients may receive historical locations * (from before the present time) under some circumstances. * diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index 3bb4781f99ef..e03643c4c632 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -250,7 +250,7 @@ public final class LocationRequest implements Parcelable { boolean hiddenFromAppOps, boolean locationSettingsIgnored, boolean lowPower, - @Nullable WorkSource workSource) { + WorkSource workSource) { Preconditions.checkArgument(intervalMillis != PASSIVE_INTERVAL || quality == POWER_NONE); Preconditions.checkArgument(minUpdateIntervalMillis <= intervalMillis); @@ -265,7 +265,7 @@ public final class LocationRequest implements Parcelable { mHideFromAppOps = hiddenFromAppOps; mLowPower = lowPower; mLocationSettingsIgnored = locationSettingsIgnored; - mWorkSource = workSource; + mWorkSource = Objects.requireNonNull(workSource); } /** @@ -645,12 +645,15 @@ public final class LocationRequest implements Parcelable { @SystemApi @Deprecated public void setWorkSource(@Nullable WorkSource workSource) { + if (workSource == null) { + workSource = new WorkSource(); + } mWorkSource = workSource; } /** - * Returns the work source used for power blame for this request. If null, the system is free to - * assign power blame as it deems most appropriate. + * Returns the work source used for power blame for this request. If empty, the system is free + * to assign power blame as it deems most appropriate. * * @return the work source used for power blame for this request * @@ -658,7 +661,7 @@ public final class LocationRequest implements Parcelable { */ @TestApi @SystemApi - public @Nullable WorkSource getWorkSource() { + public @NonNull WorkSource getWorkSource() { return mWorkSource; } @@ -1062,9 +1065,9 @@ public final class LocationRequest implements Parcelable { } /** - * Sets the work source to use for power blame for this location request. Defaults to null, - * which implies the system is free to assign power blame as it determines best for this - * request (which usually means blaming the owner of the location listener). + * Sets the work source to use for power blame for this location request. Defaults to an + * empty WorkSource, which implies the system is free to assign power blame as it determines + * best for this request (which usually means blaming the owner of the location listener). * * <p>Permissions enforcement occurs when resulting location request is actually used, not * when this method is invoked. @@ -1108,7 +1111,7 @@ public final class LocationRequest implements Parcelable { mHiddenFromAppOps, mLocationSettingsIgnored, mLowPower, - mWorkSource); + new WorkSource(mWorkSource)); } } } diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java index c32970f1bb04..e023aa1dcd22 100644 --- a/location/java/android/location/util/identity/CallerIdentity.java +++ b/location/java/android/location/util/identity/CallerIdentity.java @@ -150,6 +150,11 @@ public final class CallerIdentity { return mListenerId; } + /** Returns true if this represents a system identity. */ + public boolean isSystem() { + return mUid == Process.SYSTEM_UID; + } + /** * Adds this identity to the worksource supplied, or if not worksource is supplied, creates a * new worksource representing this identity. diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt index c00ff57889d1..f43eb639ee23 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -59,3 +59,35 @@ package com.android.location.provider { } +package com.android.location.timezone.provider { + + public final class LocationTimeZoneEventUnbundled { + method public int getEventType(); + method @NonNull public java.util.List<java.lang.String> getTimeZoneIds(); + field public static final int EVENT_TYPE_PERMANENT_FAILURE = 1; // 0x1 + field public static final int EVENT_TYPE_SUCCESS = 2; // 0x2 + field public static final int EVENT_TYPE_UNCERTAIN = 3; // 0x3 + } + + public static final class LocationTimeZoneEventUnbundled.Builder { + ctor public LocationTimeZoneEventUnbundled.Builder(); + method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled build(); + method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled.Builder setEventType(int); + method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled.Builder setTimeZoneIds(@NonNull java.util.List<java.lang.String>); + } + + public abstract class LocationTimeZoneProviderBase { + ctor public LocationTimeZoneProviderBase(android.content.Context, String); + method public final android.os.IBinder getBinder(); + method protected final android.content.Context getContext(); + method protected abstract void onSetRequest(@NonNull com.android.location.timezone.provider.LocationTimeZoneProviderRequestUnbundled); + method protected final void reportLocationTimeZoneEvent(@NonNull com.android.location.timezone.provider.LocationTimeZoneEventUnbundled); + } + + public final class LocationTimeZoneProviderRequestUnbundled { + method @IntRange(from=0) public long getInitializationTimeoutMillis(); + method public boolean getReportLocationTimeZone(); + } + +} + diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java index 367557442829..aa0e8951f77c 100644 --- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java +++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java @@ -18,7 +18,6 @@ package com.android.location.timezone.provider; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.ActivityManager; import android.location.timezone.LocationTimeZoneEvent; import android.os.SystemClock; @@ -31,8 +30,6 @@ import java.util.Objects; /** * An event from a {@link LocationTimeZoneProviderBase} sent while determining a device's time zone * using its location. - * - * @hide */ public final class LocationTimeZoneEventUnbundled { @@ -69,7 +66,6 @@ public final class LocationTimeZoneEventUnbundled { /** * Returns the event type. */ - @Nullable public @EventType int getEventType() { return mDelegate.getEventType(); } @@ -118,8 +114,6 @@ public final class LocationTimeZoneEventUnbundled { /** * A builder of {@link LocationTimeZoneEventUnbundled} instances. - * - * @hide */ public static final class Builder { diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java index 9df71664ee1b..68ae722d703d 100644 --- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java +++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java @@ -16,6 +16,7 @@ package com.android.location.timezone.provider; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.IBinder; @@ -55,8 +56,6 @@ import java.util.Objects; * * <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain * API stable. - * - * @hide */ public abstract class LocationTimeZoneProviderBase { @@ -85,7 +84,8 @@ public abstract class LocationTimeZoneProviderBase { /** * Reports a new location time zone event from this provider. */ - protected void reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled event) { + protected final void reportLocationTimeZoneEvent( + @NonNull LocationTimeZoneEventUnbundled event) { ILocationTimeZoneProviderManager manager = mManager; if (manager != null) { try { @@ -102,7 +102,7 @@ public abstract class LocationTimeZoneProviderBase { * to start returning location time zones, or to stop returning location time zones, depending * on the parameters in the request. */ - protected abstract void onSetRequest(LocationTimeZoneProviderRequestUnbundled request); + protected abstract void onSetRequest(@NonNull LocationTimeZoneProviderRequestUnbundled request); private final class Service extends ILocationTimeZoneProvider.Stub { diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java index ab50dc3b00fc..10d103841bab 100644 --- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java +++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java @@ -16,6 +16,7 @@ package com.android.location.timezone.provider; +import android.annotation.IntRange; import android.annotation.NonNull; import com.android.internal.location.timezone.LocationTimeZoneProviderRequest; @@ -27,8 +28,6 @@ import java.util.Objects; * * <p>IMPORTANT: This class is effectively a public API for unbundled code, and must remain API * stable. - * - * @hide */ public final class LocationTimeZoneProviderRequestUnbundled { @@ -54,6 +53,7 @@ public final class LocationTimeZoneProviderRequestUnbundled { * true}. Failure to send an event in this time (with some fuzz) may be interpreted as if the * provider is uncertain of the time zone, and/or it could lead to the provider being disabled. */ + @IntRange(from = 0) public long getInitializationTimeoutMillis() { return mRequest.getInitializationTimeoutMillis(); } diff --git a/media/java/android/media/tv/TvChannelInfo.java b/media/java/android/media/tv/TvChannelInfo.java index 635b13045921..11cb1f7cd0bd 100644 --- a/media/java/android/media/tv/TvChannelInfo.java +++ b/media/java/android/media/tv/TvChannelInfo.java @@ -22,19 +22,44 @@ import android.annotation.Nullable; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + /** + * This class is used to specify information of a TV channel. * @hide */ public final class TvChannelInfo implements Parcelable { static final String TAG = "TvChannelInfo"; + + /** + * App tag for {@link #getAppTag()}: the corresponding application of the channel is the same as + * the caller. + * <p>{@link #getAppType()} returns {@link #APP_TYPE_SELF} if and only if the app tag is + * {@link #APP_TAG_SELF}. + */ public static final int APP_TAG_SELF = 0; + /** + * App tag for {@link #getAppType()}: the corresponding application of the channel is the same + * as the caller. + * <p>{@link #getAppType()} returns {@link #APP_TYPE_SELF} if and only if the app tag is + * {@link #APP_TAG_SELF}. + */ public static final int APP_TYPE_SELF = 1; + /** + * App tag for {@link #getAppType()}: the corresponding app of the channel is a system + * application. + */ public static final int APP_TYPE_SYSTEM = 2; + /** + * App tag for {@link #getAppType()}: the corresponding app of the channel is not a system + * application. + */ public static final int APP_TYPE_NON_SYSTEM = 3; /** @hide */ @@ -68,6 +93,7 @@ public final class TvChannelInfo implements Parcelable { @AppType private final int mAppType; private final int mAppTag; + /** @hide */ public TvChannelInfo( String inputId, @Nullable Uri channelUri, boolean isRecordingSession, boolean isForeground, @AppType int appType, int appTag) { @@ -90,24 +116,41 @@ public final class TvChannelInfo implements Parcelable { mAppTag = source.readInt(); } + /** + * Returns the TV input ID of the channel. + */ + @NonNull public String getInputId() { return mInputId; } + /** + * Returns the channel URI of the channel. + * <p>Returns {@code null} if it's a passthrough input or the permission is not granted. + */ + @Nullable public Uri getChannelUri() { return mChannelUri; } + /** + * Returns {@code true} if the channel session is a recording session. + * @see TvInputService.RecordingSession + */ public boolean isRecordingSession() { return mIsRecordingSession; } + /** + * Returns {@code true} if the application is a foreground application. + * @see android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND + */ public boolean isForeground() { return mIsForeground; } /** - * Gets app tag. + * Returns the app tag. * <p>App tag is used to differentiate one app from another. * {@link #APP_TAG_SELF} is for current app. */ @@ -115,6 +158,9 @@ public final class TvChannelInfo implements Parcelable { return mAppTag; } + /** + * Returns the app type. + */ @AppType public int getAppType() { return mAppType; @@ -126,7 +172,7 @@ public final class TvChannelInfo implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mInputId); String uriString = mChannelUri == null ? null : mChannelUri.toString(); dest.writeString(uriString); @@ -145,4 +191,26 @@ public final class TvChannelInfo implements Parcelable { + ";appType=" + mAppType + ";appTag=" + mAppTag; } + + @Override + public boolean equals(Object o) { + if (!(o instanceof TvChannelInfo)) { + return false; + } + + TvChannelInfo other = (TvChannelInfo) o; + + return TextUtils.equals(mInputId, other.getInputId()) + && Objects.equals(mChannelUri, other.mChannelUri) + && mIsRecordingSession == other.mIsRecordingSession + && mIsForeground == other.mIsForeground + && mAppType == other.mAppType + && mAppTag == other.mAppTag; + } + + @Override + public int hashCode() { + return Objects.hash( + mInputId, mChannelUri, mIsRecordingSession, mIsForeground, mAppType, mAppTag); + } } diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index d38369fea2b8..c80f3c6a0b39 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -900,8 +900,13 @@ public final class TvInputManager { public void onTvInputInfoUpdated(TvInputInfo inputInfo) { } - /** @hide */ - public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> tvChannelInfos) { + /** + * This is called when the information about current TV channels has been updated. + * + * @param tvChannelInfos a list of {@link TvChannelInfo} objects of new current channels. + * @hide + */ + public void onCurrentTvChannelInfosUpdated(@NonNull List<TvChannelInfo> tvChannelInfos) { } } @@ -1976,8 +1981,15 @@ public final class TvInputManager { } /** + * Returns the list of TV channel information for {@link TvInputService.Session} that are + * currently in use. + * <p> Permission com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS is required to get + * the channel URIs. If the permission is not granted, {@link TvChannelInfo#getChannelUri()} + * returns {@code null}. * @hide */ + @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") + @NonNull public List<TvChannelInfo> getCurrentTvChannelInfos() { try { return mService.getCurrentTvChannelInfos(mUserId); diff --git a/non-updatable-api/Android.bp b/non-updatable-api/Android.bp index 4037781c1844..00b901992b90 100644 --- a/non-updatable-api/Android.bp +++ b/non-updatable-api/Android.bp @@ -23,13 +23,31 @@ filegroup { } filegroup { + name: "non-updatable-removed.txt", + srcs: ["removed.txt"], + visibility: ["//frameworks/base/api"], +} + +filegroup { name: "non-updatable-system-current.txt", srcs: ["system-current.txt"], visibility: ["//frameworks/base/api"], } filegroup { + name: "non-updatable-system-removed.txt", + srcs: ["system-removed.txt"], + visibility: ["//frameworks/base/api"], +} + +filegroup { name: "non-updatable-module-lib-current.txt", srcs: ["module-lib-current.txt"], visibility: ["//frameworks/base/api"], } + +filegroup { + name: "non-updatable-module-lib-removed.txt", + srcs: ["module-lib-removed.txt"], + visibility: ["//frameworks/base/api"], +} diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index 983f20aad2e3..2bdf1a58e751 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -11454,10 +11454,10 @@ package android.content.pm { public final class ApkChecksum implements android.os.Parcelable { method public int describeContents(); - method public int getKind(); - method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException; - method @Nullable public String getSourcePackageName(); + method @Nullable public java.security.cert.Certificate getInstallerCertificate() throws java.security.cert.CertificateException; + method @Nullable public String getInstallerPackageName(); method @Nullable public String getSplitName(); + method public int getType(); method @NonNull public byte[] getValue(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ApkChecksum> CREATOR; @@ -11567,17 +11567,17 @@ package android.content.pm { public final class Checksum implements android.os.Parcelable { ctor public Checksum(int, @NonNull byte[]); method public int describeContents(); - method public int getKind(); + method public int getType(); method @NonNull public byte[] getValue(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Checksum> CREATOR; - field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20 - field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40 - field @Deprecated public static final int WHOLE_MD5 = 2; // 0x2 - field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1 - field @Deprecated public static final int WHOLE_SHA1 = 4; // 0x4 - field public static final int WHOLE_SHA256 = 8; // 0x8 - field public static final int WHOLE_SHA512 = 16; // 0x10 + field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20 + field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40 + field @Deprecated public static final int TYPE_WHOLE_MD5 = 2; // 0x2 + field public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1 + field @Deprecated public static final int TYPE_WHOLE_SHA1 = 4; // 0x4 + field @Deprecated public static final int TYPE_WHOLE_SHA256 = 8; // 0x8 + field @Deprecated public static final int TYPE_WHOLE_SHA512 = 16; // 0x10 } public class ComponentInfo extends android.content.pm.PackageItemInfo { @@ -12027,7 +12027,6 @@ package android.content.pm { method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public CharSequence getBackgroundPermissionOptionLabel(); method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int); - method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName); method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon(); method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo); @@ -12100,6 +12099,7 @@ package android.content.pm { method @Deprecated public abstract void removePackageFromPreferred(@NonNull String); method public abstract void removePermission(@NonNull String); method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int); + method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int); method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int); method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int); diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 18495294fbc4..b476e6de7055 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -126,6 +126,7 @@ package android { field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY"; field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER"; field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS"; + field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION"; field public static final String MANAGE_USB = "android.permission.MANAGE_USB"; field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS"; field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE"; @@ -306,6 +307,7 @@ package android { field public static final int config_helpPackageNameValue = 17039388; // 0x104001c field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemGallery = 17039399; // 0x1040027 + field public static final int config_systemVideoCall = 17039401; // 0x1040029 } public static final class R.style { @@ -1330,6 +1332,57 @@ package android.app.role { } +package android.app.time { + + public final class TimeManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void addTimeZoneDetectorListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.time.TimeManager.TimeZoneDetectorListener); + method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public android.app.time.TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig(); + method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void removeTimeZoneDetectorListener(@NonNull android.app.time.TimeManager.TimeZoneDetectorListener); + method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public boolean updateTimeZoneConfiguration(@NonNull android.app.time.TimeZoneConfiguration); + } + + @java.lang.FunctionalInterface public static interface TimeManager.TimeZoneDetectorListener { + method public void onChange(); + } + + public final class TimeZoneCapabilities implements android.os.Parcelable { + method public int describeContents(); + method public int getConfigureAutoDetectionEnabledCapability(); + method public int getConfigureGeoDetectionEnabledCapability(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPABILITY_NOT_ALLOWED = 20; // 0x14 + field public static final int CAPABILITY_NOT_APPLICABLE = 30; // 0x1e + field public static final int CAPABILITY_NOT_SUPPORTED = 10; // 0xa + field public static final int CAPABILITY_POSSESSED = 40; // 0x28 + field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilities> CREATOR; + } + + public final class TimeZoneCapabilitiesAndConfig implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.app.time.TimeZoneCapabilities getCapabilities(); + method @NonNull public android.app.time.TimeZoneConfiguration getConfiguration(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilitiesAndConfig> CREATOR; + } + + public final class TimeZoneConfiguration implements android.os.Parcelable { + method public int describeContents(); + method public boolean isAutoDetectionEnabled(); + method public boolean isGeoDetectionEnabled(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneConfiguration> CREATOR; + } + + public static final class TimeZoneConfiguration.Builder { + ctor public TimeZoneConfiguration.Builder(); + ctor public TimeZoneConfiguration.Builder(@NonNull android.app.time.TimeZoneConfiguration); + method @NonNull public android.app.time.TimeZoneConfiguration build(); + method @NonNull public android.app.time.TimeZoneConfiguration.Builder setAutoDetectionEnabled(boolean); + method @NonNull public android.app.time.TimeZoneConfiguration.Builder setGeoDetectionEnabled(boolean); + } + +} + package android.app.usage { public final class CacheQuotaHint implements android.os.Parcelable { @@ -4058,7 +4111,7 @@ package android.location { method @Deprecated @NonNull public String getProvider(); method public int getQuality(); method @Deprecated public float getSmallestDisplacement(); - method @Nullable public android.os.WorkSource getWorkSource(); + method @NonNull public android.os.WorkSource getWorkSource(); method public boolean isHiddenFromAppOps(); method public boolean isLocationSettingsIgnored(); method public boolean isLowPower(); diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index 9ff868467531..f7f3cbb7d332 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -321,6 +321,9 @@ public class DynamicSystemInstallationService extends Service if (!isDynamicSystemInstalled() && (getStatus() != STATUS_READY)) { Log.e(TAG, "Trying to discard AOT while there is no complete installation"); + // Stop foreground state and dismiss stale notification. + stopForeground(STOP_FOREGROUND_REMOVE); + resetTaskAndStop(); return; } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 6c7e03f104dd..f83c7a2e3963 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -361,6 +361,36 @@ public class InfoMediaManager extends MediaManager { return null; } + boolean shouldDisableMediaOutput(String packageName) { + boolean shouldDisableMediaOutput = false; + if (TextUtils.isEmpty(packageName)) { + Log.w(TAG, "shouldDisableMediaOutput() package name is null or empty!"); + return false; + } + final List<MediaRoute2Info> infos = mRouterManager.getAvailableRoutes(packageName); + if (infos.size() == 1) { + final MediaRoute2Info info = infos.get(0); + final int deviceType = info.getType(); + switch (deviceType) { + case TYPE_UNKNOWN: + case TYPE_REMOTE_TV: + case TYPE_REMOTE_SPEAKER: + case TYPE_GROUP: + shouldDisableMediaOutput = true; + break; + default: + shouldDisableMediaOutput = false; + break; + } + } + if (DEBUG) { + Log.d(TAG, "shouldDisableMediaOutput() MediaRoute2Info size : " + infos.size() + + ", package name : " + packageName + ", shouldDisableMediaOutput : " + + shouldDisableMediaOutput); + } + return shouldDisableMediaOutput; + } + private void refreshDevices() { mMediaDevices.clear(); mCurrentConnectedDevice = null; diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 72a6074ff89c..32419f49d8c9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -402,6 +402,13 @@ public class LocalMediaManager implements BluetoothCallback { return mPackageName; } + /** + * Returns {@code true} if needed to disable media output, otherwise returns {@code false}. + */ + public boolean shouldDisableMediaOutput(String packageName) { + return mInfoMediaManager.shouldDisableMediaOutput(packageName); + } + @VisibleForTesting MediaDevice updateCurrentConnectedDevice() { MediaDevice connectedDevice = null; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index 68f162ec0226..58ca73468316 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -27,6 +27,7 @@ import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -721,4 +722,47 @@ public class InfoMediaManagerTest { assertThat(mInfoMediaManager.mMediaDevices.size()).isEqualTo(0); } + + @Test + public void shouldDisableMediaOutput_infosSizeEqual1_returnsTrue() { + final MediaRoute2Info info = mock(MediaRoute2Info.class); + final List<MediaRoute2Info> infos = new ArrayList<>(); + infos.add(info); + mShadowRouter2Manager.setAvailableRoutes(infos); + + when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos); + when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER); + + assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isTrue(); + } + + @Test + public void shouldDisableMediaOutput_infosSizeEqual1AndNotCastDevice_returnsFalse() { + final MediaRoute2Info info = mock(MediaRoute2Info.class); + final List<MediaRoute2Info> infos = new ArrayList<>(); + infos.add(info); + mShadowRouter2Manager.setAvailableRoutes(infos); + + when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos); + when(info.getType()).thenReturn(TYPE_BUILTIN_SPEAKER); + + assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse(); + } + + + @Test + public void shouldDisableMediaOutput_infosSizeOverThan1_returnsFalse() { + final MediaRoute2Info info = mock(MediaRoute2Info.class); + final MediaRoute2Info info2 = mock(MediaRoute2Info.class); + final List<MediaRoute2Info> infos = new ArrayList<>(); + infos.add(info); + infos.add(info2); + mShadowRouter2Manager.setAvailableRoutes(infos); + + when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos); + when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER); + when(info2.getType()).thenReturn(TYPE_REMOTE_SPEAKER); + + assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse(); + } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index d2485cc49b6e..506b6085131b 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -892,9 +892,6 @@ class SettingsProtoDumpUtil { Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, GlobalSettingsProto.Location.SETTINGS_LINK_TO_PERMISSIONS_ENABLED); dumpSetting(s, p, - Settings.Global.LOCATION_GLOBAL_KILL_SWITCH, - GlobalSettingsProto.Location.GLOBAL_KILL_SWITCH); - dumpSetting(s, p, Settings.Global.GNSS_SATELLITE_BLACKLIST, GlobalSettingsProto.Location.GNSS_SATELLITE_BLACKLIST); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 3ccb1f4483b1..710c016d594c 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -328,10 +328,6 @@ public class SettingsProvider extends ContentProvider { return SettingsState.getUserIdFromKey(key); } - public static String settingTypeToString(int type) { - return SettingsState.settingTypeToString(type); - } - public static String keyToString(int key) { return SettingsState.keyToString(key); } @@ -373,8 +369,7 @@ public class SettingsProvider extends ContentProvider { } case Settings.CALL_METHOD_GET_SECURE: { - Setting setting = getSecureSetting(name, requestingUserId, - /*enableOverride=*/ true); + Setting setting = getSecureSetting(name, requestingUserId); return packageValueForCallResult(setting, isTrackingGeneration(args)); } @@ -581,7 +576,7 @@ public class SettingsProvider extends ContentProvider { } private ArrayList<String> buildSettingsList(Cursor cursor) { - final ArrayList<String> lines = new ArrayList<String>(); + final ArrayList<String> lines = new ArrayList<>(); try { while (cursor != null && cursor.moveToNext()) { lines.add(cursor.getString(1) + "=" + cursor.getString(2)); @@ -1381,10 +1376,6 @@ public class SettingsProvider extends ContentProvider { } private Setting getSecureSetting(String name, int requestingUserId) { - return getSecureSetting(name, requestingUserId, /*enableOverride=*/ false); - } - - private Setting getSecureSetting(String name, int requestingUserId, boolean enableOverride) { if (DEBUG) { Slog.v(LOG_TAG, "getSecureSetting(" + name + ", " + requestingUserId + ")"); } @@ -1414,14 +1405,6 @@ public class SettingsProvider extends ContentProvider { return getSsaidSettingLocked(callingPkg, owningUserId); } } - if (enableOverride) { - if (Secure.LOCATION_MODE.equals(name)) { - final Setting overridden = getLocationModeSetting(owningUserId); - if (overridden != null) { - return overridden; - } - } - } // Not the SSAID; do a straight lookup synchronized (mLock) { @@ -1511,35 +1494,6 @@ public class SettingsProvider extends ContentProvider { return null; } - private Setting getLocationModeSetting(int owningUserId) { - synchronized (mLock) { - final Setting setting = getGlobalSetting( - Global.LOCATION_GLOBAL_KILL_SWITCH); - if (!"1".equals(setting.getValue())) { - return null; - } - // Global kill-switch is enabled. Return an empty value. - final SettingsState settingsState = mSettingsRegistry.getSettingsLocked( - SETTINGS_TYPE_SECURE, owningUserId); - return settingsState.new Setting( - Secure.LOCATION_MODE, - "", // value - "", // tag - "", // default value - "", // package name - false, // from system - "0" // id - ) { - @Override - public boolean update(String value, boolean setDefault, String packageName, - String tag, boolean forceNonSystemPackage, boolean overrideableByRestore) { - Slog.wtf(LOG_TAG, "update shouldn't be called on this instance."); - return false; - } - }; - } - } - private boolean insertSecureSetting(String name, String value, String tag, boolean makeDefault, int requestingUserId, boolean forceNotify, boolean overrideableByRestore) { @@ -1808,12 +1762,8 @@ public class SettingsProvider extends ContentProvider { private boolean hasWriteSecureSettingsPermission() { // Write secure settings is a more protected permission. If caller has it we are good. - if (getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) - == PackageManager.PERMISSION_GRANTED) { - return true; - } - - return false; + return getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) + == PackageManager.PERMISSION_GRANTED; } private void validateSystemSettingValue(String name, String value) { @@ -3174,12 +3124,6 @@ public class SettingsProvider extends ContentProvider { if (isGlobalSettingsKey(key) || isConfigSettingsKey(key)) { final long token = Binder.clearCallingIdentity(); try { - if (Global.LOCATION_GLOBAL_KILL_SWITCH.equals(name) - && isGlobalSettingsKey(key)) { - // When the global kill switch is updated, send the - // change notification for the location setting. - notifyLocationChangeForRunningUsers(); - } notifySettingChangeForRunningUsers(key, name); } finally { Binder.restoreCallingIdentity(token); @@ -3257,26 +3201,6 @@ public class SettingsProvider extends ContentProvider { } } - private void notifyLocationChangeForRunningUsers() { - final List<UserInfo> users = mUserManager.getAliveUsers(); - - for (int i = 0; i < users.size(); i++) { - final int userId = users.get(i).id; - - if (!mUserManager.isUserRunning(UserHandle.of(userId))) { - continue; - } - - // Increment the generation first, so observers always see the new value - final int key = makeKey(SETTINGS_TYPE_SECURE, userId); - mGenerationRegistry.incrementGeneration(key); - - final Uri uri = getNotificationUriFor(key, Secure.LOCATION_MODE); - mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED, - userId, 0, uri).sendToTarget(); - } - } - private boolean isConfigSettingsKey(int key) { return getTypeFromKey(key) == SETTINGS_TYPE_CONFIG; } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 6678cf6f1033..b061df1423ba 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -34,7 +34,6 @@ import android.os.Message; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; -import android.provider.Settings.Global; import android.providers.settings.SettingsOperationProto; import android.text.TextUtils; import android.util.ArrayMap; @@ -47,7 +46,6 @@ import android.util.Xml; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import libcore.io.IoUtils; @@ -817,13 +815,6 @@ final class SettingsState { for (int i = 0; i < settingCount; i++) { Setting setting = settings.valueAt(i); - if (setting.isTransient()) { - if (DEBUG_PERSISTENCE) { - Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName()); - } - continue; - } - writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(), setting.getValue(), setting.getDefaultValue(), setting.getPackageName(), setting.getTag(), setting.isDefaultFromSystem(), @@ -1109,8 +1100,7 @@ final class SettingsState { ATTR_DEFAULT_VALUE_BASE64); String isPreservedInRestoreString = parser.getAttributeValue(null, ATTR_PRESERVE_IN_RESTORE); - boolean isPreservedInRestore = isPreservedInRestoreString != null - && Boolean.parseBoolean(isPreservedInRestoreString); + boolean isPreservedInRestore = Boolean.parseBoolean(isPreservedInRestoreString); String tag = null; boolean fromSystem = false; if (defaultValue != null) { @@ -1308,14 +1298,6 @@ final class SettingsState { /* resetToDefault */ true); } - public boolean isTransient() { - switch (getTypeFromKey(getKey())) { - case SETTINGS_TYPE_GLOBAL: - return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName()); - } - return false; - } - public boolean update(String value, boolean setDefault, String packageName, String tag, boolean forceNonSystemPackage, boolean overrideableByRestore) { return update(value, setDefault, packageName, tag, forceNonSystemPackage, @@ -1444,7 +1426,7 @@ final class SettingsState { } private static String fromBytes(byte[] bytes) { - final StringBuffer sb = new StringBuffer(bytes.length / 2); + final StringBuilder sb = new StringBuilder(bytes.length / 2); final int last = bytes.length - 1; diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index e027fd397cdd..34b7298103b5 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -159,6 +159,7 @@ public class SettingsBackupTest { Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS, Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS, Settings.Global.BLE_SCAN_BACKGROUND_MODE, + Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, Settings.Global.BLOCKED_SLICES, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, @@ -332,6 +333,7 @@ public class SettingsBackupTest { Settings.Global.MAX_ERROR_BYTES_PREFIX, Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY, + Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, Settings.Global.MDC_INITIAL_MAX_RETRY, Settings.Global.MHL_INPUT_SWITCHING_ENABLED, Settings.Global.MHL_POWER_CHARGE_ENABLED, diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 649ce3920d49..cb03d40dd579 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -1012,18 +1012,12 @@ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Settings"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string> - <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) --> - <skip /> - <!-- no translation found for accessibility_control_zoom_out (69578832020304084) --> - <skip /> - <!-- no translation found for accessibility_control_move_up (6622825494014720136) --> - <skip /> - <!-- no translation found for accessibility_control_move_down (5390922476900974512) --> - <skip /> - <!-- no translation found for accessibility_control_move_left (8156206978511401995) --> - <skip /> - <!-- no translation found for accessibility_control_move_right (8926821093629582888) --> - <skip /> + <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom in"</string> + <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom out"</string> + <string name="accessibility_control_move_up" msgid="6622825494014720136">"Move up"</string> + <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string> + <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string> + <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string> @@ -1087,8 +1081,6 @@ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string> <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string> <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string> - <!-- no translation found for build_number_clip_data_label (3623176728412560914) --> - <skip /> - <!-- no translation found for build_number_copy_toast (877720921605503046) --> - <skip /> + <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> + <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> </resources> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 88e497efc56f..8e5849e435f8 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -1012,18 +1012,12 @@ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Settings"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string> - <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) --> - <skip /> - <!-- no translation found for accessibility_control_zoom_out (69578832020304084) --> - <skip /> - <!-- no translation found for accessibility_control_move_up (6622825494014720136) --> - <skip /> - <!-- no translation found for accessibility_control_move_down (5390922476900974512) --> - <skip /> - <!-- no translation found for accessibility_control_move_left (8156206978511401995) --> - <skip /> - <!-- no translation found for accessibility_control_move_right (8926821093629582888) --> - <skip /> + <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom in"</string> + <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom out"</string> + <string name="accessibility_control_move_up" msgid="6622825494014720136">"Move up"</string> + <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string> + <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string> + <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string> @@ -1087,8 +1081,6 @@ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string> <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string> <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string> - <!-- no translation found for build_number_clip_data_label (3623176728412560914) --> - <skip /> - <!-- no translation found for build_number_copy_toast (877720921605503046) --> - <skip /> + <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> + <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 649ce3920d49..cb03d40dd579 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -1012,18 +1012,12 @@ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Settings"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string> - <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) --> - <skip /> - <!-- no translation found for accessibility_control_zoom_out (69578832020304084) --> - <skip /> - <!-- no translation found for accessibility_control_move_up (6622825494014720136) --> - <skip /> - <!-- no translation found for accessibility_control_move_down (5390922476900974512) --> - <skip /> - <!-- no translation found for accessibility_control_move_left (8156206978511401995) --> - <skip /> - <!-- no translation found for accessibility_control_move_right (8926821093629582888) --> - <skip /> + <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom in"</string> + <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom out"</string> + <string name="accessibility_control_move_up" msgid="6622825494014720136">"Move up"</string> + <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string> + <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string> + <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string> @@ -1087,8 +1081,6 @@ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string> <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string> <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string> - <!-- no translation found for build_number_clip_data_label (3623176728412560914) --> - <skip /> - <!-- no translation found for build_number_copy_toast (877720921605503046) --> - <skip /> + <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> + <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 649ce3920d49..cb03d40dd579 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -1012,18 +1012,12 @@ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Settings"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</string> - <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) --> - <skip /> - <!-- no translation found for accessibility_control_zoom_out (69578832020304084) --> - <skip /> - <!-- no translation found for accessibility_control_move_up (6622825494014720136) --> - <skip /> - <!-- no translation found for accessibility_control_move_down (5390922476900974512) --> - <skip /> - <!-- no translation found for accessibility_control_move_left (8156206978511401995) --> - <skip /> - <!-- no translation found for accessibility_control_move_right (8926821093629582888) --> - <skip /> + <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom in"</string> + <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom out"</string> + <string name="accessibility_control_move_up" msgid="6622825494014720136">"Move up"</string> + <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string> + <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string> + <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string> @@ -1087,8 +1081,6 @@ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string> <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string> <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string> - <!-- no translation found for build_number_clip_data_label (3623176728412560914) --> - <skip /> - <!-- no translation found for build_number_copy_toast (877720921605503046) --> - <skip /> + <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> + <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> </resources> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index f091e0b7c0b8..e107ed5ad3dc 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -1012,18 +1012,12 @@ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Settings"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Magnification Window"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification Window Controls"</string> - <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) --> - <skip /> - <!-- no translation found for accessibility_control_zoom_out (69578832020304084) --> - <skip /> - <!-- no translation found for accessibility_control_move_up (6622825494014720136) --> - <skip /> - <!-- no translation found for accessibility_control_move_down (5390922476900974512) --> - <skip /> - <!-- no translation found for accessibility_control_move_left (8156206978511401995) --> - <skip /> - <!-- no translation found for accessibility_control_move_right (8926821093629582888) --> - <skip /> + <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom in"</string> + <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom out"</string> + <string name="accessibility_control_move_up" msgid="6622825494014720136">"Move up"</string> + <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string> + <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string> + <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string> @@ -1087,8 +1081,6 @@ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string> <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string> <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new device"</string> - <!-- no translation found for build_number_clip_data_label (3623176728412560914) --> - <skip /> - <!-- no translation found for build_number_copy_toast (877720921605503046) --> - <skip /> + <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> + <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 524ced36d0ae..6fb84dd89424 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -1022,18 +1022,12 @@ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nustatymai"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Didinimo langas"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Didinimo lango valdikliai"</string> - <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) --> - <skip /> - <!-- no translation found for accessibility_control_zoom_out (69578832020304084) --> - <skip /> - <!-- no translation found for accessibility_control_move_up (6622825494014720136) --> - <skip /> - <!-- no translation found for accessibility_control_move_down (5390922476900974512) --> - <skip /> - <!-- no translation found for accessibility_control_move_left (8156206978511401995) --> - <skip /> - <!-- no translation found for accessibility_control_move_right (8926821093629582888) --> - <skip /> + <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Artinti"</string> + <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Tolinti"</string> + <string name="accessibility_control_move_up" msgid="6622825494014720136">"Perkelti aukštyn"</string> + <string name="accessibility_control_move_down" msgid="5390922476900974512">"Perkelti žemyn"</string> + <string name="accessibility_control_move_left" msgid="8156206978511401995">"Perkelti kairėn"</string> + <string name="accessibility_control_move_right" msgid="8926821093629582888">"Perkelti dešinėn"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Įrenginio valdikliai"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Pridėkite prijungtų įrenginių valdiklių"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Įrenginio valdiklių nustatymas"</string> @@ -1099,8 +1093,6 @@ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"„<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“ (atjungta)"</string> <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nepavyko prijungti. Bandykite dar kartą."</string> <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Naujo įrenginio susiejimas"</string> - <!-- no translation found for build_number_clip_data_label (3623176728412560914) --> - <skip /> - <!-- no translation found for build_number_copy_toast (877720921605503046) --> - <skip /> + <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijos numeris"</string> + <string name="build_number_copy_toast" msgid="877720921605503046">"Versijos numeris nukopijuotas į iškarpinę."</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 46cf45a8416e..b76ebda64705 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -1012,18 +1012,12 @@ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configurações"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Controles da janela de ampliação"</string> - <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) --> - <skip /> - <!-- no translation found for accessibility_control_zoom_out (69578832020304084) --> - <skip /> - <!-- no translation found for accessibility_control_move_up (6622825494014720136) --> - <skip /> - <!-- no translation found for accessibility_control_move_down (5390922476900974512) --> - <skip /> - <!-- no translation found for accessibility_control_move_left (8156206978511401995) --> - <skip /> - <!-- no translation found for accessibility_control_move_right (8926821093629582888) --> - <skip /> + <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumentar zoom"</string> + <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Diminuir zoom"</string> + <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover para cima"</string> + <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string> + <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string> + <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adiciona controles aos dispositivos conectados"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles do dispositivo"</string> @@ -1087,8 +1081,6 @@ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string> <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Não foi possível conectar. Tente novamente."</string> <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear novo dispositivo"</string> - <!-- no translation found for build_number_clip_data_label (3623176728412560914) --> - <skip /> - <!-- no translation found for build_number_copy_toast (877720921605503046) --> - <skip /> + <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da versão"</string> + <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 46cf45a8416e..b76ebda64705 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -1012,18 +1012,12 @@ <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configurações"</string> <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string> <string name="magnification_controls_title" msgid="8421106606708891519">"Controles da janela de ampliação"</string> - <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) --> - <skip /> - <!-- no translation found for accessibility_control_zoom_out (69578832020304084) --> - <skip /> - <!-- no translation found for accessibility_control_move_up (6622825494014720136) --> - <skip /> - <!-- no translation found for accessibility_control_move_down (5390922476900974512) --> - <skip /> - <!-- no translation found for accessibility_control_move_left (8156206978511401995) --> - <skip /> - <!-- no translation found for accessibility_control_move_right (8926821093629582888) --> - <skip /> + <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumentar zoom"</string> + <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Diminuir zoom"</string> + <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover para cima"</string> + <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string> + <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string> + <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adiciona controles aos dispositivos conectados"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles do dispositivo"</string> @@ -1087,8 +1081,6 @@ <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string> <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Não foi possível conectar. Tente novamente."</string> <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear novo dispositivo"</string> - <!-- no translation found for build_number_clip_data_label (3623176728412560914) --> - <skip /> - <!-- no translation found for build_number_copy_toast (877720921605503046) --> - <skip /> + <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da versão"</string> + <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index c94bcaaf7383..e4427f49e030 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -16,6 +16,9 @@ package com.android.systemui.shared.recents; +import android.app.PictureInPictureParams; +import android.content.ComponentName; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; @@ -166,4 +169,27 @@ interface ISystemUiProxy { * Notifies to expand notification panel. */ void expandNotificationPanel() = 29; + + /** + * Notifies that Activity is about to be swiped to home with entering PiP transition and + * queries the destination bounds for PiP depends on Launcher's rotation and shelf height. + * + * @param componentName ComponentName represents the Activity + * @param activityInfo ActivityInfo tied to the Activity + * @param pictureInPictureParams PictureInPictureParams tied to the Activity + * @param launcherRotation Launcher rotation to calculate the PiP destination bounds + * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds + * @return destination bounds the PiP window should land into + */ + Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo, + in PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) = 30; + + /** + * Notifies the swiping Activity to PiP onto home transition is finished + * + * @param componentName ComponentName represents the Activity + * @param destinationBounds the destination bounds the PiP window lands into + */ + void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 31; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index ee05c6cf4c47..b526a9261dc5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -34,7 +34,6 @@ import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.AppGlobals; -import android.app.IAssistDataReceiver; import android.app.WindowConfiguration; import android.content.ContentResolver; import android.content.Context; @@ -43,7 +42,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; -import android.graphics.Bitmap; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; @@ -228,21 +226,10 @@ public class ActivityManagerWrapper { /** * Starts the recents activity. The caller should manage the thread on which this is called. */ - public void startRecentsActivity(Intent intent, final AssistDataReceiver assistDataReceiver, + public void startRecentsActivity(Intent intent, long eventTime, final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback, Handler resultCallbackHandler) { try { - IAssistDataReceiver receiver = null; - if (assistDataReceiver != null) { - receiver = new IAssistDataReceiver.Stub() { - public void onHandleAssistData(Bundle resultData) { - assistDataReceiver.onHandleAssistData(resultData); - } - public void onHandleAssistScreenshot(Bitmap screenshot) { - assistDataReceiver.onHandleAssistScreenshot(screenshot); - } - }; - } IRecentsAnimationRunner runner = null; if (animationHandler != null) { runner = new IRecentsAnimationRunner.Stub() { @@ -272,7 +259,7 @@ public class ActivityManagerWrapper { } }; } - ActivityTaskManager.getService().startRecentsActivity(intent, receiver, runner); + ActivityTaskManager.getService().startRecentsActivity(intent, eventTime, runner); if (resultCallback != null) { resultCallbackHandler.post(new Runnable() { @Override @@ -296,9 +283,9 @@ public class ActivityManagerWrapper { /** * Cancels the remote recents animation started from {@link #startRecentsActivity}. */ - public void cancelRecentsAnimation(boolean restoreHomeStackPosition) { + public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) { try { - ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeStackPosition); + ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeRootTaskPosition); } catch (RemoteException e) { Log.e(TAG, "Failed to cancel recents animation", e); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index e5f2ad5b7586..5122f6cf31a1 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -27,6 +27,7 @@ import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.util.Log; +import android.view.InsetsState; import android.view.SurfaceControl; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -81,12 +82,25 @@ public class WindowManagerWrapper { WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; public static final int WINDOWING_MODE_FREEFORM = WindowConfiguration.WINDOWING_MODE_FREEFORM; + public static final int ITYPE_EXTRA_NAVIGATION_BAR = InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; + private static final WindowManagerWrapper sInstance = new WindowManagerWrapper(); public static WindowManagerWrapper getInstance() { return sInstance; } + + /** + * Sets {@param providesInsetsTypes} as the inset types provided by {@param params}. + * @param params The window layout params. + * @param providesInsetsTypes The inset types we would like this layout params to provide. + */ + public void setProvidesInsetsTypes(WindowManager.LayoutParams params, + int[] providesInsetsTypes) { + params.providesInsetsTypes = providesInsetsTypes; + } + /** * @return the stable insets for the primary display. */ diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index 911bf9ef757b..a705ec784c9a 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -37,6 +37,7 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.CommandQueue; import javax.inject.Inject; @@ -66,7 +67,8 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall @Inject public WindowMagnification(Context context, @Main Handler mainHandler, - CommandQueue commandQueue, ModeSwitchesController modeSwitchesController) { + CommandQueue commandQueue, ModeSwitchesController modeSwitchesController, + NavigationModeController navigationModeController) { super(context); mHandler = mainHandler; mLastConfiguration = new Configuration(context.getResources().getConfiguration()); @@ -77,6 +79,9 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall final WindowMagnificationController controller = new WindowMagnificationController(mContext, mHandler, new SfVsyncFrameCallbackProvider(), null, new SurfaceControl.Transaction(), this); + final int navBarMode = navigationModeController.addListener( + controller::onNavigationModeChanged); + controller.onNavigationModeChanged(navBarMode); mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( mContext, controller); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 3832ff307d20..c3474bb7ca57 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -17,6 +17,8 @@ package com.android.systemui.accessibility; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import android.annotation.NonNull; import android.annotation.Nullable; @@ -117,6 +119,9 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold // The boundary of magnification frame. private final Rect mMagnificationFrameBoundary = new Rect(); + private int mNavBarMode; + private int mNavGestureHeight; + private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; private Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback; private Locale mLocale; @@ -195,6 +200,19 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold R.dimen.magnification_drag_view_size); mOuterBorderSize = mResources.getDimensionPixelSize( R.dimen.magnification_outer_border_margin); + updateNavigationBarDimensions(); + } + + private void updateNavigationBarDimensions() { + if (!supportsSwipeUpGesture()) { + mNavGestureHeight = 0; + return; + } + mNavGestureHeight = (mDisplaySize.x > mDisplaySize.y) + ? mResources.getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_height_landscape) + : mResources.getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_gesture_height); } /** @@ -239,6 +257,13 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } } + /** Handles MirrorWindow position when the navigation bar mode changed. */ + public void onNavigationModeChanged(int mode) { + mNavBarMode = mode; + updateNavigationBarDimensions(); + updateMirrorViewLayout(); + } + /** Handles MirrorWindow position when the device rotation changed. */ private void onRotate() { final Display display = mContext.getDisplay(); @@ -246,6 +271,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold display.getRealSize(mDisplaySize); setMagnificationFrameBoundary(); mRotation = display.getRotation(); + updateNavigationBarDimensions(); if (!isWindowVisible()) { return; @@ -401,15 +427,23 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold * moved close to the screen edges. */ private void updateMirrorViewLayout() { + if (!isWindowVisible()) { + return; + } + final int maxMirrorViewX = mDisplaySize.x - mMirrorView.getWidth(); + final int maxMirrorViewY = mDisplaySize.y - mMirrorView.getHeight() - mNavGestureHeight; WindowManager.LayoutParams params = (WindowManager.LayoutParams) mMirrorView.getLayoutParams(); params.x = mMagnificationFrame.left - mMirrorSurfaceMargin; params.y = mMagnificationFrame.top - mMirrorSurfaceMargin; + // If nav bar mode supports swipe-up gesture, the Y position of mirror view should not + // overlap nav bar window to prevent window-dragging obscured. + if (supportsSwipeUpGesture()) { + params.y = Math.min(params.y, maxMirrorViewY); + } // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView // able to move close to the screen edges. - final int maxMirrorViewX = mDisplaySize.x - mMirrorView.getWidth(); - final int maxMirrorViewY = mDisplaySize.y - mMirrorView.getHeight(); final float translationX; final float translationY; if (params.x < 0) { @@ -621,6 +655,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return mMirrorView != null; } + private boolean supportsSwipeUpGesture() { + return mNavBarMode == NAV_BAR_MODE_2BUTTON || mNavBarMode == NAV_BAR_MODE_GESTURAL; + } + private CharSequence formatStateDescription(float scale) { // Cache the locale-appropriate NumberFormat. Configuration locale is guaranteed // non-null, so the first time this is called we will always get the appropriate diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 5b852086fe9f..529af2297ef1 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -25,6 +25,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; +import android.app.Person; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -35,16 +36,18 @@ import android.graphics.Bitmap; import android.graphics.Path; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.os.Parcelable; import android.os.UserHandle; import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.List; import java.util.Objects; /** @@ -169,11 +172,10 @@ class Bubble implements BubbleViewProvider { } @VisibleForTesting(visibility = PRIVATE) - Bubble(@NonNull final NotificationEntry e, + Bubble(@NonNull final BubbleEntry entry, @Nullable final BubbleController.NotificationSuppressionChangedListener listener, final BubbleController.PendingIntentCanceledListener intentCancelListener) { - Objects.requireNonNull(e); - mKey = e.getKey(); + mKey = entry.getKey(); mSuppressionListener = listener; mIntentCancelListener = intent -> { if (mIntent != null) { @@ -181,7 +183,7 @@ class Bubble implements BubbleViewProvider { } intentCancelListener.onPendingIntentCanceled(this); }; - setEntry(e); + setEntry(entry); } @Override @@ -294,8 +296,8 @@ class Bubble implements BubbleViewProvider { } /** - * Sets whether this bubble is considered visually interruptive. Normally pulled from the - * {@link NotificationEntry}, this method is purely for testing. + * Sets whether this bubble is considered visually interruptive. This method is purely for + * testing. */ @VisibleForTesting void setVisuallyInterruptiveForTest(boolean visuallyInterruptive) { @@ -388,30 +390,28 @@ class Bubble implements BubbleViewProvider { /** * Sets the entry associated with this bubble. */ - void setEntry(@NonNull final NotificationEntry entry) { + void setEntry(@NonNull final BubbleEntry entry) { Objects.requireNonNull(entry); - Objects.requireNonNull(entry.getSbn()); - mLastUpdated = entry.getSbn().getPostTime(); - mIsBubble = entry.getSbn().getNotification().isBubbleNotification(); - mPackageName = entry.getSbn().getPackageName(); - mUser = entry.getSbn().getUser(); + mLastUpdated = entry.getStatusBarNotification().getPostTime(); + mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification(); + mPackageName = entry.getStatusBarNotification().getPackageName(); + mUser = entry.getStatusBarNotification().getUser(); mTitle = getTitle(entry); - mIsClearable = entry.isClearable(); - mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot(); - mShouldSuppressNotificationList = entry.shouldSuppressNotificationList(); - mShouldSuppressPeek = entry.shouldSuppressPeek(); - mChannelId = entry.getSbn().getNotification().getChannelId(); - mNotificationId = entry.getSbn().getId(); - mAppUid = entry.getSbn().getUid(); - mInstanceId = entry.getSbn().getInstanceId(); - mFlyoutMessage = BubbleViewInfoTask.extractFlyoutMessage(entry); - mShortcutInfo = (entry.getRanking() != null ? entry.getRanking().getShortcutInfo() : null); - mMetadataShortcutId = (entry.getBubbleMetadata() != null - ? entry.getBubbleMetadata().getShortcutId() : null); + mChannelId = entry.getStatusBarNotification().getNotification().getChannelId(); + mNotificationId = entry.getStatusBarNotification().getId(); + mAppUid = entry.getStatusBarNotification().getUid(); + mInstanceId = entry.getStatusBarNotification().getInstanceId(); + mFlyoutMessage = extractFlyoutMessage(entry); if (entry.getRanking() != null) { + mShortcutInfo = entry.getRanking().getShortcutInfo(); mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive(); + if (entry.getRanking().getChannel() != null) { + mIsImportantConversation = + entry.getRanking().getChannel().isImportantConversation(); + } } if (entry.getBubbleMetadata() != null) { + mMetadataShortcutId = entry.getBubbleMetadata().getShortcutId(); mFlags = entry.getBubbleMetadata().getFlags(); mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight(); mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId(); @@ -433,8 +433,11 @@ class Bubble implements BubbleViewProvider { } mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent(); } - mIsImportantConversation = - entry.getChannel() != null && entry.getChannel().isImportantConversation(); + + mIsClearable = entry.isClearable(); + mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot(); + mShouldSuppressNotificationList = entry.shouldSuppressNotificationList(); + mShouldSuppressPeek = entry.shouldSuppressPeek(); } @Nullable @@ -735,9 +738,75 @@ class Bubble implements BubbleViewProvider { } @Nullable - private static String getTitle(@NonNull final NotificationEntry e) { - final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence( - Notification.EXTRA_TITLE); + private static String getTitle(@NonNull final BubbleEntry e) { + final CharSequence titleCharSeq = e.getStatusBarNotification() + .getNotification().extras.getCharSequence(Notification.EXTRA_TITLE); return titleCharSeq == null ? null : titleCharSeq.toString(); } + + /** + * Returns our best guess for the most relevant text summary of the latest update to this + * notification, based on its type. Returns null if there should not be an update message. + */ + @NonNull + static Bubble.FlyoutMessage extractFlyoutMessage(BubbleEntry entry) { + Objects.requireNonNull(entry); + final Notification underlyingNotif = entry.getStatusBarNotification().getNotification(); + final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle(); + + Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage(); + bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean( + Notification.EXTRA_IS_GROUP_CONVERSATION); + try { + if (Notification.BigTextStyle.class.equals(style)) { + // Return the big text, it is big so probably important. If it's not there use the + // normal text. + CharSequence bigText = + underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT); + bubbleMessage.message = !TextUtils.isEmpty(bigText) + ? bigText + : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT); + return bubbleMessage; + } else if (Notification.MessagingStyle.class.equals(style)) { + final List<Notification.MessagingStyle.Message> messages = + Notification.MessagingStyle.Message.getMessagesFromBundleArray( + (Parcelable[]) underlyingNotif.extras.get( + Notification.EXTRA_MESSAGES)); + + final Notification.MessagingStyle.Message latestMessage = + Notification.MessagingStyle.findLatestIncomingMessage(messages); + if (latestMessage != null) { + bubbleMessage.message = latestMessage.getText(); + Person sender = latestMessage.getSenderPerson(); + bubbleMessage.senderName = sender != null ? sender.getName() : null; + bubbleMessage.senderAvatar = null; + bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null; + return bubbleMessage; + } + } else if (Notification.InboxStyle.class.equals(style)) { + CharSequence[] lines = + underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES); + + // Return the last line since it should be the most recent. + if (lines != null && lines.length > 0) { + bubbleMessage.message = lines[lines.length - 1]; + return bubbleMessage; + } + } else if (Notification.MediaStyle.class.equals(style)) { + // Return nothing, media updates aren't typically useful as a text update. + return bubbleMessage; + } else { + // Default to text extra. + bubbleMessage.message = + underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT); + return bubbleMessage; + } + } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) { + // No use crashing, we'll just return null and the caller will assume there's no update + // message. + e.printStackTrace(); + } + + return bubbleMessage; + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 90b64eaf5b47..2372529b6f11 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -231,6 +231,11 @@ public class BubbleController implements Bubbles, ConfigurationController.Config */ private int mDensityDpi = Configuration.DENSITY_DPI_UNDEFINED; + /** + * Last known font scale, used to detect font size changes in {@link #onConfigChanged}. + */ + private float mFontScale = 0; + /** Last known direction, used to detect layout direction changes @link #onConfigChanged}. */ private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED; @@ -345,7 +350,42 @@ public class BubbleController implements Bubbles, ConfigurationController.Config /** * Injected constructor. See {@link BubbleModule}. */ - public BubbleController(Context context, + public static BubbleController create(Context context, + NotificationShadeWindowController notificationShadeWindowController, + StatusBarStateController statusBarStateController, + ShadeController shadeController, + @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, + ConfigurationController configurationController, + NotificationInterruptStateProvider interruptionStateProvider, + ZenModeController zenModeController, + NotificationLockscreenUserManager notifUserManager, + NotificationGroupManagerLegacy groupManager, + NotificationEntryManager entryManager, + NotifPipeline notifPipeline, + FeatureFlags featureFlags, + DumpManager dumpManager, + FloatingContentCoordinator floatingContentCoordinator, + BubbleDataRepository dataRepository, + SysUiState sysUiState, + INotificationManager notificationManager, + @Nullable IStatusBarService statusBarService, + WindowManager windowManager, + WindowManagerShellWrapper windowManagerShellWrapper, + LauncherApps launcherApps) { + return new BubbleController(context, notificationShadeWindowController, + statusBarStateController, shadeController, new BubbleData(context), synchronizer, + configurationController, interruptionStateProvider, zenModeController, + notifUserManager, groupManager, entryManager, notifPipeline, featureFlags, + dumpManager, floatingContentCoordinator, dataRepository, sysUiState, + notificationManager, statusBarService, windowManager, windowManagerShellWrapper, + launcherApps); + } + + /** + * Testing constructor. + */ + @VisibleForTesting + BubbleController(Context context, NotificationShadeWindowController notificationShadeWindowController, StatusBarStateController statusBarStateController, ShadeController shadeController, @@ -955,6 +995,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config mBubbleIconFactory = new BubbleIconFactory(mContext); mStackView.onDisplaySizeChanged(); } + if (newConfig.fontScale != mFontScale) { + mFontScale = newConfig.fontScale; + mStackView.updateFlyout(mFontScale); + } if (newConfig.getLayoutDirection() != mLayoutDirection) { mLayoutDirection = newConfig.getLayoutDirection(); mStackView.onLayoutDirectionChanged(mLayoutDirection); @@ -1120,9 +1164,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) { // Update the bubble but don't promote it out of overflow Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey()); - b.setEntry(notif); + b.setEntry(notifToBubbleEntry(notif)); } else { - Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */); + Bubble bubble = mBubbleData.getOrCreateBubble( + notifToBubbleEntry(notif), null /* persistedBubble */); inflateAndAdd(bubble, suppressFlyout, showInShade); } } @@ -1208,8 +1253,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config mBubbleData.removeSuppressedSummary(groupKey); // Remove any associated bubble children with the summary - final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup( - groupKey, mNotificationEntryManager); + final List<Bubble> bubbleChildren = getBubblesInGroup(groupKey); for (int i = 0; i < bubbleChildren.size(); i++) { removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED); } @@ -1255,6 +1299,25 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } + /** + * Retrieves any bubbles that are part of the notification group represented by the provided + * group key. + */ + private ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey) { + ArrayList<Bubble> bubbleChildren = new ArrayList<>(); + if (groupKey == null) { + return bubbleChildren; + } + for (Bubble bubble : mBubbleData.getActiveBubbles()) { + final NotificationEntry entry = + mNotificationEntryManager.getPendingOrActiveNotif(bubble.getKey()); + if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) { + bubbleChildren.add(bubble); + } + } + return bubbleChildren; + } + private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble, final boolean autoExpand) { Objects.requireNonNull(entry); @@ -1365,8 +1428,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } if (entry != null) { final String groupKey = entry.getSbn().getGroupKey(); - if (mBubbleData.getBubblesInGroup( - groupKey, mNotificationEntryManager).isEmpty()) { + if (getBubblesInGroup(groupKey).isEmpty()) { // Time to potentially remove the summary for (NotifCallback cb : mCallbacks) { cb.maybeCancelSummary(entry); @@ -1449,8 +1511,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } String groupKey = entry.getSbn().getGroupKey(); - ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup( - groupKey, mNotificationEntryManager); + ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey); boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey) && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey())); boolean isSummary = entry.getSbn().getNotification().isGroupSummary(); @@ -1694,4 +1755,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } } } + + static BubbleEntry notifToBubbleEntry(NotificationEntry e) { + return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(), + e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(), + e.shouldSuppressPeek()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 2c3cb5f8dcb0..55ecb226e3cb 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -36,8 +36,6 @@ import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController.DismissReason; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.SysUiStatsLog; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -52,8 +50,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; -import javax.inject.Inject; - /** * Keeps track of active bubbles. */ @@ -154,12 +150,11 @@ public class BubbleData { * associated with it). This list is used to check if the summary should be hidden from the * shade. * - * Key: group key of the NotificationEntry - * Value: key of the NotificationEntry + * Key: group key of the notification + * Value: key of the notification */ private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>(); - @Inject public BubbleData(Context context) { mContext = context; mBubbles = new ArrayList<>(); @@ -205,6 +200,11 @@ public class BubbleData { return mSelectedBubble; } + /** Return a read-only current active bubble lists. */ + public List<Bubble> getActiveBubbles() { + return Collections.unmodifiableList(mBubbles); + } + public void setExpanded(boolean expanded) { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "setExpanded: " + expanded); @@ -235,8 +235,8 @@ public class BubbleData { * @param persistedBubble The bubble to use, only non-null if it's a bubble being promoted from * the overflow that was persisted over reboot. */ - Bubble getOrCreateBubble(NotificationEntry entry, Bubble persistedBubble) { - String key = entry != null ? entry.getKey() : persistedBubble.getKey(); + public Bubble getOrCreateBubble(BubbleEntry entry, Bubble persistedBubble) { + String key = persistedBubble != null ? persistedBubble.getKey() : entry.getKey(); Bubble bubbleToReturn = getBubbleInStackWithKey(key); if (bubbleToReturn == null) { @@ -266,7 +266,7 @@ public class BubbleData { /** * When this method is called it is expected that all info in the bubble has completed loading. * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, - * BubbleStackView, BubbleIconFactory). + * BubbleStackView, BubbleIconFactory, boolean). */ void notificationEntryUpdated(Bubble bubble, boolean suppressFlyout, boolean showInShade) { if (DEBUG_BUBBLE_DATA) { @@ -329,7 +329,7 @@ public class BubbleData { * Retrieves the notif entry key of the summary associated with the provided group key. * * @param groupKey the group to look up - * @return the key for the {@link NotificationEntry} that is the summary of this group. + * @return the key for the notification that is the summary of this group. */ String getSummaryKey(String groupKey) { return mSuppressedGroupKeys.get(groupKey); @@ -350,25 +350,6 @@ public class BubbleData { } /** - * Retrieves any bubbles that are part of the notification group represented by the provided - * group key. - */ - ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey, @NonNull - NotificationEntryManager nem) { - ArrayList<Bubble> bubbleChildren = new ArrayList<>(); - if (groupKey == null) { - return bubbleChildren; - } - for (Bubble b : mBubbles) { - final NotificationEntry entry = nem.getPendingOrActiveNotif(b.getKey()); - if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) { - bubbleChildren.add(b); - } - } - return bubbleChildren; - } - - /** * Removes bubbles from the given package whose shortcut are not in the provided list of valid * shortcuts. */ diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java new file mode 100644 index 000000000000..6a1302518699 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java @@ -0,0 +1,98 @@ +/* + * 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.systemui.bubbles; + +import android.app.Notification.BubbleMetadata; +import android.app.NotificationManager.Policy; +import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.StatusBarNotification; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * Represents a notification with needed data and flag for bubbles. + * + * @see Bubble + */ +public class BubbleEntry { + + private StatusBarNotification mSbn; + private Ranking mRanking; + + private boolean mIsClearable; + private boolean mShouldSuppressNotificationDot; + private boolean mShouldSuppressNotificationList; + private boolean mShouldSuppressPeek; + + public BubbleEntry(@NonNull StatusBarNotification sbn, + Ranking ranking, boolean isClearable, boolean shouldSuppressNotificationDot, + boolean shouldSuppressNotificationList, boolean shouldSuppressPeek) { + mSbn = sbn; + mRanking = ranking; + + mIsClearable = isClearable; + mShouldSuppressNotificationDot = shouldSuppressNotificationDot; + mShouldSuppressNotificationList = shouldSuppressNotificationList; + mShouldSuppressPeek = shouldSuppressPeek; + } + + /** @return the {@link StatusBarNotification} for this entry. */ + @NonNull + public StatusBarNotification getStatusBarNotification() { + return mSbn; + } + + /** @return the {@link Ranking} for this entry. */ + public Ranking getRanking() { + return mRanking; + } + + /** @return the key in the {@link StatusBarNotification}. */ + public String getKey() { + return mSbn.getKey(); + } + + /** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */ + @Nullable + public BubbleMetadata getBubbleMetadata() { + return getStatusBarNotification().getNotification().getBubbleMetadata(); + } + + /** @return true if this notification is clearable. */ + public boolean isClearable() { + return mIsClearable; + } + + /** @return true if {@link Policy#SUPPRESSED_EFFECT_BADGE} set for this notification. */ + public boolean shouldSuppressNotificationDot() { + return mShouldSuppressNotificationDot; + } + + /** + * @return true if {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST} + * set for this notification. + */ + public boolean shouldSuppressNotificationList() { + return mShouldSuppressNotificationList; + } + + /** @return true if {@link Policy#SUPPRESSED_EFFECT_PEEK} set for this notification. */ + public boolean shouldSuppressPeek() { + return mShouldSuppressPeek; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java index 1fa3aaae5e61..69f7828ff8fc 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java @@ -34,6 +34,7 @@ import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.text.TextUtils; +import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -212,6 +213,14 @@ public class BubbleFlyoutView extends FrameLayout { super.onDraw(canvas); } + void updateFontSize(float fontScale) { + final float fontSize = mContext.getResources() + .getDimensionPixelSize(com.android.internal.R.dimen.text_size_body_2_material); + final float newFontSize = fontSize * fontScale; + mMessageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, newFontSize); + mSenderText.setTextSize(TypedValue.COMPLEX_UNIT_PX, newFontSize); + } + /** Configures the flyout, collapsed into to dot form. */ void setupFlyoutStartingAsDot( Bubble.FlyoutMessage flyoutMessage, diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 0dcd1d2b4b09..e83954b8de18 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -1156,6 +1156,10 @@ public class BubbleStackView extends FrameLayout addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); } + void updateFlyout(float fontScale) { + mFlyout.updateFontSize(fontScale); + } + private void updateOverflow() { mBubbleOverflow.update(); mBubbleContainer.reorderView(mBubbleOverflow.getIconView(), diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java index 28757facc220..010a29e3560a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java @@ -22,8 +22,6 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import android.annotation.NonNull; -import android.app.Notification; -import android.app.Person; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -36,8 +34,6 @@ import android.graphics.Path; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.AsyncTask; -import android.os.Parcelable; -import android.text.TextUtils; import android.util.Log; import android.util.PathParser; import android.view.LayoutInflater; @@ -47,10 +43,8 @@ import androidx.annotation.Nullable; import com.android.internal.graphics.ColorUtils; import com.android.launcher3.icons.BitmapInfo; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; import java.lang.ref.WeakReference; -import java.util.List; import java.util.Objects; /** @@ -208,73 +202,6 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask } } - - /** - * Returns our best guess for the most relevant text summary of the latest update to this - * notification, based on its type. Returns null if there should not be an update message. - */ - @NonNull - static Bubble.FlyoutMessage extractFlyoutMessage(NotificationEntry entry) { - Objects.requireNonNull(entry); - final Notification underlyingNotif = entry.getSbn().getNotification(); - final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle(); - - Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage(); - bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean( - Notification.EXTRA_IS_GROUP_CONVERSATION); - try { - if (Notification.BigTextStyle.class.equals(style)) { - // Return the big text, it is big so probably important. If it's not there use the - // normal text. - CharSequence bigText = - underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT); - bubbleMessage.message = !TextUtils.isEmpty(bigText) - ? bigText - : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT); - return bubbleMessage; - } else if (Notification.MessagingStyle.class.equals(style)) { - final List<Notification.MessagingStyle.Message> messages = - Notification.MessagingStyle.Message.getMessagesFromBundleArray( - (Parcelable[]) underlyingNotif.extras.get( - Notification.EXTRA_MESSAGES)); - - final Notification.MessagingStyle.Message latestMessage = - Notification.MessagingStyle.findLatestIncomingMessage(messages); - if (latestMessage != null) { - bubbleMessage.message = latestMessage.getText(); - Person sender = latestMessage.getSenderPerson(); - bubbleMessage.senderName = sender != null ? sender.getName() : null; - bubbleMessage.senderAvatar = null; - bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null; - return bubbleMessage; - } - } else if (Notification.InboxStyle.class.equals(style)) { - CharSequence[] lines = - underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES); - - // Return the last line since it should be the most recent. - if (lines != null && lines.length > 0) { - bubbleMessage.message = lines[lines.length - 1]; - return bubbleMessage; - } - } else if (Notification.MediaStyle.class.equals(style)) { - // Return nothing, media updates aren't typically useful as a text update. - return bubbleMessage; - } else { - // Default to text extra. - bubbleMessage.message = - underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT); - return bubbleMessage; - } - } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) { - // No use crashing, we'll just return null and the caller will assume there's no update - // message. - e.printStackTrace(); - } - - return bubbleMessage; - } - @Nullable static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) { Objects.requireNonNull(context); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java index d2eaf0d8d810..00ae3a33b468 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java @@ -23,7 +23,6 @@ import android.view.WindowManager; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.bubbles.BubbleController; -import com.android.systemui.bubbles.BubbleData; import com.android.systemui.bubbles.BubbleDataRepository; import com.android.systemui.bubbles.Bubbles; import com.android.systemui.dagger.SysUISingleton; @@ -59,7 +58,6 @@ public interface BubbleModule { NotificationShadeWindowController notificationShadeWindowController, StatusBarStateController statusBarStateController, ShadeController shadeController, - BubbleData data, ConfigurationController configurationController, NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, @@ -77,12 +75,11 @@ public interface BubbleModule { WindowManager windowManager, WindowManagerShellWrapper windowManagerShellWrapper, LauncherApps launcherApps) { - return new BubbleController( + return BubbleController.create( context, notificationShadeWindowController, statusBarStateController, shadeController, - data, null /* synchronizer */, configurationController, interruptionStateProvider, diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java index fa3328417bd6..1e239b1e9ec9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java @@ -17,30 +17,44 @@ package com.android.systemui.qs; import com.android.systemui.R; +import com.android.systemui.util.ViewController; import javax.inject.Inject; -public class QSContainerImplController { - private final QSContainerImpl mView; +class QSContainerImplController extends ViewController<QSContainerImpl> { private final QuickStatusBarHeaderController mQuickStatusBarHeaderController; private QSContainerImplController(QSContainerImpl view, QuickStatusBarHeaderController.Builder quickStatusBarHeaderControllerBuilder) { - mView = view; + super(view); mQuickStatusBarHeaderController = quickStatusBarHeaderControllerBuilder .setQuickStatusBarHeader(mView.findViewById(R.id.header)).build(); } + @Override + public void init() { + super.init(); + mQuickStatusBarHeaderController.init(); + } + public void setListening(boolean listening) { mQuickStatusBarHeaderController.setListening(listening); } - public static class Builder { + @Override + protected void onViewAttached() { + } + + @Override + protected void onViewDetached() { + } + + static class Builder { private final QuickStatusBarHeaderController.Builder mQuickStatusBarHeaderControllerBuilder; private QSContainerImpl mView; @Inject - public Builder( + Builder( QuickStatusBarHeaderController.Builder quickStatusBarHeaderControllerBuilder) { mQuickStatusBarHeaderControllerBuilder = quickStatusBarHeaderControllerBuilder; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index f1bb8996e181..3a783653a2d8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -142,7 +142,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mQSContainerImplController = mQSContainerImplControllerBuilder .setQSContainerImpl((QSContainerImpl) view) .build(); - + mQSContainerImplController.init(); mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter); mQSAnimator = new QSAnimator(this, mHeader.findViewById(R.id.quick_qs_panel), mQSPanel); @@ -367,14 +367,13 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca if (DEBUG) Log.d(TAG, "setListening " + listening); mListening = listening; mQSContainerImplController.setListening(listening); - mHeader.setListening(listening); mFooter.setListening(listening); mQSPanel.setListening(mListening, mQsExpanded); } @Override public void setHeaderListening(boolean listening) { - mHeader.setListening(listening); + mQSContainerImplController.setListening(listening); mFooter.setListening(listening); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 544249a51b08..a9fbc744b38e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -17,27 +17,16 @@ package com.android.systemui.qs; import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; - import android.annotation.ColorInt; -import android.app.AlarmManager; +import android.app.AlarmManager.AlarmClockInfo; import android.content.Context; -import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; import android.media.AudioManager; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.provider.AlarmClock; -import android.provider.Settings; -import android.service.notification.ZenModeConfig; -import android.text.format.DateUtils; import android.util.AttributeSet; -import android.util.Log; import android.util.MathUtils; import android.util.Pair; import android.view.ContextThemeWrapper; @@ -53,93 +42,48 @@ import android.widget.Space; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; -import com.android.internal.logging.UiEventLogger; import com.android.settingslib.Utils; import com.android.systemui.BatteryMeterView; import com.android.systemui.DualToneHandler; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.demomode.DemoMode; -import com.android.systemui.demomode.DemoModeController; -import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.privacy.OngoingPrivacyChip; -import com.android.systemui.privacy.PrivacyChipEvent; -import com.android.systemui.privacy.PrivacyItem; -import com.android.systemui.privacy.PrivacyItemController; import com.android.systemui.qs.QSDetail.Callback; -import com.android.systemui.qs.carrier.QSCarrierGroup; -import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager; import com.android.systemui.statusbar.phone.StatusBarWindowView; -import com.android.systemui.statusbar.phone.StatusIconContainer; import com.android.systemui.statusbar.policy.Clock; -import com.android.systemui.statusbar.policy.DateView; -import com.android.systemui.statusbar.policy.NextAlarmController; -import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.util.RingerModeTracker; -import java.util.ArrayList; -import java.util.List; import java.util.Locale; import java.util.Objects; -import javax.inject.Inject; -import javax.inject.Named; - /** * View that contains the top-most bits of the screen (primarily the status bar with date, time, and * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner * contents. */ -public class QuickStatusBarHeader extends RelativeLayout implements - View.OnClickListener, NextAlarmController.NextAlarmChangeCallback, - ZenModeController.Callback, LifecycleOwner { - private static final String TAG = "QuickStatusBarHeader"; - private static final boolean DEBUG = false; - - /** Delay for auto fading out the long press tooltip after it's fully visible (in ms). */ - private static final long AUTO_FADE_OUT_DELAY_MS = DateUtils.SECOND_IN_MILLIS * 6; - private static final int FADE_ANIMATION_DURATION_MS = 300; - private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0; - public static final int MAX_TOOLTIP_SHOWN_COUNT = 2; +public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwner { - private final NextAlarmController mAlarmController; - private final ZenModeController mZenController; - private final StatusBarIconController mStatusBarIconController; - private final ActivityStarter mActivityStarter; - - private QSPanel mQsPanel; + public static final int MAX_TOOLTIP_SHOWN_COUNT = 2; private boolean mExpanded; - private boolean mListening; private boolean mQsDisabled; - private QSCarrierGroup mCarrierGroup; protected QuickQSPanel mHeaderQsPanel; - protected QSTileHost mHost; - private TintedIconManager mIconManager; private TouchAnimator mStatusIconsAlphaAnimator; private TouchAnimator mHeaderTextContainerAlphaAnimator; private TouchAnimator mPrivacyChipAlphaAnimator; private DualToneHandler mDualToneHandler; - private final CommandQueue mCommandQueue; private View mSystemIconsView; private View mQuickQsStatusIcons; private View mHeaderTextContainerView; - private int mRingerMode = AudioManager.RINGER_MODE_NORMAL; - private AlarmManager.AlarmClockInfo mNextAlarm; - private ImageView mNextAlarmIcon; /** {@link TextView} containing the actual text indicating when the next alarm will go off. */ private TextView mNextAlarmTextView; @@ -149,23 +93,13 @@ public class QuickStatusBarHeader extends RelativeLayout implements private TextView mRingerModeTextView; private View mRingerContainer; private Clock mClockView; - private DateView mDateView; private OngoingPrivacyChip mPrivacyChip; private Space mSpace; private BatteryMeterView mBatteryRemainingIcon; - private RingerModeTracker mRingerModeTracker; - private DemoModeController mDemoModeController; - private DemoMode mDemoModeReceiver; - private UserTracker mUserTracker; - private boolean mAllIndicatorsEnabled; - private boolean mMicCameraIndicatorsEnabled; - - private PrivacyItemController mPrivacyItemController; - private final UiEventLogger mUiEventLogger; + // Used for RingerModeTracker private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); - private boolean mHasTopCutout = false; private int mStatusBarPaddingTop = 0; private int mRoundedCornerPadding = 0; private int mContentMarginStart; @@ -175,59 +109,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements private int mCutOutPaddingRight; private float mExpandedHeaderAlpha = 1.0f; private float mKeyguardExpansionFraction; - private boolean mPrivacyChipLogged = false; - - private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() { - @Override - public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) { - mPrivacyChip.setPrivacyList(privacyItems); - setChipVisibility(!privacyItems.isEmpty()); - } - - @Override - public void onFlagAllChanged(boolean flag) { - if (mAllIndicatorsEnabled != flag) { - mAllIndicatorsEnabled = flag; - update(); - } - } - @Override - public void onFlagMicCameraChanged(boolean flag) { - if (mMicCameraIndicatorsEnabled != flag) { - mMicCameraIndicatorsEnabled = flag; - update(); - } - } - - private void update() { - StatusIconContainer iconContainer = requireViewById(R.id.statusIcons); - iconContainer.setIgnoredSlots(getIgnoredIconSlots()); - setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); - } - }; - - @Inject - public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - NextAlarmController nextAlarmController, ZenModeController zenModeController, - StatusBarIconController statusBarIconController, - ActivityStarter activityStarter, PrivacyItemController privacyItemController, - CommandQueue commandQueue, RingerModeTracker ringerModeTracker, - UiEventLogger uiEventLogger, DemoModeController demoModeController, - UserTracker userTracker) { + public QuickStatusBarHeader(Context context, AttributeSet attrs) { super(context, attrs); - mAlarmController = nextAlarmController; - mZenController = zenModeController; - mStatusBarIconController = statusBarIconController; - mActivityStarter = activityStarter; - mPrivacyItemController = privacyItemController; mDualToneHandler = new DualToneHandler( new ContextThemeWrapper(context, R.style.QSHeaderTheme)); - mCommandQueue = commandQueue; - mRingerModeTracker = ringerModeTracker; - mUiEventLogger = uiEventLogger; - mDemoModeController = demoModeController; - mUserTracker = userTracker; } @Override @@ -237,11 +123,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements mHeaderQsPanel = findViewById(R.id.quick_qs_panel); mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons); mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons); - StatusIconContainer iconContainer = findViewById(R.id.statusIcons); - // Ignore privacy icons because they show in the space above QQS - iconContainer.addIgnoredSlots(getIgnoredIconSlots()); - iconContainer.setShouldRestrictIcons(false); - mIconManager = new TintedIconManager(iconContainer, mCommandQueue); // Views corresponding to the header info section (e.g. ringer and next alarm). mHeaderTextContainerView = findViewById(R.id.header_text_container); @@ -249,36 +130,18 @@ public class QuickStatusBarHeader extends RelativeLayout implements mNextAlarmIcon = findViewById(R.id.next_alarm_icon); mNextAlarmTextView = findViewById(R.id.next_alarm_text); mNextAlarmContainer = findViewById(R.id.alarm_container); - mNextAlarmContainer.setOnClickListener(this::onClick); mRingerModeIcon = findViewById(R.id.ringer_mode_icon); mRingerModeTextView = findViewById(R.id.ringer_mode_text); mRingerContainer = findViewById(R.id.ringer_container); - mRingerContainer.setOnClickListener(this::onClick); mPrivacyChip = findViewById(R.id.privacy_chip); - mPrivacyChip.setOnClickListener(this::onClick); - mCarrierGroup = findViewById(R.id.carrier_group); - updateResources(); Rect tintArea = new Rect(0, 0, 0, 0); - int colorForeground = Utils.getColorAttrDefaultColor(getContext(), - android.R.attr.colorForeground); - float intensity = getColorIntensity(colorForeground); - int fillColor = mDualToneHandler.getSingleColor(intensity); - // Set light text on the header icons because they will always be on a black background applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT); - // Set the correct tint for the status icons so they contrast - mIconManager.setTint(fillColor); - mNextAlarmIcon.setImageTintList(ColorStateList.valueOf(fillColor)); - mRingerModeIcon.setImageTintList(ColorStateList.valueOf(fillColor)); - mClockView = findViewById(R.id.clock); - mClockView.setOnClickListener(this); - mDemoModeReceiver = new ClockDemoModeReceiver(mClockView); - mDateView = findViewById(R.id.date); mSpace = findViewById(R.id.space); // Tint for the battery icons are handled in setupHost() @@ -290,33 +153,28 @@ public class QuickStatusBarHeader extends RelativeLayout implements mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE); mRingerModeTextView.setSelected(true); mNextAlarmTextView.setSelected(true); + } - mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); - mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); + void onAttach(TintedIconManager iconManager) { + int colorForeground = Utils.getColorAttrDefaultColor(getContext(), + android.R.attr.colorForeground); + float intensity = getColorIntensity(colorForeground); + int fillColor = mDualToneHandler.getSingleColor(intensity); + + // Set the correct tint for the status icons so they contrast + iconManager.setTint(fillColor); + mNextAlarmIcon.setImageTintList(ColorStateList.valueOf(fillColor)); + mRingerModeIcon.setImageTintList(ColorStateList.valueOf(fillColor)); } public QuickQSPanel getHeaderQsPanel() { return mHeaderQsPanel; } - private List<String> getIgnoredIconSlots() { - ArrayList<String> ignored = new ArrayList<>(); - if (getChipEnabled()) { - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_camera)); - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_microphone)); - if (mAllIndicatorsEnabled) { - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_location)); - } - } - - return ignored; - } - - private void updateStatusText() { - boolean changed = updateRingerStatus() || updateAlarmStatus(); + void updateStatusText(int ringerMode, AlarmClockInfo nextAlarm, boolean zenOverridingRinger, + boolean use24HourFormat) { + boolean changed = updateRingerStatus(ringerMode, zenOverridingRinger) + || updateAlarmStatus(nextAlarm, use24HourFormat); if (changed) { boolean alarmVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE; @@ -326,32 +184,17 @@ public class QuickStatusBarHeader extends RelativeLayout implements } } - private void setChipVisibility(boolean chipVisible) { - if (chipVisible && getChipEnabled()) { - mPrivacyChip.setVisibility(View.VISIBLE); - // Makes sure that the chip is logged as viewed at most once each time QS is opened - // mListening makes sure that the callback didn't return after the user closed QS - if (!mPrivacyChipLogged && mListening) { - mPrivacyChipLogged = true; - mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW); - } - } else { - mPrivacyChip.setVisibility(View.GONE); - } - } - - private boolean updateRingerStatus() { + private boolean updateRingerStatus(int ringerMode, boolean zenOverridingRinger) { boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE; CharSequence originalRingerText = mRingerModeTextView.getText(); boolean ringerVisible = false; - if (!ZenModeConfig.isZenOverridingRinger(mZenController.getZen(), - mZenController.getConsolidatedPolicy())) { - if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) { + if (!zenOverridingRinger) { + if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate); mRingerModeTextView.setText(R.string.qs_status_phone_vibrate); ringerVisible = true; - } else if (mRingerMode == AudioManager.RINGER_MODE_SILENT) { + } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) { mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_mute); mRingerModeTextView.setText(R.string.qs_status_phone_muted); ringerVisible = true; @@ -365,14 +208,14 @@ public class QuickStatusBarHeader extends RelativeLayout implements !Objects.equals(originalRingerText, mRingerModeTextView.getText()); } - private boolean updateAlarmStatus() { + private boolean updateAlarmStatus(AlarmClockInfo nextAlarm, boolean use24HourFormat) { boolean isOriginalVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE; CharSequence originalAlarmText = mNextAlarmTextView.getText(); boolean alarmVisible = false; - if (mNextAlarm != null) { + if (nextAlarm != null) { alarmVisible = true; - mNextAlarmTextView.setText(formatNextAlarm(mNextAlarm)); + mNextAlarmTextView.setText(formatNextAlarm(nextAlarm, use24HourFormat)); } mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE); mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE); @@ -419,7 +262,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements setMinimumHeight(sbHeight + qqsHeight); } - private void updateResources() { + void updateResources() { Resources resources = mContext.getResources(); updateMinimumHeight(); @@ -529,18 +372,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements } @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - mRingerModeTracker.getRingerModeInternal().observe(this, ringer -> { - mRingerMode = ringer; - updateStatusText(); - }); - mStatusBarIconController.addIconGroup(mIconManager); - mDemoModeController.addCallback(mDemoModeReceiver); - requestApplyInsets(); - } - - @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { // Handle padding of the clock DisplayCutout cutout = insets.getDisplayCutout(); @@ -563,17 +394,14 @@ public class QuickStatusBarHeader extends RelativeLayout implements if (cutout != null) { Rect topCutout = cutout.getBoundingRectTop(); if (topCutout.isEmpty() || cornerCutout) { - mHasTopCutout = false; lp.width = 0; mSpace.setVisibility(View.GONE); } else { - mHasTopCutout = true; lp.width = topCutout.width(); mSpace.setVisibility(View.VISIBLE); } } mSpace.setLayoutParams(lp); - setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE); mCutOutPaddingLeft = padding.first; mCutOutPaddingRight = padding.second; mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top; @@ -611,103 +439,14 @@ public class QuickStatusBarHeader extends RelativeLayout implements 0); } - @Override - @VisibleForTesting - public void onDetachedFromWindow() { - setListening(false); - mRingerModeTracker.getRingerModeInternal().removeObservers(this); - mStatusBarIconController.removeIconGroup(mIconManager); - mDemoModeController.removeCallback(mDemoModeReceiver); - super.onDetachedFromWindow(); - } - - public void setListening(boolean listening) { - if (listening == mListening) { - return; - } - mHeaderQsPanel.setListening(listening); - if (mHeaderQsPanel.switchTileLayout()) { - updateResources(); - } - mListening = listening; - - if (listening) { - mZenController.addCallback(this); - mAlarmController.addCallback(this); - mLifecycle.setCurrentState(Lifecycle.State.RESUMED); - // Get the most up to date info - mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); - mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); - mPrivacyItemController.addCallback(mPICCallback); - } else { - mZenController.removeCallback(this); - mAlarmController.removeCallback(this); - mLifecycle.setCurrentState(Lifecycle.State.CREATED); - mPrivacyItemController.removeCallback(mPICCallback); - mPrivacyChipLogged = false; - } - } - - @Override - public void onClick(View v) { - if (v == mClockView) { - mActivityStarter.postStartActivityDismissingKeyguard(new Intent( - AlarmClock.ACTION_SHOW_ALARMS), 0); - } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) { - if (mNextAlarm.getShowIntent() != null) { - mActivityStarter.postStartActivityDismissingKeyguard( - mNextAlarm.getShowIntent()); - } else { - Log.d(TAG, "No PendingIntent for next alarm. Using default intent"); - mActivityStarter.postStartActivityDismissingKeyguard(new Intent( - AlarmClock.ACTION_SHOW_ALARMS), 0); - } - } else if (v == mPrivacyChip) { - // If the privacy chip is visible, it means there were some indicators - Handler mUiHandler = new Handler(Looper.getMainLooper()); - mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK); - mUiHandler.post(() -> { - mActivityStarter.postStartActivityDismissingKeyguard( - new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0); - mHost.collapsePanels(); - }); - } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) { - mActivityStarter.postStartActivityDismissingKeyguard(new Intent( - Settings.ACTION_SOUND_SETTINGS), 0); - } - } - - @Override - public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) { - mNextAlarm = nextAlarm; - updateStatusText(); - } - - @Override - public void onZenChanged(int zen) { - updateStatusText(); - } - - @Override - public void onConfigChanged(ZenModeConfig config) { - updateStatusText(); - } - public void updateEverything() { post(() -> setClickable(!mExpanded)); } public void setQSPanel(final QSPanel qsPanel) { - mQsPanel = qsPanel; - setupHost(qsPanel.getHost()); - } - - public void setupHost(final QSTileHost host) { - mHost = host; //host.setHeaderView(mExpandIndicator); - mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this); - mHeaderQsPanel.setHost(host, null /* No customization in header */); - + mHeaderQsPanel.setQSPanelAndHeader(qsPanel, this); + mHeaderQsPanel.setHost(qsPanel.getHost(), null /* No customization in header */); Rect tintArea = new Rect(0, 0, 0, 0); int colorForeground = Utils.getColorAttrDefaultColor(getContext(), @@ -721,12 +460,11 @@ public class QuickStatusBarHeader extends RelativeLayout implements mHeaderQsPanel.setCallback(qsPanelCallback); } - private String formatNextAlarm(AlarmManager.AlarmClockInfo info) { + private String formatNextAlarm(AlarmClockInfo info, boolean use24HourFormat) { if (info == null) { return ""; } - String skeleton = android.text.format.DateFormat - .is24HourFormat(mContext, mUserTracker.getUserId()) ? "EHm" : "Ehma"; + String skeleton = use24HourFormat ? "EHm" : "Ehma"; String pattern = android.text.format.DateFormat .getBestDateTimePattern(Locale.getDefault(), skeleton); return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString(); @@ -777,37 +515,4 @@ public class QuickStatusBarHeader extends RelativeLayout implements updateHeaderTextContainerAlphaAnimator(); } } - - private boolean getChipEnabled() { - return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled; - } - - private static class ClockDemoModeReceiver implements DemoMode { - private Clock mClockView; - - @Override - public List<String> demoCommands() { - return List.of(COMMAND_CLOCK); - } - - ClockDemoModeReceiver(Clock clockView) { - mClockView = clockView; - } - - @Override - public void dispatchDemoCommand(String command, Bundle args) { - mClockView.dispatchDemoCommand(command, args); - } - - @Override - public void onDemoModeStarted() { - mClockView.onDemoModeStarted(); - } - - @Override - public void onDemoModeFinished() { - mClockView.onDemoModeFinished(); - } - } - } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index d899acbade4a..676a300b0ff2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -16,36 +16,393 @@ package com.android.systemui.qs; +import android.app.AlarmManager.AlarmClockInfo; +import android.content.Intent; +import android.media.AudioManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.provider.AlarmClock; +import android.provider.Settings; +import android.service.notification.ZenModeConfig; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; + +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + +import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; +import com.android.systemui.demomode.DemoMode; +import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.privacy.OngoingPrivacyChip; +import com.android.systemui.privacy.PrivacyChipEvent; +import com.android.systemui.privacy.PrivacyItem; +import com.android.systemui.privacy.PrivacyItemController; import com.android.systemui.qs.carrier.QSCarrierGroupController; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.StatusIconContainer; +import com.android.systemui.statusbar.policy.Clock; +import com.android.systemui.statusbar.policy.NextAlarmController; +import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.statusbar.policy.ZenModeController.Callback; +import com.android.systemui.util.RingerModeTracker; +import com.android.systemui.util.ViewController; + +import java.util.ArrayList; +import java.util.List; import javax.inject.Inject; -public class QuickStatusBarHeaderController { - private final QuickStatusBarHeader mView; +/** + * Controller for {@link QuickStatusBarHeader}. + */ +class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> { + private static final String TAG = "QuickStatusBarHeader"; + + private final ZenModeController mZenModeController; + private final NextAlarmController mNextAlarmController; + private final PrivacyItemController mPrivacyItemController; + private final RingerModeTracker mRingerModeTracker; + private final ActivityStarter mActivityStarter; + private final UiEventLogger mUiEventLogger; private final QSCarrierGroupController mQSCarrierGroupController; + private final QuickQSPanel mHeaderQsPanel; + private final LifecycleRegistry mLifecycle; + private final OngoingPrivacyChip mPrivacyChip; + private final Clock mClockView; + private final View mNextAlarmContainer; + private final View mRingerContainer; + private final QSTileHost mQSTileHost; + private final StatusBarIconController mStatusBarIconController; + private final CommandQueue mCommandQueue; + private final DemoModeController mDemoModeController; + private final UserTracker mUserTracker; + private final StatusIconContainer mIconContainer; + private final StatusBarIconController.TintedIconManager mIconManager; + private final DemoMode mDemoModeReceiver; + + private boolean mListening; + private AlarmClockInfo mNextAlarm; + private boolean mAllIndicatorsEnabled; + private boolean mMicCameraIndicatorsEnabled; + private boolean mPrivacyChipLogged; + private int mRingerMode = AudioManager.RINGER_MODE_NORMAL; + + private final ZenModeController.Callback mZenModeControllerCallback = new Callback() { + @Override + public void onZenChanged(int zen) { + mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(), + use24HourFormat()); + } + + @Override + public void onConfigChanged(ZenModeConfig config) { + mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(), + use24HourFormat()); + } + }; + + private boolean use24HourFormat() { + return android.text.format.DateFormat.is24HourFormat( + mView.getContext(), mUserTracker.getUserId()); + + } + + private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() { + @Override + public void onNextAlarmChanged(AlarmClockInfo nextAlarm) { + mNextAlarm = nextAlarm; + mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(), + use24HourFormat()); + } + }; + + private final LifecycleOwner mLifecycleOwner = new LifecycleOwner() { + @NonNull + @Override + public Lifecycle getLifecycle() { + return mLifecycle; + } + }; + + private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() { + @Override + public void onPrivacyItemsChanged(@NonNull List<PrivacyItem> privacyItems) { + mPrivacyChip.setPrivacyList(privacyItems); + setChipVisibility(!privacyItems.isEmpty()); + } + + @Override + public void onFlagAllChanged(boolean flag) { + if (mAllIndicatorsEnabled != flag) { + mAllIndicatorsEnabled = flag; + update(); + } + } + + @Override + public void onFlagMicCameraChanged(boolean flag) { + if (mMicCameraIndicatorsEnabled != flag) { + mMicCameraIndicatorsEnabled = flag; + update(); + } + } + + private void update() { + StatusIconContainer iconContainer = mView.requireViewById(R.id.statusIcons); + iconContainer.setIgnoredSlots(getIgnoredIconSlots()); + setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); + } + }; + + private View.OnClickListener mOnClickListener = new OnClickListener() { + @Override + public void onClick(View v) { + if (v == mClockView) { + mActivityStarter.postStartActivityDismissingKeyguard(new Intent( + AlarmClock.ACTION_SHOW_ALARMS), 0); + } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) { + if (mNextAlarm.getShowIntent() != null) { + mActivityStarter.postStartActivityDismissingKeyguard( + mNextAlarm.getShowIntent()); + } else { + Log.d(TAG, "No PendingIntent for next alarm. Using default intent"); + mActivityStarter.postStartActivityDismissingKeyguard(new Intent( + AlarmClock.ACTION_SHOW_ALARMS), 0); + } + } else if (v == mPrivacyChip) { + // If the privacy chip is visible, it means there were some indicators + Handler mUiHandler = new Handler(Looper.getMainLooper()); + mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK); + mUiHandler.post(() -> { + mActivityStarter.postStartActivityDismissingKeyguard( + new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0); + mQSTileHost.collapsePanels(); + }); + } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) { + mActivityStarter.postStartActivityDismissingKeyguard(new Intent( + Settings.ACTION_SOUND_SETTINGS), 0); + } + } + }; private QuickStatusBarHeaderController(QuickStatusBarHeader view, + ZenModeController zenModeController, NextAlarmController nextAlarmController, + PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker, + ActivityStarter activityStarter, UiEventLogger uiEventLogger, + QSTileHost qsTileHost, StatusBarIconController statusBarIconController, + CommandQueue commandQueue, DemoModeController demoModeController, + UserTracker userTracker, QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) { - mView = view; + super(view); + mZenModeController = zenModeController; + mNextAlarmController = nextAlarmController; + mPrivacyItemController = privacyItemController; + mRingerModeTracker = ringerModeTracker; + mActivityStarter = activityStarter; + mUiEventLogger = uiEventLogger; + mQSTileHost = qsTileHost; + mStatusBarIconController = statusBarIconController; + mCommandQueue = commandQueue; + mDemoModeController = demoModeController; + mUserTracker = userTracker; + mLifecycle = new LifecycleRegistry(mLifecycleOwner); + mQSCarrierGroupController = qsCarrierGroupControllerBuilder .setQSCarrierGroup(mView.findViewById(R.id.carrier_group)) .build(); + + + mPrivacyChip = mView.findViewById(R.id.privacy_chip); + mHeaderQsPanel = mView.findViewById(R.id.quick_qs_panel); + mNextAlarmContainer = mView.findViewById(R.id.alarm_container); + mClockView = mView.findViewById(R.id.clock); + mRingerContainer = mView.findViewById(R.id.ringer_container); + mIconContainer = mView.findViewById(R.id.statusIcons); + + mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, mCommandQueue); + mDemoModeReceiver = new ClockDemoModeReceiver(mClockView); + } + + @Override + protected void onViewAttached() { + mRingerModeTracker.getRingerModeInternal().observe(mLifecycleOwner, ringer -> { + mRingerMode = ringer; + mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(), + use24HourFormat()); + }); + + mClockView.setOnClickListener(mOnClickListener); + mNextAlarmContainer.setOnClickListener(mOnClickListener); + mRingerContainer.setOnClickListener(mOnClickListener); + mPrivacyChip.setOnClickListener(mOnClickListener); + + // Ignore privacy icons because they show in the space above QQS + mIconContainer.addIgnoredSlots(getIgnoredIconSlots()); + mIconContainer.setShouldRestrictIcons(false); + mStatusBarIconController.addIconGroup(mIconManager); + + mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); + mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); + + setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE); + + mView.onAttach(mIconManager); + + mDemoModeController.addCallback(mDemoModeReceiver); + } + + @Override + protected void onViewDetached() { + mRingerModeTracker.getRingerModeInternal().removeObservers(mLifecycleOwner); + mClockView.setOnClickListener(null); + mNextAlarmContainer.setOnClickListener(null); + mRingerContainer.setOnClickListener(null); + mPrivacyChip.setOnClickListener(null); + mStatusBarIconController.removeIconGroup(mIconManager); + mDemoModeController.removeCallback(mDemoModeReceiver); + setListening(false); } public void setListening(boolean listening) { mQSCarrierGroupController.setListening(listening); - // TODO: move mView.setListening logic into here. - mView.setListening(listening); + + if (listening == mListening) { + return; + } + mListening = listening; + + mHeaderQsPanel.setListening(listening); + if (mHeaderQsPanel.switchTileLayout()) { + mView.updateResources(); + } + + if (listening) { + mZenModeController.addCallback(mZenModeControllerCallback); + mNextAlarmController.addCallback(mNextAlarmChangeCallback); + mLifecycle.setCurrentState(Lifecycle.State.RESUMED); + // Get the most up to date info + mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); + mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); + mPrivacyItemController.addCallback(mPICCallback); + } else { + mZenModeController.removeCallback(mZenModeControllerCallback); + mNextAlarmController.removeCallback(mNextAlarmChangeCallback); + mLifecycle.setCurrentState(Lifecycle.State.CREATED); + mPrivacyItemController.removeCallback(mPICCallback); + mPrivacyChipLogged = false; + } } + private void setChipVisibility(boolean chipVisible) { + if (chipVisible && getChipEnabled()) { + mPrivacyChip.setVisibility(View.VISIBLE); + // Makes sure that the chip is logged as viewed at most once each time QS is opened + // mListening makes sure that the callback didn't return after the user closed QS + if (!mPrivacyChipLogged && mListening) { + mPrivacyChipLogged = true; + mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW); + } + } else { + mPrivacyChip.setVisibility(View.GONE); + } + } - public static class Builder { + private List<String> getIgnoredIconSlots() { + ArrayList<String> ignored = new ArrayList<>(); + if (getChipEnabled()) { + ignored.add(mView.getResources().getString( + com.android.internal.R.string.status_bar_camera)); + ignored.add(mView.getResources().getString( + com.android.internal.R.string.status_bar_microphone)); + if (mAllIndicatorsEnabled) { + ignored.add(mView.getResources().getString( + com.android.internal.R.string.status_bar_location)); + } + } + + return ignored; + } + + private boolean getChipEnabled() { + return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled; + } + + private boolean isZenOverridingRinger() { + return ZenModeConfig.isZenOverridingRinger(mZenModeController.getZen(), + mZenModeController.getConsolidatedPolicy()); + } + + + private static class ClockDemoModeReceiver implements DemoMode { + private Clock mClockView; + + @Override + public List<String> demoCommands() { + return List.of(COMMAND_CLOCK); + } + + ClockDemoModeReceiver(Clock clockView) { + mClockView = clockView; + } + + @Override + public void dispatchDemoCommand(String command, Bundle args) { + mClockView.dispatchDemoCommand(command, args); + } + + @Override + public void onDemoModeStarted() { + mClockView.onDemoModeStarted(); + } + + @Override + public void onDemoModeFinished() { + mClockView.onDemoModeFinished(); + } + } + + static class Builder { + private final ZenModeController mZenModeController; + private final NextAlarmController mNextAlarmController; + private final PrivacyItemController mPrivacyItemController; + private final RingerModeTracker mRingerModeTracker; + private final ActivityStarter mActivityStarter; + private final UiEventLogger mUiEventLogger; + private final QSTileHost mQsTileHost; + private final StatusBarIconController mStatusBarIconController; + private final CommandQueue mCommandQueue; + private final DemoModeController mDemoModeController; + private final UserTracker mUserTracker; private final QSCarrierGroupController.Builder mQSCarrierGroupControllerBuilder; private QuickStatusBarHeader mView; @Inject - public Builder(QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) { + Builder(ZenModeController zenModeController, NextAlarmController nextAlarmController, + PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker, + ActivityStarter activityStarter, UiEventLogger uiEventLogger, QSTileHost qsTileHost, + StatusBarIconController statusBarIconController, CommandQueue commandQueue, + DemoModeController demoModeController, UserTracker userTracker, + QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) { + mZenModeController = zenModeController; + mNextAlarmController = nextAlarmController; + mPrivacyItemController = privacyItemController; + mRingerModeTracker = ringerModeTracker; + mActivityStarter = activityStarter; + mUiEventLogger = uiEventLogger; + mQsTileHost = qsTileHost; + mStatusBarIconController = statusBarIconController; + mCommandQueue = commandQueue; + mDemoModeController = demoModeController; + mUserTracker = userTracker; mQSCarrierGroupControllerBuilder = qsCarrierGroupControllerBuilder; } @@ -54,8 +411,13 @@ public class QuickStatusBarHeaderController { return this; } - public QuickStatusBarHeaderController build() { - return new QuickStatusBarHeaderController(mView, mQSCarrierGroupControllerBuilder); + + QuickStatusBarHeaderController build() { + return new QuickStatusBarHeaderController(mView, mZenModeController, + mNextAlarmController, mPrivacyItemController, mRingerModeTracker, + mActivityStarter, mUiEventLogger, mQsTileHost, mStatusBarIconController, + mCommandQueue, mDemoModeController, mUserTracker, + mQSCarrierGroupControllerBuilder); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 0ae1170a25fc..3b0415b31eb5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -35,12 +35,14 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_T import android.annotation.FloatRange; import android.app.ActivityTaskManager; +import android.app.PictureInPictureParams; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; @@ -503,6 +505,38 @@ public class OverviewProxyService extends CurrentUserTracker implements } } + @Override + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, + int launcherRotation, int shelfHeight) { + if (!verifyCaller("startSwipePipToHome") || !mHasPipFeature) { + return null; + } + long binderToken = Binder.clearCallingIdentity(); + try { + return mPipOptional.map(pip -> + pip.startSwipePipToHome(componentName, activityInfo, + pictureInPictureParams, launcherRotation, shelfHeight)) + .orElse(null); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + + @Override + public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + if (!verifyCaller("stopSwipePipToHome") || !mHasPipFeature) { + return; + } + long binderToken = Binder.clearCallingIdentity(); + try { + mPipOptional.ifPresent(pip -> pip.stopSwipePipToHome( + componentName, destinationBounds)); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java index df03c3e08f08..0aa9d4d662a5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java @@ -48,6 +48,7 @@ public class ScreenInternalAudioRecorder { private long mTotalBytes; private MediaMuxer mMuxer; private boolean mMic; + private boolean mStarted; private int mTrackId = -1; @@ -263,10 +264,14 @@ public class ScreenInternalAudioRecorder { * start recording * @throws IllegalStateException if recording fails to initialize */ - public void start() throws IllegalStateException { - if (mThread != null) { - Log.e(TAG, "a recording is being done in parallel or stop is not called"); + public synchronized void start() throws IllegalStateException { + if (mStarted) { + if (mThread == null) { + throw new IllegalStateException("Recording stopped and can't restart (single use)"); + } + throw new IllegalStateException("Recording already started"); } + mStarted = true; mAudioRecord.startRecording(); if (mMic) mAudioRecordMic.startRecording(); Log.d(TAG, "channel count " + mAudioRecord.getChannelCount()); diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index a6cd350b33ce..344f0d2f5506 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -27,7 +27,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.qs.QSFooterImpl; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QuickQSPanel; -import com.android.systemui.qs.QuickStatusBarHeader; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; @@ -93,10 +92,6 @@ public class InjectionInflationController { } /** - * Creates the QuickStatusBarHeader. - */ - QuickStatusBarHeader createQsHeader(); - /** * Creates the QSFooterImpl. */ QSFooterImpl createQsFooter(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java index 1e969c226ff1..fa78d1cc0191 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java @@ -36,6 +36,7 @@ import android.view.accessibility.IWindowMagnificationConnectionCallback; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.CommandQueue; import org.junit.Before; @@ -65,6 +66,8 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { @Mock private ModeSwitchesController mModeSwitchesController; @Mock + private NavigationModeController mNavigationModeController; + @Mock private IRemoteMagnificationAnimationCallback mAnimationCallback; private IWindowMagnificationConnection mIWindowMagnificationConnection; private WindowMagnification mWindowMagnification; @@ -79,7 +82,8 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { }).when(mAccessibilityManager).setWindowMagnificationConnection( any(IWindowMagnificationConnection.class)); mWindowMagnification = new WindowMagnification(getContext(), - getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController); + getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController, + mNavigationModeController); mWindowMagnification.mWindowMagnificationAnimationController = mWindowMagnificationAnimationController; mWindowMagnification.requestWindowMagnificationConnection(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index c6440f4290fd..5f2fd697b86d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.accessibility; import static android.view.Choreographer.FrameCallback; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import static org.hamcrest.Matchers.containsString; @@ -28,6 +29,7 @@ import static org.junit.Assert.assertTrue; 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.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; @@ -294,4 +296,15 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { assertTrue( mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null)); } + + @Test + public void onNavigationModeChanged_updateMirrorViewLayout() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN); + mWindowMagnificationController.onNavigationModeChanged(NAV_BAR_MODE_GESTURAL); + }); + + verify(mWindowManager).updateViewLayout(eq(mMirrorView), any()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java index 936558bca2d2..4a0e216ae87e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java @@ -35,6 +35,7 @@ import android.view.accessibility.IWindowMagnificationConnectionCallback; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.CommandQueue; import org.junit.Before; @@ -53,6 +54,8 @@ public class WindowMagnificationTest extends SysuiTestCase { private AccessibilityManager mAccessibilityManager; @Mock private ModeSwitchesController mModeSwitchesController; + @Mock + private NavigationModeController mNavigationModeController; private CommandQueue mCommandQueue; private WindowMagnification mWindowMagnification; @@ -63,7 +66,8 @@ public class WindowMagnificationTest extends SysuiTestCase { mCommandQueue = new CommandQueue(getContext()); mWindowMagnification = new WindowMagnification(getContext(), - getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController); + getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController, + mNavigationModeController); mWindowMagnification.start(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java index 4bbc41e517b0..6e2c7e5c19ef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java @@ -16,8 +16,6 @@ package com.android.systemui.bubbles; -import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; - import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; @@ -29,7 +27,9 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.PendingIntent; import android.graphics.drawable.Icon; +import android.os.Bundle; import android.os.UserHandle; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -39,10 +39,6 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.bubbles.BubbleData.TimeSource; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.google.common.collect.ImmutableList; @@ -68,15 +64,15 @@ import org.mockito.MockitoAnnotations; @TestableLooper.RunWithLooper(setAsMainLooper = true) public class BubbleDataTest extends SysuiTestCase { - private NotificationEntry mEntryA1; - private NotificationEntry mEntryA2; - private NotificationEntry mEntryA3; - private NotificationEntry mEntryB1; - private NotificationEntry mEntryB2; - private NotificationEntry mEntryB3; - private NotificationEntry mEntryC1; - private NotificationEntry mEntryInterruptive; - private NotificationEntry mEntryDismissed; + private BubbleEntry mEntryA1; + private BubbleEntry mEntryA2; + private BubbleEntry mEntryA3; + private BubbleEntry mEntryB1; + private BubbleEntry mEntryB2; + private BubbleEntry mEntryB3; + private BubbleEntry mEntryC1; + private BubbleEntry mEntryInterruptive; + private BubbleEntry mEntryDismissed; private Bubble mBubbleA1; private Bubble mBubbleA2; @@ -99,8 +95,6 @@ public class BubbleDataTest extends SysuiTestCase { @Mock private PendingIntent mDeleteIntent; - private NotificationTestHelper mNotificationTestHelper; - @Captor private ArgumentCaptor<BubbleData.Update> mUpdateCaptor; @@ -112,29 +106,23 @@ public class BubbleDataTest extends SysuiTestCase { @Before public void setUp() throws Exception { - mNotificationTestHelper = new NotificationTestHelper( - mContext, - mDependency, - TestableLooper.get(this)); MockitoAnnotations.initMocks(this); - mEntryA1 = createBubbleEntry(1, "a1", "package.a"); - mEntryA2 = createBubbleEntry(1, "a2", "package.a"); - mEntryA3 = createBubbleEntry(1, "a3", "package.a"); - mEntryB1 = createBubbleEntry(1, "b1", "package.b"); - mEntryB2 = createBubbleEntry(1, "b2", "package.b"); - mEntryB3 = createBubbleEntry(1, "b3", "package.b"); - mEntryC1 = createBubbleEntry(1, "c1", "package.c"); - - mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d"); - modifyRanking(mEntryInterruptive) - .setVisuallyInterruptive(true) - .build(); + mEntryA1 = createBubbleEntry(1, "a1", "package.a", null); + mEntryA2 = createBubbleEntry(1, "a2", "package.a", null); + mEntryA3 = createBubbleEntry(1, "a3", "package.a", null); + mEntryB1 = createBubbleEntry(1, "b1", "package.b", null); + mEntryB2 = createBubbleEntry(1, "b2", "package.b", null); + mEntryB3 = createBubbleEntry(1, "b3", "package.b", null); + mEntryC1 = createBubbleEntry(1, "c1", "package.c", null); + + NotificationListenerService.Ranking ranking = + mock(NotificationListenerService.Ranking.class); + when(ranking.visuallyInterruptive()).thenReturn(true); + mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d", ranking); mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null); - ExpandableNotificationRow row = mNotificationTestHelper.createBubble(); - mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d"); - mEntryDismissed.setRow(row); + mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d", null); mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null); mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener); @@ -862,12 +850,13 @@ public class BubbleDataTest extends SysuiTestCase { } - private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) { - return createBubbleEntry(userId, notifKey, packageName, 1000); + private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName, + NotificationListenerService.Ranking ranking) { + return createBubbleEntry(userId, notifKey, packageName, ranking, 1000); } - private void setPostTime(NotificationEntry entry, long postTime) { - when(entry.getSbn().getPostTime()).thenReturn(postTime); + private void setPostTime(BubbleEntry entry, long postTime) { + when(entry.getStatusBarNotification().getPostTime()).thenReturn(postTime); } /** @@ -875,16 +864,19 @@ public class BubbleDataTest extends SysuiTestCase { * required for BubbleData functionality and verification. NotificationTestHelper is used only * as a convenience to create a Notification w/BubbleMetadata. */ - private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName, - long postTime) { + private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName, + NotificationListenerService.Ranking ranking, long postTime) { // BubbleMetadata Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder( mExpandIntent, Icon.createWithResource("", 0)) .setDeleteIntent(mDeleteIntent) .build(); // Notification -> BubbleMetadata - Notification notification = mNotificationTestHelper.createNotification(false, - null /* groupKey */, bubbleMetadata); + Notification notification = mock(Notification.class); + notification.setBubbleMetadata(bubbleMetadata); + + // Notification -> extras + notification.extras = new Bundle(); // StatusBarNotification StatusBarNotification sbn = mock(StatusBarNotification.class); @@ -895,18 +887,18 @@ public class BubbleDataTest extends SysuiTestCase { when(sbn.getNotification()).thenReturn(notification); // NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata - return new NotificationEntryBuilder().setSbn(sbn).build(); + return new BubbleEntry(sbn, ranking, true, false, false, false); } private void setCurrentTime(long time) { when(mTimeSource.currentTimeMillis()).thenReturn(time); } - private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) { + private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime) { sendUpdatedEntryAtTime(entry, postTime, true /* visuallyInterruptive */); } - private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime, + private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, boolean visuallyInterruptive) { setPostTime(entry, postTime); // BubbleController calls this: diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java index f7f3a377f31d..29ead593c51a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java @@ -24,12 +24,14 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.Notification; import android.app.PendingIntent; import android.content.Intent; import android.graphics.drawable.Icon; import android.os.Bundle; +import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -37,8 +39,6 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import org.junit.Before; import org.junit.Test; @@ -52,8 +52,10 @@ import org.mockito.MockitoAnnotations; public class BubbleTest extends SysuiTestCase { @Mock private Notification mNotif; + @Mock + private StatusBarNotification mSbn; - private NotificationEntry mEntry; + private BubbleEntry mBubbleEntry; private Bundle mExtras; private Bubble mBubble; @@ -67,18 +69,16 @@ public class BubbleTest extends SysuiTestCase { mExtras = new Bundle(); mNotif.extras = mExtras; - mEntry = new NotificationEntryBuilder() - .setNotification(mNotif) - .build(); - - mBubble = new Bubble(mEntry, mSuppressionListener, null); - Intent target = new Intent(mContext, BubblesTestActivity.class); Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder( PendingIntent.getActivity(mContext, 0, target, 0), Icon.createWithResource(mContext, R.drawable.android)) .build(); - mEntry.setBubbleMetadata(metadata); + when(mSbn.getNotification()).thenReturn(mNotif); + when(mNotif.getBubbleMetadata()).thenReturn(metadata); + when(mSbn.getKey()).thenReturn("mock"); + mBubbleEntry = new BubbleEntry(mSbn, null, true, false, false, false); + mBubble = new Bubble(mBubbleEntry, mSuppressionListener, null); } @Test @@ -86,7 +86,7 @@ public class BubbleTest extends SysuiTestCase { final String msg = "Hello there!"; doReturn(Notification.Style.class).when(mNotif).getNotificationStyle(); mExtras.putCharSequence(Notification.EXTRA_TEXT, msg); - assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message); + assertEquals(msg, Bubble.extractFlyoutMessage(mBubbleEntry).message); } @Test @@ -97,7 +97,7 @@ public class BubbleTest extends SysuiTestCase { mExtras.putCharSequence(Notification.EXTRA_BIG_TEXT, msg); // Should be big text, not the small text. - assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message); + assertEquals(msg, Bubble.extractFlyoutMessage(mBubbleEntry).message); } @Test @@ -105,7 +105,7 @@ public class BubbleTest extends SysuiTestCase { doReturn(Notification.MediaStyle.class).when(mNotif).getNotificationStyle(); // Media notifs don't get update messages. - assertNull(BubbleViewInfoTask.extractFlyoutMessage(mEntry).message); + assertNull(Bubble.extractFlyoutMessage(mBubbleEntry).message); } @Test @@ -121,7 +121,7 @@ public class BubbleTest extends SysuiTestCase { // Should be the last one only. assertEquals("Really? I prefer them that way.", - BubbleViewInfoTask.extractFlyoutMessage(mEntry).message); + Bubble.extractFlyoutMessage(mBubbleEntry).message); } @Test @@ -136,8 +136,8 @@ public class BubbleTest extends SysuiTestCase { "Oh, hello!", 0, "Mady").toBundle()}); // Should be the last one only. - assertEquals("Oh, hello!", BubbleViewInfoTask.extractFlyoutMessage(mEntry).message); - assertEquals("Mady", BubbleViewInfoTask.extractFlyoutMessage(mEntry).senderName); + assertEquals("Oh, hello!", Bubble.extractFlyoutMessage(mBubbleEntry).message); + assertEquals("Mady", Bubble.extractFlyoutMessage(mBubbleEntry).senderName); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java index ca328fbe44fb..9ebb587d3d85 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java @@ -36,6 +36,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.phone.ShadeController; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -73,6 +74,11 @@ public class MediaOutputDialogTest extends SysuiTestCase { when(mMediaDevice.getFeatures()).thenReturn(mFeatures); } + @After + public void tearDown() { + mMediaOutputDialog.dismissDialog(); + } + @Test public void getStopButtonVisibility_remoteDevice_returnVisible() { mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 1c2d08402e69..e472cb22ed6b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -37,7 +37,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.keyguard.CarrierText; import com.android.systemui.Dependency; -import com.android.systemui.R; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -116,11 +115,6 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { qs.setListening(false); processAllMessages(); - - // Manually push header through detach so it can handle standard cleanup it does on - // removed from window. - ((QuickStatusBarHeader) qs.getView().findViewById(R.id.header)).onDetachedFromWindow(); - host.destroy(); processAllMessages(); } diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp index 915c2f6087b8..613d28b20b8e 100644 --- a/packages/Tethering/Android.bp +++ b/packages/Tethering/Android.bp @@ -102,6 +102,7 @@ java_defaults { ], libs: [ "framework-tethering", + "framework-wifi", ], jarjar_rules: "jarjar-rules.txt", optimize: { diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java index fd9e36080c80..b285849f9edf 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java @@ -15,6 +15,7 @@ */ package com.android.networkstack.tethering; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.TetheringManager.TETHERING_WIFI_P2P; import static java.util.Arrays.asList; @@ -23,7 +24,6 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.IpPrefix; import android.net.LinkAddress; -import android.net.LinkProperties; import android.net.Network; import android.net.ip.IpServer; import android.net.util.PrefixUtils; @@ -90,16 +90,24 @@ public class PrivateAddressCoordinator { /** * Record a new upstream IpPrefix which may conflict with tethering downstreams. - * The downstreams will be notified if a conflict is found. + * The downstreams will be notified if a conflict is found. When updateUpstreamPrefix is called, + * UpstreamNetworkState must have an already populated LinkProperties. */ - public void updateUpstreamPrefix(final Network network, final LinkProperties lp) { - final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(lp.getAllLinkAddresses()); + public void updateUpstreamPrefix(final UpstreamNetworkState ns) { + // Do not support VPN as upstream + if (ns.networkCapabilities != null && ns.networkCapabilities.hasTransport(TRANSPORT_VPN)) { + removeUpstreamPrefix(ns.network); + return; + } + + final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes( + ns.linkProperties.getAllLinkAddresses()); if (ipv4Prefixes.isEmpty()) { - removeUpstreamPrefix(network); + removeUpstreamPrefix(ns.network); return; } - mUpstreamPrefixMap.put(network, ipv4Prefixes); + mUpstreamPrefixMap.put(ns.network, ipv4Prefixes); handleMaybePrefixConflict(ipv4Prefixes); } diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java index 64d5025807e7..474f4e8b603a 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -1678,14 +1678,6 @@ public class Tethering { } } - private void addUpstreamPrefixes(final UpstreamNetworkState ns) { - mPrivateAddressCoordinator.updateUpstreamPrefix(ns.network, ns.linkProperties); - } - - private void removeUpstreamPrefixes(final UpstreamNetworkState ns) { - mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network); - } - @VisibleForTesting void handleUpstreamNetworkMonitorCallback(int arg1, Object o) { if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) { @@ -1696,10 +1688,10 @@ public class Tethering { final UpstreamNetworkState ns = (UpstreamNetworkState) o; switch (arg1) { case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: - addUpstreamPrefixes(ns); + mPrivateAddressCoordinator.updateUpstreamPrefix(ns); break; case UpstreamNetworkMonitor.EVENT_ON_LOST: - removeUpstreamPrefixes(ns); + mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network); break; } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java index 8e93c2e447b3..7b6632c36f24 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java @@ -15,6 +15,10 @@ */ package com.android.networkstack.tethering; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; @@ -30,13 +34,12 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.net.ConnectivityManager; -import android.net.InetAddresses; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkCapabilities; import android.net.ip.IpServer; -import android.net.util.NetworkConstants; import android.net.util.PrefixUtils; import androidx.test.filters.SmallTest; @@ -48,13 +51,10 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - @RunWith(AndroidJUnit4.class) @SmallTest public final class PrivateAddressCoordinatorTest { - private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0"; - private static final String TEST_WIFI_IFNAME = "test_wlan0"; + private static final String TEST_IFNAME = "test0"; @Mock private IpServer mHotspotIpServer; @Mock private IpServer mUsbIpServer; @@ -69,7 +69,8 @@ public final class PrivateAddressCoordinatorTest { private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24"); private final Network mWifiNetwork = new Network(1); private final Network mMobileNetwork = new Network(2); - private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork}; + private final Network mVpnNetwork = new Network(3); + private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork}; private void setUpIpServers() throws Exception { when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB); @@ -184,33 +185,25 @@ public final class PrivateAddressCoordinatorTest { assertEquals("Fail to reselect available prefix: ", predefinedPrefix, allowUseFreePrefix); } - private LinkProperties buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6, - boolean isMobile) { - final String testIface; - final String testIpv4Address; - if (isMobile) { - testIface = TEST_MOBILE_IFNAME; - testIpv4Address = "10.0.0.1"; - } else { - testIface = TEST_WIFI_IFNAME; - testIpv4Address = "192.168.43.5"; - } - + private UpstreamNetworkState buildUpstreamNetworkState(final Network network, + final LinkAddress v4Addr, final LinkAddress v6Addr, final NetworkCapabilities cap) { final LinkProperties prop = new LinkProperties(); - prop.setInterfaceName(testIface); + prop.setInterfaceName(TEST_IFNAME); + if (v4Addr != null) prop.addLinkAddress(v4Addr); - if (withIPv4) { - prop.addLinkAddress( - new LinkAddress(InetAddresses.parseNumericAddress(testIpv4Address), - NetworkConstants.IPV4_ADDR_BITS)); - } + if (v6Addr != null) prop.addLinkAddress(v6Addr); + + return new UpstreamNetworkState(prop, cap, network); + } - if (withIPv6) { - prop.addLinkAddress( - new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"), - NetworkConstants.RFC7421_PREFIX_LENGTH)); + private NetworkCapabilities makeNetworkCapabilities(final int transportType) { + final NetworkCapabilities cap = new NetworkCapabilities(); + cap.addTransportType(transportType); + if (transportType == TRANSPORT_VPN) { + cap.removeCapability(NET_CAPABILITY_NOT_VPN); } - return prop; + + return cap; } @Test @@ -220,53 +213,76 @@ public final class PrivateAddressCoordinatorTest { final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24"); // Force always get subAddress "43.5" for conflict testing. when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr); - // 1. Enable hotspot with prefix 192.168.43.0/24 + // - Enable hotspot with prefix 192.168.43.0/24 final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress( mHotspotIpServer); final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(hotspotAddr); assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix); when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr); - // 2. Update v6 only mobile network, hotspot prefix should not be removed. - List<String> testConflicts; - final LinkProperties v6OnlyMobileProp = buildUpstreamLinkProperties(false, true, true); - mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v6OnlyMobileProp); + // - test mobile network with null NetworkCapabilities. Ideally this should not happen, + // just make sure no crash in this case. + final UpstreamNetworkState noCapUpstream = buildUpstreamNetworkState(mMobileNetwork, + new LinkAddress("10.0.0.8/24"), null, null); + mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream); + verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + // - test mobile upstream with no address. + final UpstreamNetworkState noAddress = buildUpstreamNetworkState(mMobileNetwork, + null, null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); + mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream); + verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + // - Update v6 only mobile network, hotspot prefix should not be removed. + final UpstreamNetworkState v6OnlyMobile = buildUpstreamNetworkState(mMobileNetwork, + null, new LinkAddress("2001:db8::/64"), + makeNetworkCapabilities(TRANSPORT_CELLULAR)); + mPrivateAddressCoordinator.updateUpstreamPrefix(v6OnlyMobile); verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork); - // 3. Update v4 only mobile network, hotspot prefix should not be removed. - final LinkProperties v4OnlyMobileProp = buildUpstreamLinkProperties(true, false, true); - mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4OnlyMobileProp); + // - Update v4 only mobile network, hotspot prefix should not be removed. + final UpstreamNetworkState v4OnlyMobile = buildUpstreamNetworkState(mMobileNetwork, + new LinkAddress("10.0.0.8/24"), null, + makeNetworkCapabilities(TRANSPORT_CELLULAR)); + mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyMobile); verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // 4. Update v4v6 mobile network, hotspot prefix should not be removed. - final LinkProperties v4v6MobileProp = buildUpstreamLinkProperties(true, true, true); - mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4v6MobileProp); + // - Update v4v6 mobile network, hotspot prefix should not be removed. + final UpstreamNetworkState v4v6Mobile = buildUpstreamNetworkState(mMobileNetwork, + new LinkAddress("10.0.0.8/24"), new LinkAddress("2001:db8::/64"), + makeNetworkCapabilities(TRANSPORT_CELLULAR)); + mPrivateAddressCoordinator.updateUpstreamPrefix(v4v6Mobile); verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // 5. Update v6 only wifi network, hotspot prefix should not be removed. - final LinkProperties v6OnlyWifiProp = buildUpstreamLinkProperties(false, true, false); - mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v6OnlyWifiProp); + // - Update v6 only wifi network, hotspot prefix should not be removed. + final UpstreamNetworkState v6OnlyWifi = buildUpstreamNetworkState(mWifiNetwork, + null, new LinkAddress("2001:db8::/64"), makeNetworkCapabilities(TRANSPORT_WIFI)); + mPrivateAddressCoordinator.updateUpstreamPrefix(v6OnlyWifi); verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork); - // 6. Update v4 only wifi network, it conflict with hotspot prefix. - final LinkProperties v4OnlyWifiProp = buildUpstreamLinkProperties(true, false, false); - mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp); + // - Update vpn network, it conflict with hotspot prefix but VPN networks are ignored. + final UpstreamNetworkState v4OnlyVpn = buildUpstreamNetworkState(mVpnNetwork, + new LinkAddress("192.168.43.5/24"), null, makeNetworkCapabilities(TRANSPORT_VPN)); + mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyVpn); + verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + // - Update v4 only wifi network, it conflict with hotspot prefix. + final UpstreamNetworkState v4OnlyWifi = buildUpstreamNetworkState(mWifiNetwork, + new LinkAddress("192.168.43.5/24"), null, makeNetworkCapabilities(TRANSPORT_WIFI)); + mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi); verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); reset(mHotspotIpServer); - // 7. Restart hotspot again and its prefix is different previous. + // - Restart hotspot again and its prefix is different previous. mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress( mHotspotIpServer); final IpPrefix hotspotPrefix2 = PrefixUtils.asIpPrefix(hotspotAddr2); assertNotEquals(hotspotPrefix, hotspotPrefix2); when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2); - mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp); + mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi); verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); - // 7. Usb tethering can be enabled and its prefix is different with conflict one. + // - Usb tethering can be enabled and its prefix is different with conflict one. final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress( mUsbIpServer); final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(usbAddr); assertNotEquals(predefinedPrefix, usbPrefix); assertNotEquals(hotspotPrefix2, usbPrefix); when(mUsbIpServer.getAddress()).thenReturn(usbAddr); - // 8. Disable wifi upstream, then wifi's prefix can be selected again. + // - Disable wifi upstream, then wifi's prefix can be selected again. mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork); final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress( mEthernetIpServer); diff --git a/services/Android.bp b/services/Android.bp index ef52c2aff002..25a0d7e06b22 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -3,6 +3,12 @@ java_defaults { plugins: [ "error_prone_android_framework", ], + errorprone: { + javacflags: [ + "-Xep:AndroidFrameworkCompatChange:ERROR", + "-Xep:AndroidFrameworkUid:ERROR", + ], + }, } filegroup { diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 6ca99d1a244b..40b171869cf2 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -317,7 +317,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind AndroidFuture<Association> future = new AndroidFuture<>(); service.startDiscovery(request, callingPackage, callback, future); return future; - }).whenComplete(uncheckExceptions((association, err) -> { + }).cancelTimeout().whenComplete(uncheckExceptions((association, err) -> { if (err == null) { addAssociation(association); } else { diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 6d774869b391..7775354a8077 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -18,14 +18,10 @@ package com.android.server; import static android.Manifest.permission.ACCESS_MTP; import static android.Manifest.permission.INSTALL_PACKAGES; -import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -import static android.Manifest.permission.WRITE_MEDIA_STORAGE; -import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.OP_LEGACY_STORAGE; import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE; -import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE; import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES; import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE; import static android.content.pm.PackageManager.MATCH_ANY_USER; @@ -137,7 +133,6 @@ import android.util.TimeUtils; import android.util.Xml; import com.android.internal.annotations.GuardedBy; -import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.content.PackageMonitor; import com.android.internal.os.AppFuseMount; @@ -153,7 +148,6 @@ import com.android.internal.util.HexDump; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; -import com.android.server.SystemService.TargetUser; import com.android.server.pm.Installer; import com.android.server.storage.AppFuseBridge; import com.android.server.storage.StorageSessionController; @@ -2523,19 +2517,6 @@ class StorageManagerService extends IStorageManager.Stub abortIdleMaint(null); } - private void remountUidExternalStorage(int uid, int mode) { - if (uid == Process.SYSTEM_UID) { - // No need to remount uid for system because it has all access anyways - return; - } - - try { - mVold.remountUid(uid, mode); - } catch (Exception e) { - Slog.wtf(TAG, e); - } - } - @Override public void setDebugFlags(int flags, int mask) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); @@ -3847,21 +3828,6 @@ class StorageManagerService extends IStorageManager.Stub } } - private IAppOpsCallback.Stub mAppOpsCallback = new IAppOpsCallback.Stub() { - @Override - public void opChanged(int op, int uid, String packageName) throws RemoteException { - if (!ENABLE_ISOLATED_STORAGE) return; - - int mountMode = getMountMode(uid, packageName); - boolean isUidActive = LocalServices.getService(ActivityManagerInternal.class) - .getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT; - - if (isUidActive) { - remountUidExternalStorage(uid, mountMode); - } - } - }; - private void addObbStateLocked(ObbState obbState) throws RemoteException { final IBinder binder = obbState.getBinder(); List<ObbState> obbStates = mObbMounts.get(binder); @@ -4236,19 +4202,9 @@ class StorageManagerService extends IStorageManager.Stub } // Determine if caller is holding runtime permission - final boolean hasRead = StorageManager.checkPermissionAndCheckOp(mContext, false, 0, - uid, packageName, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE); final boolean hasWrite = StorageManager.checkPermissionAndCheckOp(mContext, false, 0, uid, packageName, WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE); - // We're only willing to give out broad access if they also hold - // runtime permission; this is a firm CDD requirement - final boolean hasFull = mIPackageManager.checkUidPermission(WRITE_MEDIA_STORAGE, - uid) == PERMISSION_GRANTED; - if (hasFull && hasWrite) { - return Zygote.MOUNT_EXTERNAL_FULL; - } - // We're only willing to give out installer access if they also hold // runtime permission; this is a firm CDD requirement final boolean hasInstall = mIPackageManager.checkUidPermission(INSTALL_PACKAGES, @@ -4268,19 +4224,7 @@ class StorageManagerService extends IStorageManager.Stub if ((hasInstall || hasInstallOp) && hasWrite) { return Zygote.MOUNT_EXTERNAL_INSTALLER; } - - // Otherwise we're willing to give out sandboxed or non-sandboxed if - // they hold the runtime permission - boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, - uid, packageName) == MODE_ALLOWED; - - if (hasLegacy && hasWrite) { - return Zygote.MOUNT_EXTERNAL_WRITE; - } else if (hasLegacy && hasRead) { - return Zygote.MOUNT_EXTERNAL_READ; - } else { - return Zygote.MOUNT_EXTERNAL_DEFAULT; - } + return Zygote.MOUNT_EXTERNAL_DEFAULT; } catch (RemoteException e) { // Should not happen } @@ -4570,12 +4514,6 @@ class StorageManagerService extends IStorageManager.Stub } @Override - public void onExternalStoragePolicyChanged(int uid, String packageName) { - final int mountMode = getExternalStorageMountMode(uid, packageName); - remountUidExternalStorage(uid, mountMode); - } - - @Override public int getExternalStorageMountMode(int uid, String packageName) { if (ENABLE_ISOLATED_STORAGE) { return getMountMode(uid, packageName); @@ -4720,16 +4658,6 @@ class StorageManagerService extends IStorageManager.Stub updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED); return; } - - if (mode == MODE_ALLOWED && (code == OP_READ_EXTERNAL_STORAGE - || code == OP_WRITE_EXTERNAL_STORAGE - || code == OP_REQUEST_INSTALL_PACKAGES)) { - final UserManagerInternal userManagerInternal = - LocalServices.getService(UserManagerInternal.class); - if (userManagerInternal.isUserInitialized(UserHandle.getUserId(uid))) { - onExternalStoragePolicyChanged(uid, packageName); - } - } } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 5a34283ed0cc..aaff831aeb96 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -42,7 +42,6 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.content.BroadcastReceiver; @@ -5736,8 +5735,8 @@ public class AccountManagerService private boolean isProfileOwner(int uid) { final DevicePolicyManagerInternal dpmi = LocalServices.getService(DevicePolicyManagerInternal.class); - return (dpmi != null) - && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode. + return (dpmi != null) && (dpmi.isActiveProfileOwner(uid) || dpmi.isActiveDeviceOwner(uid)); } @Override diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ffdcd156122e..1fe0012615da 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -153,7 +153,6 @@ import android.app.ContentProviderHolder; import android.app.IActivityController; import android.app.IActivityManager; import android.app.IApplicationThread; -import android.app.IAssistDataReceiver; import android.app.IInstrumentationWatcher; import android.app.INotificationManager; import android.app.IProcessObserver; @@ -286,7 +285,6 @@ import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoUtils; import android.view.Display; import android.view.Gravity; -import android.view.IRecentsAnimationRunner; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; @@ -2397,8 +2395,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void setFocusedStack(int stackId) { - mActivityTaskManager.setFocusedStack(stackId); + public void setFocusedRootTask(int taskId) { + mActivityTaskManager.setFocusedRootTask(taskId); } /** Sets the task stack listener that gets callbacks when a task stack changes. */ @@ -2840,18 +2838,6 @@ public class ActivityManagerService extends IActivityManager.Stub return mActivityTaskManager.startActivityFromRecents(taskId, bOptions); } - @Override - public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver, - IRecentsAnimationRunner recentsAnimationRunner) { - mActivityTaskManager.startRecentsActivity( - intent, assistDataReceiver, recentsAnimationRunner); - } - - @Override - public void cancelRecentsAnimation(boolean restoreHomeStackPosition) { - mActivityTaskManager.cancelRecentsAnimation(restoreHomeStackPosition); - } - /** * This is the internal entry point for handling Activity.finish(). * @@ -5672,11 +5658,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void removeStack(int stackId) { - mActivityTaskManager.removeStack(stackId); - } - - @Override public boolean removeTask(int taskId) { return mActivityTaskManager.removeTask(taskId); } @@ -5712,8 +5693,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void moveTaskToStack(int taskId, int stackId, boolean toTop) { - mActivityTaskManager.moveTaskToStack(taskId, stackId, toTop); + public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) { + mActivityTaskManager.moveTaskToRootTask(taskId, rootTaskId, toTop); } @Override @@ -5723,17 +5704,17 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * Moves the top activity in the input stackId to the pinned stack. + * Moves the top activity in the input rootTaskId to the pinned root task. * - * @param stackId Id of stack to move the top activity to pinned stack. - * @param bounds Bounds to use for pinned stack. + * @param rootTaskId Id of root task to move the top activity to pinned root task. + * @param bounds Bounds to use for pinned root task. * - * @return True if the top activity of the input stack was successfully moved to the pinned - * stack. + * @return True if the top activity of the input root task was successfully moved to the pinned + * root task. */ @Override - public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) { - return mActivityTaskManager.moveTopActivityToPinnedStack(stackId, bounds); + public boolean moveTopActivityToPinnedRootTask(int rootTaskId, Rect bounds) { + return mActivityTaskManager.moveTopActivityToPinnedRootTask(rootTaskId, bounds); } @Override @@ -8198,12 +8179,10 @@ public class ActivityManagerService extends IActivityManager.Stub } finally { Binder.restoreCallingIdentity(identity); } - if (uid == Process.INVALID_UID) { - return Process.INVALID_UID; - } + // If the uid is Process.INVALID_UID, the below 'if' check will be always true if (UserHandle.getAppId(uid) != UserHandle.getAppId(callingUid)) { // Requires the DUMP permission if the target package doesn't belong - // to the caller. + // to the caller or it doesn't exist. enforceCallingPermission(android.Manifest.permission.DUMP, function); } return uid; @@ -16492,7 +16471,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int getStorageMountMode(int pid, int uid) { if (uid == SHELL_UID || uid == ROOT_UID) { - return Zygote.MOUNT_EXTERNAL_FULL; + return Zygote.MOUNT_EXTERNAL_DEFAULT; } synchronized (mPidsSelfLocked) { final ProcessRecord pr = mPidsSelfLocked.get(pid); diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index d346430fe312..31e7106979f1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2587,9 +2587,9 @@ final class ActivityManagerShellCommand extends ShellCommand { case "info": return runRootTaskInfo(pw); case "move-top-activity-to-pinned-stack": - return runMoveTopActivityToPinnedStack(pw); + return runMoveTopActivityToPinnedRootTask(pw); case "remove": - return runStackRemove(pw); + return runRootTaskRemove(pw); default: getErrPrintWriter().println("Error: unknown command '" + op + "'"); return -1; @@ -2626,19 +2626,19 @@ final class ActivityManagerShellCommand extends ShellCommand { } int runDisplayMoveStack(PrintWriter pw) throws RemoteException { - String stackIdStr = getNextArgRequired(); - int stackId = Integer.parseInt(stackIdStr); + String rootTaskIdStr = getNextArgRequired(); + int rootTaskId = Integer.parseInt(rootTaskIdStr); String displayIdStr = getNextArgRequired(); int displayId = Integer.parseInt(displayIdStr); - mTaskInterface.moveStackToDisplay(stackId, displayId); + mTaskInterface.moveRootTaskToDisplay(rootTaskId, displayId); return 0; } int runStackMoveTask(PrintWriter pw) throws RemoteException { String taskIdStr = getNextArgRequired(); int taskId = Integer.parseInt(taskIdStr); - String stackIdStr = getNextArgRequired(); - int stackId = Integer.parseInt(stackIdStr); + String rootTaskIdStr = getNextArgRequired(); + int rootTaskId = Integer.parseInt(rootTaskIdStr); String toTopStr = getNextArgRequired(); final boolean toTop; if ("true".equals(toTopStr)) { @@ -2650,7 +2650,7 @@ final class ActivityManagerShellCommand extends ShellCommand { return -1; } - mTaskInterface.moveTaskToStack(taskId, stackId, toTop); + mTaskInterface.moveTaskToRootTask(taskId, rootTaskId, toTop); return 0; } @@ -2670,22 +2670,22 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } - int runStackRemove(PrintWriter pw) throws RemoteException { - String stackIdStr = getNextArgRequired(); - int stackId = Integer.parseInt(stackIdStr); - mTaskInterface.removeStack(stackId); + int runRootTaskRemove(PrintWriter pw) throws RemoteException { + String taskIdStr = getNextArgRequired(); + int taskId = Integer.parseInt(taskIdStr); + mTaskInterface.removeTask(taskId); return 0; } - int runMoveTopActivityToPinnedStack(PrintWriter pw) throws RemoteException { - int stackId = Integer.parseInt(getNextArgRequired()); + int runMoveTopActivityToPinnedRootTask(PrintWriter pw) throws RemoteException { + int rootTaskId = Integer.parseInt(getNextArgRequired()); final Rect bounds = getBounds(); if (bounds == null) { getErrPrintWriter().println("Error: invalid input bounds"); return -1; } - if (!mTaskInterface.moveTopActivityToPinnedStack(stackId, bounds)) { + if (!mTaskInterface.moveTopActivityToPinnedRootTask(rootTaskId, bounds)) { getErrPrintWriter().println("Didn't move top activity to pinned stack."); return -1; } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 5e6556339b43..ed47616d92dc 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -92,7 +92,6 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; -import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.system.Os; import android.text.TextUtils; @@ -1780,14 +1779,10 @@ public final class ProcessList { final IPackageManager pm = AppGlobals.getPackageManager(); permGids = pm.getPackageGids(app.info.packageName, MATCH_DIRECT_BOOT_AUTO, app.userId); - if (StorageManager.hasIsolatedStorage() && mountExtStorageFull) { - mountExternal = Zygote.MOUNT_EXTERNAL_FULL; - } else { - StorageManagerInternal storageManagerInternal = LocalServices.getService( - StorageManagerInternal.class); - mountExternal = storageManagerInternal.getExternalStorageMountMode(uid, - app.info.packageName); - } + StorageManagerInternal storageManagerInternal = LocalServices.getService( + StorageManagerInternal.class); + mountExternal = storageManagerInternal.getExternalStorageMountMode(uid, + app.info.packageName); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -1918,7 +1913,9 @@ public final class ProcessList { String instructionSet = null; if (app.info.primaryCpuAbi != null) { - instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi); + // If ABI override is specified, use the isa derived from the value of ABI override. + // Otherwise, use the isa derived from primary ABI + instructionSet = VMRuntime.getInstructionSet(requiredAbi); } app.gids = gids; @@ -1927,7 +1924,7 @@ public final class ProcessList { // If instructionSet is non-null, this indicates that the system_server is spawning a // process with an ISA that may be different from its own. System (kernel and hardware) - // compatililty for these features is checked in the decideTaggingLevel in the + // compatibility for these features is checked in the decideTaggingLevel in the // system_server process (not the child process). As both MTE and TBI are only supported // in aarch64, we can simply ensure that the new process is also aarch64. This prevents // the mismatch where a 64-bit system server spawns a 32-bit child that thinks it should diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index c1777b847d9b..7dbb39e3cb39 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -1709,24 +1709,13 @@ public class AppOpsService extends IAppOpsService.Stub { if (Process.isIsolated(uid)) { return Zygote.MOUNT_EXTERNAL_NONE; } - if (noteOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE, uid, - packageName, null, true, "External storage policy", true) - != AppOpsManager.MODE_ALLOWED) { - return Zygote.MOUNT_EXTERNAL_NONE; - } - if (noteOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, uid, - packageName, null, true, "External storage policy", true) - != AppOpsManager.MODE_ALLOWED) { - return Zygote.MOUNT_EXTERNAL_READ; - } - return Zygote.MOUNT_EXTERNAL_WRITE; + return Zygote.MOUNT_EXTERNAL_DEFAULT; } @Override public boolean hasExternalStorage(int uid, String packageName) { final int mountMode = getMountMode(uid, packageName); - return mountMode == Zygote.MOUNT_EXTERNAL_READ - || mountMode == Zygote.MOUNT_EXTERNAL_WRITE; + return mountMode != Zygote.MOUNT_EXTERNAL_NONE; } }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java index 92c498c55dbf..bac944fca1de 100644 --- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java @@ -27,8 +27,6 @@ public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> { private static final String TAG = "GenerateChallengeClient"; - protected long mChallenge; - public GenerateChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) { @@ -51,12 +49,5 @@ public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> { super.start(callback); startHalOperation(); - try { - getListener().onChallengeGenerated(getSensorId(), mChallenge); - mCallback.onClientFinished(this, true /* success */); - } catch (RemoteException e) { - Slog.e(TAG, "Remote exception", e); - mCallback.onClientFinished(this, false /* success */); - } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java index ba401f255e43..406a7ccedc33 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java @@ -59,7 +59,14 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiomet @Override protected void startHalOperation() { try { - mChallenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value; + final long challenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value; + try { + getListener().onChallengeGenerated(getSensorId(), challenge); + mCallback.onClientFinished(this, true /* success */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + mCallback.onClientFinished(this, false /* success */); + } } catch (RemoteException e) { Slog.e(TAG, "generateChallenge failed", e); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java index abaaac59fbef..5169c7de8cdd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java @@ -45,7 +45,14 @@ public class FingerprintGenerateChallengeClient @Override protected void startHalOperation() { try { - mChallenge = getFreshDaemon().preEnroll(); + final long challenge = getFreshDaemon().preEnroll(); + try { + getListener().onChallengeGenerated(getSensorId(), challenge); + mCallback.onClientFinished(this, true /* success */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + mCallback.onClientFinished(this, false /* success */); + } } catch (RemoteException e) { Slog.e(TAG, "preEnroll failed", e); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 14b34780bfc9..a45d94bfc002 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1130,7 +1130,13 @@ public class Vpn { return mNetworkInfo; } - public int getNetId() { + /** + * Return netId of current running VPN network. + * + * @return a netId if there is a running VPN network or NETID_UNSET if there is no running VPN + * network or network is null. + */ + public synchronized int getNetId() { final NetworkAgent agent = mNetworkAgent; if (null == agent) return NETID_UNSET; final Network network = agent.getNetwork(); @@ -1708,7 +1714,7 @@ public class Vpn { /** * Return the configuration of the currently running VPN. */ - public VpnConfig getVpnConfig() { + public synchronized VpnConfig getVpnConfig() { enforceControlPermission(); return mConfig; } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index bb6f14c0f6c4..96679c3ab767 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -16,6 +16,8 @@ package com.android.server.hdmi; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.tv.cec.V1_0.CecMessage; import android.hardware.tv.cec.V1_0.HotplugEvent; @@ -774,6 +776,7 @@ final class HdmiCecController { private IHdmiCec mHdmiCec; private final Object mLock = new Object(); private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS; + @Nullable private HdmiCecCallback mCallback; @Override public String nativeInit() { @@ -782,7 +785,7 @@ final class HdmiCecController { boolean connectToHal() { try { - mHdmiCec = IHdmiCec.getService(); + mHdmiCec = IHdmiCec.getService(true); try { mHdmiCec.linkToDeath(this, HDMI_CEC_HAL_DEATH_COOKIE); } catch (RemoteException e) { @@ -796,7 +799,8 @@ final class HdmiCecController { } @Override - public void setCallback(HdmiCecCallback callback) { + public void setCallback(@NonNull HdmiCecCallback callback) { + mCallback = callback; try { mHdmiCec.setCallback(callback); } catch (RemoteException e) { @@ -936,6 +940,10 @@ final class HdmiCecController { if (cookie == HDMI_CEC_HAL_DEATH_COOKIE) { HdmiLogger.error("Service died cookie : " + cookie + "; reconnecting"); connectToHal(); + // Reconnect the callback + if (mCallback != null) { + setCallback(mCallback); + } } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index cc8a330b5edb..a2304f4ad158 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -231,6 +231,8 @@ public class InputManagerService extends IInputManager.Stub private static native void nativePilferPointers(long ptr, IBinder token); private static native void nativeSetInputFilterEnabled(long ptr, boolean enable); private static native void nativeSetInTouchMode(long ptr, boolean inTouchMode); + private static native void nativeSetMaximumObscuringOpacityForTouch(long ptr, float opacity); + private static native void nativeSetBlockUntrustedTouchesMode(long ptr, int mode); private static native int nativeInjectInputEvent(long ptr, InputEvent event, int injectorPid, int injectorUid, int syncMode, int timeoutMillis, int policyFlags); @@ -399,6 +401,8 @@ public class InputManagerService extends IInputManager.Stub registerShowTouchesSettingObserver(); registerAccessibilityLargePointerSettingObserver(); registerLongPressTimeoutObserver(); + registerMaximumObscuringOpacityForTouchSettingObserver(); + registerBlockUntrustedTouchesModeSettingObserver(); mContext.registerReceiver(new BroadcastReceiver() { @Override @@ -414,6 +418,8 @@ public class InputManagerService extends IInputManager.Stub updateShowTouchesFromSettings(); updateAccessibilityLargePointerFromSettings(); updateDeepPressStatusFromSettings("just booted"); + updateMaximumObscuringOpacityForTouchFromSettings(); + updateBlockUntrustedTouchesModeFromSettings(); } // TODO(BT) Pass in parameter for bluetooth system @@ -1739,6 +1745,46 @@ public class InputManagerService extends IInputManager.Stub }, UserHandle.USER_ALL); } + private void registerBlockUntrustedTouchesModeSettingObserver() { + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE), + /* notifyForDescendants */ true, + new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + updateBlockUntrustedTouchesModeFromSettings(); + } + }, UserHandle.USER_ALL); + } + + private void updateBlockUntrustedTouchesModeFromSettings() { + final int mode = InputManager.getInstance().getBlockUntrustedTouchesMode(mContext); + nativeSetBlockUntrustedTouchesMode(mPtr, mode); + } + + private void registerMaximumObscuringOpacityForTouchSettingObserver() { + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH), + /* notifyForDescendants */ true, + new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + updateMaximumObscuringOpacityForTouchFromSettings(); + } + }, UserHandle.USER_ALL); + } + + private void updateMaximumObscuringOpacityForTouchFromSettings() { + final float opacity = InputManager.getInstance().getMaximumObscuringOpacityForTouch( + mContext); + if (opacity < 0 || opacity > 1) { + Log.e(TAG, "Invalid maximum obscuring opacity " + opacity + + ", it should be >= 0 and <= 1, rejecting update."); + return; + } + nativeSetMaximumObscuringOpacityForTouch(mPtr, opacity); + } + private int getShowTouchesSetting(int defaultValue) { int result = defaultValue; try { diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java index 6f35c8ba1e0a..ecdd429cd0c8 100644 --- a/services/core/java/com/android/server/location/LocationFudger.java +++ b/services/core/java/com/android/server/location/LocationFudger.java @@ -111,7 +111,7 @@ public class LocationFudger { */ public Location createCoarse(Location fine) { synchronized (this) { - if (fine == mCachedFineLocation) { + if (fine == mCachedFineLocation || fine == mCachedCoarseLocation) { return mCachedCoarseLocation; } } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 227cdeefdc3b..f72fee689190 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -17,12 +17,14 @@ package com.android.server.location; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.app.compat.CompatChanges.isChangeEnabled; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; import static android.location.LocationManager.NETWORK_PROVIDER; +import static android.location.LocationManager.PREVENT_PENDING_INTENT_SYSTEM_API_USAGE; import static android.location.LocationRequest.LOW_POWER_EXCEPTIONS; import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; @@ -75,7 +77,6 @@ import android.os.WorkSource.WorkChain; import android.stats.location.LocationStatsEnums; import android.util.IndentingPrintWriter; import android.util.Log; -import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.location.ProviderProperties; @@ -570,7 +571,7 @@ public class LocationManagerService extends ILocationManager.Stub { new IllegalArgumentException()); } - request = validateLocationRequest(request); + request = validateLocationRequest(request, identity); LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, @@ -592,7 +593,21 @@ public class LocationManagerService extends ILocationManager.Stub { // clients in the system process must have an attribution tag set Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null); - request = validateLocationRequest(request); + // pending intents requests may not use system apis because we do not keep track if clients + // lose the relevant permissions, and thus should not get the benefit of those apis. its + // simplest to ensure these apis are simply never set for pending intent requests. the same + // does not apply for listener requests since those will have the process (including the + // listener) killed on permission removal + boolean usesSystemApi = request.isLowPower() + || request.isHiddenFromAppOps() + || request.isLocationSettingsIgnored() + || !request.getWorkSource().isEmpty(); + if (usesSystemApi + && isChangeEnabled(PREVENT_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) { + throw new SecurityException("PendingIntent location requests may not use system APIs"); + } + + request = validateLocationRequest(request, identity); LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, @@ -601,9 +616,9 @@ public class LocationManagerService extends ILocationManager.Stub { manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent); } - private LocationRequest validateLocationRequest(LocationRequest request) { - WorkSource workSource = request.getWorkSource(); - if (workSource != null && !workSource.isEmpty()) { + private LocationRequest validateLocationRequest(LocationRequest request, + CallerIdentity identity) { + if (!request.getWorkSource().isEmpty()) { mContext.enforceCallingOrSelfPermission( permission.UPDATE_DEVICE_STATS, "setting a work source requires " + permission.UPDATE_DEVICE_STATS); @@ -634,23 +649,25 @@ public class LocationManagerService extends ILocationManager.Stub { } } - if (request.getWorkSource() != null) { - if (request.getWorkSource().isEmpty()) { - sanitized.setWorkSource(null); - } else if (request.getWorkSource().getPackageName(0) == null) { - Log.w(TAG, "received (and ignoring) illegal worksource with no package name"); - sanitized.setWorkSource(null); - } else { - List<WorkChain> workChains = request.getWorkSource().getWorkChains(); - if (workChains != null && !workChains.isEmpty() && workChains.get( - 0).getAttributionTag() == null) { - Log.w(TAG, - "received (and ignoring) illegal worksource with no attribution tag"); - sanitized.setWorkSource(null); - } + WorkSource workSource = new WorkSource(request.getWorkSource()); + if (workSource.size() > 0 && workSource.getPackageName(0) == null) { + Log.w(TAG, "received (and ignoring) illegal worksource with no package name"); + workSource.clear(); + } else { + List<WorkChain> workChains = workSource.getWorkChains(); + if (workChains != null && !workChains.isEmpty() + && workChains.get(0).getAttributionTag() == null) { + Log.w(TAG, + "received (and ignoring) illegal worksource with no attribution tag"); + workSource.clear(); } } + if (workSource.isEmpty()) { + identity.addToWorkSource(workSource); + } + sanitized.setWorkSource(workSource); + return sanitized.build(); } @@ -684,15 +701,7 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - Location location = manager.getLastLocation(identity, permissionLevel, false); - - // lastly - note app ops - if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), - identity)) { - return null; - } - - return location; + return manager.getLastLocation(identity, permissionLevel, false); } @Nullable @@ -710,7 +719,7 @@ public class LocationManagerService extends ILocationManager.Stub { // clients in the system process must have an attribution tag set Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null); - request = validateLocationRequest(request); + request = validateLocationRequest(request, identity); LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, @@ -727,7 +736,6 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - // use fine permission level to avoid creating unnecessary coarse locations Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL, PERMISSION_FINE, false, Long.MAX_VALUE); if (location == null) { @@ -1116,9 +1124,8 @@ public class LocationManagerService extends ILocationManager.Stub { return; } - ipw.print("Location Manager State:"); + ipw.println("Location Manager State:"); ipw.increaseIndent(); - ipw.println("Elapsed Realtime: " + TimeUtils.formatDuration(SystemClock.elapsedRealtime())); ipw.println("User Info:"); ipw.increaseIndent(); diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index 48f8da4f4b14..ecf82e8f8bd6 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -31,6 +31,7 @@ import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; import static com.android.internal.location.ProviderRequest.EMPTY_REQUEST; +import static com.android.internal.location.ProviderRequest.INTERVAL_DISABLED; import static com.android.server.location.LocationManagerService.D; import static com.android.server.location.LocationManagerService.TAG; import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; @@ -238,8 +239,7 @@ class LocationProviderManager extends protected abstract class Registration extends RemoteListenerRegistration<LocationRequest, LocationTransport, LocationListenerOperation> { - @PermissionLevel protected final int mPermissionLevel; - private final WorkSource mWorkSource; + private final @PermissionLevel int mPermissionLevel; // we cache these values because checking/calculating on the fly is more expensive private boolean mPermitted; @@ -247,22 +247,17 @@ class LocationProviderManager extends private LocationRequest mProviderLocationRequest; private boolean mIsUsingHighPower; - @Nullable private Location mLastLocation = null; + private @Nullable Location mLastLocation = null; protected Registration(LocationRequest request, CallerIdentity identity, LocationTransport transport, @PermissionLevel int permissionLevel) { super(Objects.requireNonNull(request), identity, transport); Preconditions.checkArgument(permissionLevel > PERMISSION_NONE); - mPermissionLevel = permissionLevel; - - if (request.getWorkSource() != null && !request.getWorkSource().isEmpty()) { - mWorkSource = request.getWorkSource(); - } else { - mWorkSource = identity.addToWorkSource(null); - } + Preconditions.checkArgument(!request.getWorkSource().isEmpty()); - mProviderLocationRequest = super.getRequest(); + mPermissionLevel = permissionLevel; + mProviderLocationRequest = request; } @GuardedBy("mLock") @@ -376,6 +371,10 @@ class LocationProviderManager extends return mLastLocation; } + public @PermissionLevel int getPermissionLevel() { + return mPermissionLevel; + } + public final boolean isForeground() { return mForeground; } @@ -389,10 +388,6 @@ class LocationProviderManager extends return LocationProviderManager.this; } - protected final WorkSource getWorkSource() { - return mWorkSource; - } - @GuardedBy("mLock") private void onHighPowerUsageChanged() { boolean isUsingHighPower = isUsingHighPower(); @@ -524,16 +519,18 @@ class LocationProviderManager extends } } - if (baseRequest.isLocationSettingsIgnored()) { + boolean locationSettingsIgnored = baseRequest.isLocationSettingsIgnored(); + if (locationSettingsIgnored) { // if we are not currently allowed use location settings ignored, disable it if (!mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains( getIdentity().getPackageName()) && !mLocationManagerInternal.isProvider( null, getIdentity())) { builder.setLocationSettingsIgnored(false); + locationSettingsIgnored = false; } } - if (!baseRequest.isLocationSettingsIgnored() && !isThrottlingExempt()) { + if (!locationSettingsIgnored && !isThrottlingExempt()) { // throttle in the background if (!mForeground) { builder.setIntervalMillis(max(baseRequest.getIntervalMillis(), @@ -607,7 +604,7 @@ class LocationProviderManager extends mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class)) .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); mWakeLock.setReferenceCounted(true); - mWakeLock.setWorkSource(getWorkSource()); + mWakeLock.setWorkSource(request.getWorkSource()); } @Override @@ -626,7 +623,7 @@ class LocationProviderManager extends onAlarm(); } else if (mExpirationRealtimeMs < Long.MAX_VALUE) { mAlarmHelper.setDelayedAlarm(mExpirationRealtimeMs - registerTimeMs, this, - getWorkSource()); + getRequest().getWorkSource()); } // start listening for provider enabled/disabled events @@ -688,7 +685,7 @@ class LocationProviderManager extends if (maxLocationAgeMs > MIN_REQUEST_DELAY_MS) { Location lastLocation = getLastLocationUnsafe( getIdentity().getUserId(), - PERMISSION_FINE, // acceptLocationChange() handles coarsening this + getPermissionLevel(), getRequest().isLocationSettingsIgnored(), maxLocationAgeMs); if (lastLocation != null) { @@ -730,18 +727,8 @@ class LocationProviderManager extends return null; } - Location location; - switch (mPermissionLevel) { - case PERMISSION_FINE: - location = fineLocation; - break; - case PERMISSION_COARSE: - location = mLocationFudger.createCoarse(fineLocation); - break; - default: - // shouldn't be possible to have a client added without location permissions - throw new AssertionError(); - } + Location location = Objects.requireNonNull( + getPermittedLocation(fineLocation, getPermissionLevel())); Location lastDeliveredLocation = getLastDeliveredLocation(); if (lastDeliveredLocation != null) { @@ -763,7 +750,7 @@ class LocationProviderManager extends } // note app ops - if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel), + if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()), getIdentity())) { if (D) { Log.w(TAG, "noteOp denied for " + getIdentity()); @@ -960,7 +947,7 @@ class LocationProviderManager extends } protected final class GetCurrentLocationListenerRegistration extends Registration implements - IBinder.DeathRecipient, ProviderEnabledListener, OnAlarmListener { + IBinder.DeathRecipient, OnAlarmListener { private volatile LocationTransport mTransport; @@ -972,11 +959,6 @@ class LocationProviderManager extends mTransport = transport; } - @GuardedBy("mLock") - void deliverLocation(@Nullable Location location) { - executeSafely(getExecutor(), () -> mTransport, acceptLocationChange(location)); - } - @Override protected void onListenerUnregister() { mTransport = null; @@ -999,29 +981,13 @@ class LocationProviderManager extends onAlarm(); } else if (mExpirationRealtimeMs < Long.MAX_VALUE) { mAlarmHelper.setDelayedAlarm(mExpirationRealtimeMs - registerTimeMs, this, - getWorkSource()); - } - - // if this request is ignoring location settings, then we don't want to immediately fail - // it if the provider is disabled or becomes disabled. - if (!getRequest().isLocationSettingsIgnored()) { - // start listening for provider enabled/disabled events - addEnabledListener(this); - - // if the provider is currently disabled fail immediately - int userId = getIdentity().getUserId(); - if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) { - deliverLocation(null); - } + getRequest().getWorkSource()); } } @GuardedBy("mLock") @Override protected void onProviderListenerUnregister() { - // stop listening for provider enabled/disabled events - removeEnabledListener(this); - // remove alarm for expiration if (mExpirationRealtimeMs < Long.MAX_VALUE) { mAlarmHelper.cancel(this); @@ -1030,6 +996,38 @@ class LocationProviderManager extends ((IBinder) getKey()).unlinkToDeath(this, 0); } + @GuardedBy("mLock") + @Override + protected LocationListenerOperation onProviderListenerActive() { + Location lastLocation = getLastLocationUnsafe( + getIdentity().getUserId(), + getPermissionLevel(), + getRequest().isLocationSettingsIgnored(), + MAX_CURRENT_LOCATION_AGE_MS); + if (lastLocation != null) { + return acceptLocationChange(lastLocation); + } + + return null; + } + + @GuardedBy("mLock") + @Override + protected LocationListenerOperation onProviderListenerInactive() { + if (!getRequest().isLocationSettingsIgnored()) { + // if we go inactive for any reason, fail immediately + return acceptLocationChange(null); + } + + return null; + } + + void deliverNull() { + synchronized (mLock) { + executeSafely(getExecutor(), () -> mTransport, acceptLocationChange(null)); + } + } + @Override public void onAlarm() { if (D) { @@ -1039,9 +1037,10 @@ class LocationProviderManager extends } synchronized (mLock) { - deliverLocation(null); // no need to remove alarm after it's fired mExpirationRealtimeMs = Long.MAX_VALUE; + + deliverNull(); } } @@ -1060,29 +1059,16 @@ class LocationProviderManager extends } // lastly - note app ops - Location location; - if (fineLocation == null) { - location = null; - } else if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel), + if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()), getIdentity())) { if (D) { Log.w(TAG, "noteOp denied for " + getIdentity()); } - location = null; - } else { - switch (mPermissionLevel) { - case PERMISSION_FINE: - location = fineLocation; - break; - case PERMISSION_COARSE: - location = mLocationFudger.createCoarse(fineLocation); - break; - default: - // shouldn't be possible to have a client added without location permissions - throw new AssertionError(); - } + fineLocation = null; } + Location location = getPermittedLocation(fineLocation, getPermissionLevel()); + return new LocationListenerOperation() { @Override public Location getLocation() { @@ -1118,22 +1104,6 @@ class LocationProviderManager extends } @Override - public void onProviderEnabledChanged(String provider, int userId, boolean enabled) { - Preconditions.checkState(mName.equals(provider)); - - if (userId != getIdentity().getUserId()) { - return; - } - - // if the provider is disabled we give up on current location immediately - if (!getRequest().isLocationSettingsIgnored() && !enabled) { - synchronized (mLock) { - deliverLocation(null); - } - } - } - - @Override public void binderDied() { try { if (D) { @@ -1172,7 +1142,7 @@ class LocationProviderManager extends protected final LocationManagerInternal mLocationManagerInternal; protected final SettingsHelper mSettingsHelper; - protected final UserInfoHelper mUserInfoHelper; + protected final UserInfoHelper mUserHelper; protected final AlarmHelper mAlarmHelper; protected final AppOpsHelper mAppOpsHelper; protected final LocationPermissionsHelper mLocationPermissionsHelper; @@ -1234,7 +1204,7 @@ class LocationProviderManager extends mLocationManagerInternal = Objects.requireNonNull( LocalServices.getService(LocationManagerInternal.class)); mSettingsHelper = injector.getSettingsHelper(); - mUserInfoHelper = injector.getUserInfoHelper(); + mUserHelper = injector.getUserInfoHelper(); mAlarmHelper = injector.getAlarmHelper(); mAppOpsHelper = injector.getAppOpsHelper(); mLocationPermissionsHelper = injector.getLocationPermissionsHelper(); @@ -1259,7 +1229,7 @@ class LocationProviderManager extends synchronized (mLock) { mStarted = true; - mUserInfoHelper.addListener(mUserChangedListener); + mUserHelper.addListener(mUserChangedListener); mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener); long identity = Binder.clearCallingIdentity(); @@ -1274,20 +1244,19 @@ class LocationProviderManager extends public void stopManager() { synchronized (mLock) { - mUserInfoHelper.removeListener(mUserChangedListener); + mUserHelper.removeListener(mUserChangedListener); mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener); - // notify and remove all listeners + mStarted = false; + long identity = Binder.clearCallingIdentity(); try { - onUserStopped(UserHandle.USER_ALL); + onEnabledChanged(UserHandle.USER_ALL); removeRegistrationIf(key -> true); + mEnabledListeners.clear(); } finally { Binder.restoreCallingIdentity(identity); } - - mEnabledListeners.clear(); - mStarted = false; } } @@ -1439,31 +1408,43 @@ class LocationProviderManager extends identity.getPackageName())) { return null; } - if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { - return null; + if (!ignoreLocationSettings) { + if (!isEnabled(identity.getUserId())) { + return null; + } + if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) { + return null; + } } - if (!ignoreLocationSettings && !isEnabled(identity.getUserId())) { + + // lastly - note app ops + if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), + identity)) { return null; } - Location location = getLastLocationUnsafe(identity.getUserId(), permissionLevel, - ignoreLocationSettings, Long.MAX_VALUE); - - // we don't note op here because we don't know what the client intends to do with the - // location, the client is responsible for noting if necessary + Location location = getPermittedLocation( + getLastLocationUnsafe( + identity.getUserId(), + permissionLevel, + ignoreLocationSettings, + Long.MAX_VALUE), + permissionLevel); - if (identity.getPid() == Process.myPid() && location != null) { + if (location != null && identity.getPid() == Process.myPid()) { // if delivering to the same process, make a copy of the location first (since // location is mutable) - return new Location(location); - } else { - return location; + location = new Location(location); } + + return location; } /** * This function does not perform any permissions or safety checks, by calling it you are - * committing to performing all applicable checks yourself. + * committing to performing all applicable checks yourself. This always returns a "fine" + * location, even if the permissionLevel is coarse. You are responsible for coarsening the + * location if necessary. */ @Nullable public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel, @@ -1471,7 +1452,7 @@ class LocationProviderManager extends if (userId == UserHandle.USER_ALL) { // find the most recent location across all users Location lastLocation = null; - final int[] runningUserIds = mUserInfoHelper.getRunningUserIds(); + final int[] runningUserIds = mUserHelper.getRunningUserIds(); for (int i = 0; i < runningUserIds.length; i++) { Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel, ignoreLocationSettings, maximumAgeMs); @@ -1516,7 +1497,7 @@ class LocationProviderManager extends private void setLastLocation(Location location, int userId) { if (userId == UserHandle.USER_ALL) { - final int[] runningUserIds = mUserInfoHelper.getRunningUserIds(); + final int[] runningUserIds = mUserHelper.getRunningUserIds(); for (int i = 0; i < runningUserIds.length; i++) { setLastLocation(location, runningUserIds[i]); } @@ -1532,17 +1513,16 @@ class LocationProviderManager extends mLastLocations.put(userId, lastLocation); } - Location coarseLocation = mLocationFudger.createCoarse(location); if (isEnabled(userId)) { - lastLocation.set(location, coarseLocation); + lastLocation.set(location); } - lastLocation.setBypass(location, coarseLocation); + lastLocation.setBypass(location); } } @Nullable public ICancellationSignal getCurrentLocation(LocationRequest request, - CallerIdentity callerIdentity, int permissionLevel, ILocationCallback callback) { + CallerIdentity identity, int permissionLevel, ILocationCallback callback) { if (request.getDurationMillis() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) { request = new LocationRequest.Builder(request) .setDurationMillis(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) @@ -1552,52 +1532,31 @@ class LocationProviderManager extends GetCurrentLocationListenerRegistration registration = new GetCurrentLocationListenerRegistration( request, - callerIdentity, + identity, new GetCurrentLocationTransport(callback), permissionLevel); synchronized (mLock) { - if (mSettingsHelper.isLocationPackageBlacklisted(callerIdentity.getUserId(), - callerIdentity.getPackageName())) { - registration.deliverLocation(null); - return null; - } - if (!mUserInfoHelper.isCurrentUserId(callerIdentity.getUserId())) { - registration.deliverLocation(null); - return null; - } - if (!request.isLocationSettingsIgnored() && !isEnabled(callerIdentity.getUserId())) { - registration.deliverLocation(null); - return null; - } - - Location lastLocation = getLastLocationUnsafe( - callerIdentity.getUserId(), - permissionLevel, - request.isLocationSettingsIgnored(), - MAX_CURRENT_LOCATION_AGE_MS); - if (lastLocation != null) { - registration.deliverLocation(lastLocation); - return null; - } - - // if last location isn't good enough then we add a location request - long identity = Binder.clearCallingIdentity(); + long ident = Binder.clearCallingIdentity(); try { addRegistration(callback.asBinder(), registration); + if (!registration.isActive()) { + // if the registration never activated, fail it immediately + registration.deliverNull(); + } } finally { - Binder.restoreCallingIdentity(identity); + Binder.restoreCallingIdentity(ident); } } ICancellationSignal cancelTransport = CancellationSignal.createTransport(); - CancellationSignal cancellationSignal = CancellationSignal.fromTransport(cancelTransport); - cancellationSignal.setOnCancelListener(SingleUseCallback.wrap( - () -> { - synchronized (mLock) { - removeRegistration(callback.asBinder(), registration); - } - })); + CancellationSignal.fromTransport(cancelTransport) + .setOnCancelListener(SingleUseCallback.wrap( + () -> { + synchronized (mLock) { + removeRegistration(callback.asBinder(), registration); + } + })); return cancelTransport; } @@ -1610,36 +1569,36 @@ class LocationProviderManager extends } } - public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity, + public void registerLocationRequest(LocationRequest request, CallerIdentity identity, @PermissionLevel int permissionLevel, ILocationListener listener) { + LocationListenerRegistration registration = new LocationListenerRegistration( + request, + identity, + new LocationListenerTransport(listener), + permissionLevel); + synchronized (mLock) { - long identity = Binder.clearCallingIdentity(); + long ident = Binder.clearCallingIdentity(); try { - addRegistration( - listener.asBinder(), - new LocationListenerRegistration( - request, - callerIdentity, - new LocationListenerTransport(listener), - permissionLevel)); + addRegistration(listener.asBinder(), registration); } finally { - Binder.restoreCallingIdentity(identity); + Binder.restoreCallingIdentity(ident); } } } public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity, @PermissionLevel int permissionLevel, PendingIntent pendingIntent) { + LocationPendingIntentRegistration registration = new LocationPendingIntentRegistration( + request, + callerIdentity, + new LocationPendingIntentTransport(mContext, pendingIntent), + permissionLevel); + synchronized (mLock) { long identity = Binder.clearCallingIdentity(); try { - addRegistration( - pendingIntent, - new LocationPendingIntentRegistration( - request, - callerIdentity, - new LocationPendingIntentTransport(mContext, pendingIntent), - permissionLevel)); + addRegistration(pendingIntent, registration); } finally { Binder.restoreCallingIdentity(identity); } @@ -1850,6 +1809,9 @@ class LocationProviderManager extends if (!isEnabled(identity.getUserId())) { return false; } + if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) { + return false; + } switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) { case LOCATION_MODE_FOREGROUND_ONLY: @@ -1887,7 +1849,7 @@ class LocationProviderManager extends Preconditions.checkState(Thread.holdsLock(mLock)); } - long intervalMs = Long.MAX_VALUE; + long intervalMs = INTERVAL_DISABLED; boolean locationSettingsIgnored = false; boolean lowPower = true; ArrayList<LocationRequest> locationRequests = new ArrayList<>(registrations.size()); @@ -1906,12 +1868,18 @@ class LocationProviderManager extends locationRequests.add(request); } + if (intervalMs == INTERVAL_DISABLED) { + return EMPTY_REQUEST; + } + // calculate who to blame for power in a somewhat arbitrary fashion. we pick a threshold // interval slightly higher that the minimum interval, and spread the blame across all // contributing registrations under that threshold (since worksource does not allow us to // represent differing power blame ratios). - long thresholdIntervalMs = (intervalMs + 1000) * 3 / 2; - if (thresholdIntervalMs < 0 || thresholdIntervalMs >= PASSIVE_INTERVAL) { + long thresholdIntervalMs; + try { + thresholdIntervalMs = Math.multiplyExact(Math.addExact(intervalMs, 1000) / 2, 3); + } catch (ArithmeticException e) { // check for and handle overflow by setting to one below the passive interval so passive // requests are automatically skipped thresholdIntervalMs = PASSIVE_INTERVAL - 1; @@ -1920,7 +1888,7 @@ class LocationProviderManager extends WorkSource workSource = new WorkSource(); for (Registration registration : registrations) { if (registration.getRequest().getIntervalMillis() <= thresholdIntervalMs) { - workSource.add(registration.getWorkSource()); + workSource.add(registration.getRequest().getWorkSource()); } } @@ -1953,7 +1921,7 @@ class LocationProviderManager extends // location for our calculations instead. this prevents spammy add/remove behavior last = getLastLocationUnsafe( registration.getIdentity().getUserId(), - PERMISSION_FINE, + registration.getPermissionLevel(), false, locationRequest.getIntervalMillis()); } @@ -1978,7 +1946,8 @@ class LocationProviderManager extends synchronized (mLock) { switch (change) { case UserListener.CURRENT_USER_CHANGED: - onEnabledChanged(userId); + updateRegistrations( + registration -> registration.getIdentity().getUserId() == userId); break; case UserListener.USER_STARTED: onUserStarted(userId); @@ -2159,13 +2128,10 @@ class LocationProviderManager extends } if (userId == UserHandle.USER_ALL) { - onEnabledChanged(UserHandle.USER_ALL); mEnabled.clear(); mLastLocations.clear(); } else { Preconditions.checkArgument(userId >= 0); - - onEnabledChanged(userId); mEnabled.delete(userId); mLastLocations.remove(userId); } @@ -2182,7 +2148,7 @@ class LocationProviderManager extends // settings for instance) do not support the null user return; } else if (userId == UserHandle.USER_ALL) { - final int[] runningUserIds = mUserInfoHelper.getRunningUserIds(); + final int[] runningUserIds = mUserHelper.getRunningUserIds(); for (int i = 0; i < runningUserIds.length; i++) { onEnabledChanged(runningUserIds[i]); } @@ -2193,7 +2159,6 @@ class LocationProviderManager extends boolean enabled = mStarted && mProvider.getState().allowed - && mUserInfoHelper.isCurrentUserId(userId) && mSettingsHelper.isLocationEnabled(userId); int index = mEnabled.indexOfKey(userId); @@ -2249,6 +2214,20 @@ class LocationProviderManager extends updateRegistrations(registration -> registration.getIdentity().getUserId() == userId); } + @Nullable + private Location getPermittedLocation(@Nullable Location fineLocation, + @PermissionLevel int permissionLevel) { + switch (permissionLevel) { + case PERMISSION_FINE: + return fineLocation; + case PERMISSION_COARSE: + return fineLocation != null ? mLocationFudger.createCoarse(fineLocation) : null; + default: + // shouldn't be possible to have a client added without location permissions + throw new AssertionError(); + } + } + public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { synchronized (mLock) { ipw.print(mName); @@ -2261,7 +2240,7 @@ class LocationProviderManager extends super.dump(fd, ipw, args); - int[] userIds = mUserInfoHelper.getRunningUserIds(); + int[] userIds = mUserHelper.getRunningUserIds(); for (int userId : userIds) { if (userIds.length != 1) { ipw.print("user "); @@ -2299,10 +2278,14 @@ class LocationProviderManager extends public void clearMock() { if (mFineLocation != null && mFineLocation.isFromMockProvider()) { mFineLocation = null; + } + if (mCoarseLocation != null && mCoarseLocation.isFromMockProvider()) { mCoarseLocation = null; } if (mFineBypassLocation != null && mFineBypassLocation.isFromMockProvider()) { mFineBypassLocation = null; + } + if (mCoarseBypassLocation != null && mCoarseBypassLocation.isFromMockProvider()) { mCoarseBypassLocation = null; } } @@ -2333,14 +2316,14 @@ class LocationProviderManager extends } } - public void set(Location fineLocation, Location coarseLocation) { - mFineLocation = calculateNextFine(mFineLocation, fineLocation); - mCoarseLocation = calculateNextCoarse(mCoarseLocation, coarseLocation); + public void set(Location location) { + mFineLocation = calculateNextFine(mFineLocation, location); + mCoarseLocation = calculateNextCoarse(mCoarseLocation, location); } - public void setBypass(Location fineLocation, Location coarseLocation) { - mFineBypassLocation = calculateNextFine(mFineBypassLocation, fineLocation); - mCoarseBypassLocation = calculateNextCoarse(mCoarseBypassLocation, coarseLocation); + public void setBypass(Location location) { + mFineBypassLocation = calculateNextFine(mFineBypassLocation, location); + mCoarseBypassLocation = calculateNextCoarse(mCoarseBypassLocation, location); } private Location calculateNextFine(@Nullable Location oldFine, Location newFine) { @@ -2362,8 +2345,8 @@ class LocationProviderManager extends } // update last coarse interval only if enough time has passed - if (newCoarse.getElapsedRealtimeNanos() - MIN_COARSE_INTERVAL_MS - > oldCoarse.getElapsedRealtimeNanos()) { + if (newCoarse.getElapsedRealtimeMillis() - MIN_COARSE_INTERVAL_MS + > oldCoarse.getElapsedRealtimeMillis()) { return newCoarse; } else { return oldCoarse; diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java index 58e6d593ed2d..baa01b1506af 100644 --- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java +++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java @@ -327,7 +327,7 @@ public class GeofenceManager extends protected boolean isActive(GeofenceRegistration registration) { CallerIdentity identity = registration.getIdentity(); return registration.isPermitted() - && mUserInfoHelper.isCurrentUserId(identity.getUserId()) + && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId())) && mSettingsHelper.isLocationEnabled(identity.getUserId()) && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), identity.getPackageName()); diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java index e782d5ec436d..1cdb118f5787 100644 --- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java @@ -259,7 +259,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter CallerIdentity identity = registration.getIdentity(); return registration.isPermitted() && (registration.isForeground() || isBackgroundRestrictionExempt(identity)) - && mUserInfoHelper.isCurrentUserId(identity.getUserId()) + && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId())) && mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER, identity.getUserId()) && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java index 99414c4cdf91..d48263722d38 100644 --- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java +++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java @@ -44,16 +44,22 @@ import java.util.List; import java.util.Objects; /** - * A real implementation of {@link LocationTimeZoneProviderController} that supports a single - * {@link LocationTimeZoneProvider}. + * A real implementation of {@link LocationTimeZoneProviderController} that supports a primary and a + * secondary {@link LocationTimeZoneProvider}. * - * TODO(b/152744911): This implementation currently only supports a single ("primary") provider. - * Support for a secondary provider will be added in a later commit. + * <p>The primary is used until it fails or becomes uncertain. The secondary will then be enabled. + * The controller will immediately make suggestions based on "certain" {@link + * LocationTimeZoneEvent}s, i.e. events that demonstrate the provider is certain what the time zone + * is. The controller will not make immediate suggestions based on "uncertain" events, giving + * providers time to change their mind. This also gives the secondary provider time to initialize + * when the primary becomes uncertain. */ class ControllerImpl extends LocationTimeZoneProviderController { @NonNull private final LocationTimeZoneProvider mPrimaryProvider; + @NonNull private final LocationTimeZoneProvider mSecondaryProvider; + @GuardedBy("mSharedLock") // Non-null after initialize() private ConfigurationInternal mCurrentUserConfiguration; @@ -67,7 +73,9 @@ class ControllerImpl extends LocationTimeZoneProviderController { private Callback mCallback; /** - * Used for scheduling uncertainty timeouts, i.e after the provider has reported uncertainty. + * Used for scheduling uncertainty timeouts, i.e after a provider has reported uncertainty. + * This timeout is not provider-specific: it is started when the controller becomes uncertain + * due to events it has received from one or other provider. */ @NonNull private final SingleRunnableQueue mUncertaintyTimeoutQueue; @@ -77,10 +85,12 @@ class ControllerImpl extends LocationTimeZoneProviderController { private GeolocationTimeZoneSuggestion mLastSuggestion; ControllerImpl(@NonNull ThreadingDomain threadingDomain, - @NonNull LocationTimeZoneProvider primaryProvider) { + @NonNull LocationTimeZoneProvider primaryProvider, + @NonNull LocationTimeZoneProvider secondaryProvider) { super(threadingDomain); mUncertaintyTimeoutQueue = threadingDomain.createSingleRunnableQueue(); mPrimaryProvider = Objects.requireNonNull(primaryProvider); + mSecondaryProvider = Objects.requireNonNull(secondaryProvider); } @Override @@ -96,8 +106,9 @@ class ControllerImpl extends LocationTimeZoneProviderController { LocationTimeZoneProvider.ProviderListener providerListener = ControllerImpl.this::onProviderStateChange; mPrimaryProvider.initialize(providerListener); + mSecondaryProvider.initialize(providerListener); - alterProviderEnabledStateIfRequired( + alterProvidersEnabledStateIfRequired( null /* oldConfiguration */, mCurrentUserConfiguration); } } @@ -115,15 +126,15 @@ class ControllerImpl extends LocationTimeZoneProviderController { if (!newConfig.equals(oldConfig)) { if (newConfig.getUserId() != oldConfig.getUserId()) { - // If the user changed, disable the provider if needed. It may be re-enabled for - // the new user below if their settings allow. + // If the user changed, disable the providers if needed. They may be re-enabled + // for the new user immediately afterwards if their settings allow. debugLog("User changed. old=" + oldConfig.getUserId() - + ", new=" + newConfig.getUserId() + ": Disabling provider"); - disableProvider(); + + ", new=" + newConfig.getUserId() + ": Disabling providers"); + disableProviders(); - alterProviderEnabledStateIfRequired(null /* oldConfiguration */, newConfig); + alterProvidersEnabledStateIfRequired(null /* oldConfiguration */, newConfig); } else { - alterProviderEnabledStateIfRequired(oldConfig, newConfig); + alterProvidersEnabledStateIfRequired(oldConfig, newConfig); } } } @@ -140,10 +151,11 @@ class ControllerImpl extends LocationTimeZoneProviderController { } @GuardedBy("mSharedLock") - private void disableProvider() { + private void disableProviders() { disableProviderIfEnabled(mPrimaryProvider); + disableProviderIfEnabled(mSecondaryProvider); - // By definition, if the provider is disabled, the controller is uncertain. + // By definition, if both providers are disabled, the controller is uncertain. cancelUncertaintyTimeout(); } @@ -181,7 +193,7 @@ class ControllerImpl extends LocationTimeZoneProviderController { } /** - * Sets the provider into the correct enabled/disabled state for the {@code newConfiguration} + * Sets the providers into the correct enabled/disabled state for the {@code newConfiguration} * and, if there is a provider state change, makes any suggestions required to inform the * downstream time zone detection code. * @@ -190,7 +202,7 @@ class ControllerImpl extends LocationTimeZoneProviderController { * or when a new configuration has been received. */ @GuardedBy("mSharedLock") - private void alterProviderEnabledStateIfRequired( + private void alterProvidersEnabledStateIfRequired( @Nullable ConfigurationInternal oldConfiguration, @NonNull ConfigurationInternal newConfiguration) { @@ -203,21 +215,40 @@ class ControllerImpl extends LocationTimeZoneProviderController { return; } + // The check above ensures that the logic below only executes if providers are going from + // {enabled *} -> {disabled}, or {disabled} -> {enabled initializing}. If this changes in + // future and there could be {enabled *} -> {enabled *} cases, or cases where the provider + // can't be assumed to go straight to the {enabled initializing} state, then the logic below + // would need to cover extra conditions, for example: + // 1) If the primary is in {enabled uncertain}, the secondary should be enabled. + // 2) If (1), and the secondary instantly enters the {perm failed} state, the uncertainty + // timeout started when the primary entered {enabled uncertain} should be cancelled. + if (newGeoDetectionEnabled) { // Try to enable the primary provider. tryEnableProvider(mPrimaryProvider, newConfiguration); + // The secondary should only ever be enabled if the primary now isn't enabled (i.e. it + // couldn't become {enabled initializing} because it is {perm failed}). ProviderState newPrimaryState = mPrimaryProvider.getCurrentState(); if (!newPrimaryState.isEnabled()) { - // If the provider is perm failed then the controller is immediately considered - // uncertain. - GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( - "Provider is failed:" - + " primary=" + mPrimaryProvider.getCurrentState()); - makeSuggestion(suggestion); + // If the primary provider is {perm failed} then the controller must try to enable + // the secondary. + tryEnableProvider(mSecondaryProvider, newConfiguration); + + ProviderState newSecondaryState = mSecondaryProvider.getCurrentState(); + if (!newSecondaryState.isEnabled()) { + // If both providers are {perm failed} then the controller immediately + // becomes uncertain. + GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( + "Providers are failed:" + + " primary=" + mPrimaryProvider.getCurrentState() + + " secondary=" + mPrimaryProvider.getCurrentState()); + makeSuggestion(suggestion); + } } } else { - disableProvider(); + disableProviders(); // There can be an uncertainty timeout set if the controller most recently received // an uncertain event. This is a no-op if there isn't a timeout set. @@ -300,35 +331,63 @@ class ControllerImpl extends LocationTimeZoneProviderController { } private void assertProviderKnown(@NonNull LocationTimeZoneProvider provider) { - if (provider != mPrimaryProvider) { + if (provider != mPrimaryProvider && provider != mSecondaryProvider) { throw new IllegalArgumentException("Unknown provider: " + provider); } } /** - * Called when the provider has reported that it has failed permanently. + * Called when a provider has reported that it has failed permanently. */ @GuardedBy("mSharedLock") private void handleProviderFailedStateChange(@NonNull ProviderState providerState) { LocationTimeZoneProvider failedProvider = providerState.provider; + ProviderState primaryCurrentState = mPrimaryProvider.getCurrentState(); + ProviderState secondaryCurrentState = mSecondaryProvider.getCurrentState(); + + // If a provider has failed, the other may need to be enabled. + if (failedProvider == mPrimaryProvider) { + if (secondaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) { + // The primary must have failed. Try to enable the secondary. This does nothing if + // the provider is already enabled, and will leave the provider in + // {enabled initializing} if the provider is disabled. + tryEnableProvider(mSecondaryProvider, mCurrentUserConfiguration); + } + } else if (failedProvider == mSecondaryProvider) { + // No-op: The secondary will only be active if the primary is uncertain or is failed. + // So, there the primary should not need to be enabled when the secondary fails. + if (primaryCurrentState.stateEnum != PROVIDER_STATE_ENABLED_UNCERTAIN + && primaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) { + warnLog("Secondary provider unexpected reported a failure:" + + " failed provider=" + failedProvider.getName() + + ", primary provider=" + mPrimaryProvider + + ", secondary provider=" + mSecondaryProvider); + } + } - // If the provider is newly perm failed then the controller is uncertain by - // definition. - cancelUncertaintyTimeout(); + // If both providers are now failed, the controller needs to tell the next component in the + // time zone detection process. + if (primaryCurrentState.stateEnum == PROVIDER_STATE_PERM_FAILED + && secondaryCurrentState.stateEnum == PROVIDER_STATE_PERM_FAILED) { - // If the provider is now failed, then we must send a suggestion informing the time - // zone detector that there are no further updates coming in future. + // If both providers are newly failed then the controller is uncertain by definition + // and it will never recover so it can send a suggestion immediately. + cancelUncertaintyTimeout(); - GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( - "The provider is permanently failed:" - + " provider=" + failedProvider); - makeSuggestion(suggestion); + // If both providers are now failed, then a suggestion must be sent informing the time + // zone detector that there are no further updates coming in future. + GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( + "Both providers are permanently failed:" + + " primary=" + primaryCurrentState.provider + + ", secondary=" + secondaryCurrentState.provider); + makeSuggestion(suggestion); + } } /** * Called when a provider has changed state but just moved from one enabled state to another * enabled state, usually as a result of a new {@link LocationTimeZoneEvent} being received. - * However, there are rare cases where the event can be null. + * However, there are rare cases where the event can also be null. */ @GuardedBy("mSharedLock") private void handleProviderEnabledStateChange(@NonNull ProviderState providerState) { @@ -395,6 +454,10 @@ class ControllerImpl extends LocationTimeZoneProviderController { // By definition, the controller is now certain. cancelUncertaintyTimeout(); + if (provider == mPrimaryProvider) { + disableProviderIfEnabled(mSecondaryProvider); + } + GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(timeZoneIds); suggestion.addDebugInfo(reason); @@ -421,6 +484,11 @@ class ControllerImpl extends LocationTimeZoneProviderController { mPrimaryProvider.dump(ipw, args); ipw.decreaseIndent(); // level 2 + ipw.println("Secondary Provider:"); + ipw.increaseIndent(); // level 2 + mSecondaryProvider.dump(ipw, args); + ipw.decreaseIndent(); // level 2 + ipw.decreaseIndent(); // level 1 } } @@ -470,6 +538,14 @@ class ControllerImpl extends LocationTimeZoneProviderController { mUncertaintyTimeoutQueue.runDelayed(() -> onProviderUncertaintyTimeout(provider), delay.toMillis()); } + + if (provider == mPrimaryProvider) { + // (Try to) enable the secondary. It could already be enabled, or enabling might not + // succeed if the provider has previously reported it is perm failed. The uncertainty + // timeout (set above) is used to ensure that an uncertain suggestion will be made if + // the secondary cannot generate a success event in time. + tryEnableProvider(mSecondaryProvider, mCurrentUserConfiguration); + } } private void onProviderUncertaintyTimeout(@NonNull LocationTimeZoneProvider provider) { @@ -478,7 +554,8 @@ class ControllerImpl extends LocationTimeZoneProviderController { synchronized (mSharedLock) { GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion( "Uncertainty timeout triggered for " + provider.getName() + ":" - + " primary=" + mPrimaryProvider); + + " primary=" + mPrimaryProvider + + ", secondary=" + mSecondaryProvider); makeSuggestion(suggestion); } } @@ -498,6 +575,8 @@ class ControllerImpl extends LocationTimeZoneProviderController { LocationTimeZoneProvider targetProvider; if (Objects.equals(mPrimaryProvider.getName(), targetProviderName)) { targetProvider = mPrimaryProvider; + } else if (Objects.equals(mSecondaryProvider.getName(), targetProviderName)) { + targetProvider = mSecondaryProvider; } else { warnLog("Unable to process simulated binder provider event," + " unknown providerName in event=" + event); diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java index 97d3b826f3ff..a817759c9783 100644 --- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java +++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java @@ -44,8 +44,8 @@ import java.util.Objects; * are made to the {@link TimeZoneDetectorInternal}, and the {@link LocationTimeZoneProvider}s that * offer {@link android.location.timezone.LocationTimeZoneEvent}s. * - * TODO(b/152744911): This implementation currently only supports a primary provider. Support for a - * secondary provider must be added in a later commit. + * <p>For details of the time zone suggestion behavior, see {@link + * LocationTimeZoneProviderController}. * * <p>Implementation details: * @@ -109,6 +109,7 @@ public class LocationTimeZoneManagerService extends Binder { static final String TAG = "LocationTZDetector"; static final String PRIMARY_PROVIDER_NAME = "primary"; + static final String SECONDARY_PROVIDER_NAME = "secondary"; private static final String SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX = "persist.sys.location_tz_simulation_mode."; @@ -117,6 +118,8 @@ public class LocationTimeZoneManagerService extends Binder { private static final String PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION = "com.android.location.timezone.service.v1.PrimaryLocationTimeZoneProvider"; + private static final String SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION = + "com.android.location.timezone.service.v1.SecondaryLocationTimeZoneProvider"; @NonNull private final Context mContext; @@ -160,8 +163,9 @@ public class LocationTimeZoneManagerService extends Binder { // Called on an arbitrary thread during initialization. synchronized (mSharedLock) { LocationTimeZoneProvider primary = createPrimaryProvider(); + LocationTimeZoneProvider secondary = createSecondaryProvider(); mLocationTimeZoneDetectorController = - new ControllerImpl(mThreadingDomain, primary); + new ControllerImpl(mThreadingDomain, primary, secondary); ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain); ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl( mThreadingDomain, mLocationTimeZoneDetectorController); @@ -189,6 +193,23 @@ public class LocationTimeZoneManagerService extends Binder { return createLocationTimeZoneProvider(PRIMARY_PROVIDER_NAME, proxy); } + private LocationTimeZoneProvider createSecondaryProvider() { + LocationTimeZoneProviderProxy proxy; + if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) { + proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); + } else { + proxy = RealLocationTimeZoneProviderProxy.createAndRegister( + mContext, + mThreadingDomain, + SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION, + com.android.internal.R.bool.config_enableSecondaryLocationTimeZoneOverlay, + com.android.internal.R.string + .config_secondaryLocationTimeZoneProviderPackageName + ); + } + return createLocationTimeZoneProvider(SECONDARY_PROVIDER_NAME, proxy); + } + private boolean isInSimulationMode(String providerName) { return SystemProperties.getBoolean( SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + providerName, false); diff --git a/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java index ef2e349fceaa..f1d37237872b 100644 --- a/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java +++ b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java @@ -21,6 +21,7 @@ import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN; import static com.android.server.location.timezone.LocationTimeZoneManagerService.PRIMARY_PROVIDER_NAME; +import static com.android.server.location.timezone.LocationTimeZoneManagerService.SECONDARY_PROVIDER_NAME; import android.annotation.NonNull; import android.annotation.Nullable; @@ -42,7 +43,8 @@ import java.util.Objects; */ final class SimulatedBinderProviderEvent { - private static final List<String> VALID_PROVIDER_NAMES = Arrays.asList(PRIMARY_PROVIDER_NAME); + private static final List<String> VALID_PROVIDER_NAMES = + Arrays.asList(PRIMARY_PROVIDER_NAME, SECONDARY_PROVIDER_NAME); static final int INJECTED_EVENT_TYPE_ON_BIND = 1; static final int INJECTED_EVENT_TYPE_ON_UNBIND = 2; diff --git a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java index d85ca5e730b0..6e4cf064b440 100644 --- a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java +++ b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java @@ -16,7 +16,13 @@ package com.android.server.location.util; +import static android.os.PowerManager.locationPowerSaveModeToString; + +import static com.android.server.location.LocationManagerService.D; +import static com.android.server.location.LocationManagerService.TAG; + import android.os.PowerManager.LocationPowerSaveMode; +import android.util.Log; import java.util.concurrent.CopyOnWriteArrayList; @@ -60,7 +66,12 @@ public abstract class LocationPowerSaveModeHelper { protected final void notifyLocationPowerSaveModeChanged( @LocationPowerSaveMode int locationPowerSaveMode) { + if (D) { + Log.d(TAG, "location power save mode is now " + locationPowerSaveModeToString( + locationPowerSaveMode)); + } mLocationEventLog.logLocationPowerSaveMode(locationPowerSaveMode); + for (LocationPowerSaveModeChangedListener listener : mListeners) { listener.onLocationPowerSaveModeChanged(locationPowerSaveMode); } diff --git a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java index d47bce31ed23..ecd6966c1493 100644 --- a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java +++ b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java @@ -16,6 +16,11 @@ package com.android.server.location.util; +import static com.android.server.location.LocationManagerService.D; +import static com.android.server.location.LocationManagerService.TAG; + +import android.util.Log; + import java.util.concurrent.CopyOnWriteArrayList; /** @@ -55,6 +60,10 @@ public abstract class ScreenInteractiveHelper { } protected final void notifyScreenInteractiveChanged(boolean interactive) { + if (D) { + Log.d(TAG, "screen interactive is now " + interactive); + } + for (ScreenInteractiveChangedListener listener : mListeners) { listener.onScreenInteractiveChanged(interactive); } diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java index 7c1c1c7ce403..5a8fbf3d26b5 100644 --- a/services/core/java/com/android/server/net/NetworkStatsAccess.java +++ b/services/core/java/com/android/server/net/NetworkStatsAccess.java @@ -24,7 +24,6 @@ import static android.net.TrafficStats.UID_TETHERING; import android.Manifest; import android.annotation.IntDef; import android.app.AppOpsManager; -import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManagerInternal; import android.content.Context; import android.content.pm.PackageManager; @@ -111,8 +110,7 @@ public final class NetworkStatsAccess { boolean hasCarrierPrivileges = tm != null && tm.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; - boolean isDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid, - DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + boolean isDeviceOwner = dpmi != null && dpmi.isActiveDeviceOwner(callingUid); if (hasCarrierPrivileges || isDeviceOwner || UserHandle.getAppId(callingUid) == android.os.Process.SYSTEM_UID) { // Carrier-privileged apps and device owners, and the system can access data usage for @@ -126,8 +124,9 @@ public final class NetworkStatsAccess { return NetworkStatsAccess.Level.DEVICESUMMARY; } - boolean isProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode. + boolean isProfileOwner = dpmi != null && (dpmi.isActiveProfileOwner(callingUid) + || dpmi.isActiveDeviceOwner(callingUid)); if (isProfileOwner) { // Apps with the AppOps permission, profile owners, and apps with the privileged // permission can access data usage for all apps in this user/profile. diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java index a4a94c29abfa..25ad9280fa99 100644 --- a/services/core/java/com/android/server/notification/EventConditionProvider.java +++ b/services/core/java/com/android/server/notification/EventConditionProvider.java @@ -271,7 +271,7 @@ public class EventConditionProvider extends SystemConditionProviderService { new Intent(ACTION_EVALUATE) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) .putExtra(EXTRA_TIME, time), - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); alarms.cancel(pendingIntent); if (time == 0 || time < now) { if (DEBUG) Slog.d(TAG, "Not scheduling evaluate: " + (time == 0 ? "no time specified" diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b4c98e06a442..cab33770c0fc 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -137,7 +137,6 @@ import android.app.PendingIntent; import android.app.StatsManager; import android.app.StatusBarManager; import android.app.UriGrantsManager; -import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManagerInternal; import android.app.backup.BackupManager; import android.app.role.OnRoleHoldersChangedListener; @@ -3084,6 +3083,8 @@ public class NotificationManagerService extends SystemService { UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null); } + mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, + enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); try { getContext().sendBroadcastAsUser( new Intent(ACTION_APP_BLOCK_STATE_CHANGED) @@ -4541,11 +4542,11 @@ public class NotificationManagerService extends SystemService { } catch (NameNotFoundException e) { return false; } + //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode. return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg) - || (mDpm != null && - mDpm.isActiveAdminWithPolicy(Binder.getCallingUid(), - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)); + || (mDpm != null && (mDpm.isActiveProfileOwner(Binder.getCallingUid()) + || mDpm.isActiveDeviceOwner(Binder.getCallingUid()))); } @Override @@ -5247,7 +5248,8 @@ public class NotificationManagerService extends SystemService { Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg); if (appIntent != null) { summaryNotification.contentIntent = PendingIntent.getActivityAsUser( - getContext(), 0, appIntent, 0, null, UserHandle.of(userId)); + getContext(), 0, appIntent, PendingIntent.FLAG_IMMUTABLE, null, + UserHandle.of(userId)); } final StatusBarNotification summarySbn = new StatusBarNotification(adjustedSbn.getPackageName(), @@ -6830,7 +6832,7 @@ public class NotificationManagerService extends SystemService { .appendPath(record.getKey()).build()) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) .putExtra(EXTRA_KEY, record.getKey()), - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi); } diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java index 961d3db867e9..35170332a077 100644 --- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java +++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java @@ -223,7 +223,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { new Intent(ACTION_EVALUATE) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) .putExtra(EXTRA_TIME, time), - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); alarms.cancel(pendingIntent); if (time > now) { if (DEBUG) Slog.d(TAG, String.format("Scheduling evaluate for %s, in %s, now=%s", diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java index 8640dbcab33c..7e8ff9499ff8 100644 --- a/services/core/java/com/android/server/pm/ApkChecksums.java +++ b/services/core/java/com/android/server/pm/ApkChecksums.java @@ -16,13 +16,13 @@ package com.android.server.pm; -import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA256; -import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA512; -import static android.content.pm.Checksum.WHOLE_MD5; -import static android.content.pm.Checksum.WHOLE_MERKLE_ROOT_4K_SHA256; -import static android.content.pm.Checksum.WHOLE_SHA1; -import static android.content.pm.Checksum.WHOLE_SHA256; -import static android.content.pm.Checksum.WHOLE_SHA512; +import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256; +import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512; +import static android.content.pm.Checksum.TYPE_WHOLE_MD5; +import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256; +import static android.content.pm.Checksum.TYPE_WHOLE_SHA1; +import static android.content.pm.Checksum.TYPE_WHOLE_SHA256; +import static android.content.pm.Checksum.TYPE_WHOLE_SHA512; import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; import static android.content.pm.PackageParser.APK_FILE_EXTENSION; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256; @@ -182,13 +182,13 @@ public class ApkChecksums { dos.writeUTF(splitName); } - dos.writeInt(checksum.getKind()); + dos.writeInt(checksum.getType()); final byte[] valueBytes = checksum.getValue(); dos.writeInt(valueBytes.length); dos.write(valueBytes); - final String packageName = checksum.getSourcePackageName(); + final String packageName = checksum.getInstallerPackageName(); if (packageName == null) { dos.writeInt(-1); } else { @@ -196,7 +196,7 @@ public class ApkChecksums { dos.writeUTF(packageName); } - final Certificate cert = checksum.getSourceCertificate(); + final Certificate cert = checksum.getInstallerCertificate(); final byte[] certBytes = (cert == null) ? null : cert.getEncoded(); if (certBytes == null) { dos.writeInt(-1); @@ -225,7 +225,7 @@ public class ApkChecksums { splitName = dis.readUTF(); } - final int kind = dis.readInt(); + final int type = dis.readInt(); final byte[] valueBytes = new byte[dis.readInt()]; dis.read(valueBytes); @@ -245,7 +245,7 @@ public class ApkChecksums { certBytes = new byte[certBytesLength]; dis.read(certBytes); } - checksums[i] = new ApkChecksum(splitName, new Checksum(kind, valueBytes), + checksums[i] = new ApkChecksum(splitName, new Checksum(type, valueBytes), packageName, certBytes); } return checksums; @@ -265,8 +265,8 @@ public class ApkChecksums { * @param statusReceiver to receive the resulting checksums */ public static void getChecksums(List<Pair<String, File>> filesToChecksum, - @Checksum.Kind int optional, - @Checksum.Kind int required, + @Checksum.Type int optional, + @Checksum.Type int required, @Nullable Certificate[] trustedInstallers, @NonNull IntentSender statusReceiver, @NonNull Injector injector) { @@ -292,7 +292,7 @@ public class ApkChecksums { private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum, List<Map<Integer, ApkChecksum>> result, - @Checksum.Kind int required, + @Checksum.Type int required, @NonNull IntentSender statusReceiver, @NonNull Injector injector, long startTime) { @@ -339,32 +339,32 @@ public class ApkChecksums { * * @param split split name, null for base * @param file to fetch checksums for - * @param kinds mask to fetch checksums + * @param types mask to fetch checksums * @param trustedInstallers array of certificate to trust, two specific cases: * null - trust anybody, * [] - trust nobody. * @param checksums resulting checksums */ private static void getAvailableApkChecksums(String split, File file, - @Checksum.Kind int kinds, + @Checksum.Type int types, @Nullable Certificate[] trustedInstallers, Map<Integer, ApkChecksum> checksums) { final String filePath = file.getAbsolutePath(); // Always available: FSI or IncFs. - if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) { + if (isRequired(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, types, checksums)) { // Hashes in fs-verity and IncFS are always verified. ApkChecksum checksum = extractHashFromFS(split, filePath); if (checksum != null) { - checksums.put(checksum.getKind(), checksum); + checksums.put(checksum.getType(), checksum); } } // System enforced: v2/v3. - if (isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) || isRequired( - PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) { + if (isRequired(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, types, checksums) || isRequired( + TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, types, checksums)) { Map<Integer, ApkChecksum> v2v3checksums = extractHashFromV2V3Signature( - split, filePath, kinds); + split, filePath, types); if (v2v3checksums != null) { checksums.putAll(v2v3checksums); } @@ -377,9 +377,9 @@ public class ApkChecksums { final ApkChecksum[] digests = readChecksums(digestsFile); final Set<Signature> trusted = convertToSet(trustedInstallers); for (ApkChecksum digest : digests) { - if (isRequired(digest.getKind(), kinds, checksums) && isTrusted(digest, + if (isRequired(digest.getType(), types, checksums) && isTrusted(digest, trusted)) { - checksums.put(digest.getKind(), digest); + checksums.put(digest.getType(), digest); } } } catch (IOException e) { @@ -395,16 +395,16 @@ public class ApkChecksums { * Whether the file is available for checksumming or we need to wait. */ private static boolean needToWait(File file, - @Checksum.Kind int kinds, + @Checksum.Type int types, Map<Integer, ApkChecksum> checksums, @NonNull Injector injector) throws IOException { - if (!isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums) - && !isRequired(WHOLE_MD5, kinds, checksums) - && !isRequired(WHOLE_SHA1, kinds, checksums) - && !isRequired(WHOLE_SHA256, kinds, checksums) - && !isRequired(WHOLE_SHA512, kinds, checksums) - && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) - && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) { + if (!isRequired(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, types, checksums) + && !isRequired(TYPE_WHOLE_MD5, types, checksums) + && !isRequired(TYPE_WHOLE_SHA1, types, checksums) + && !isRequired(TYPE_WHOLE_SHA256, types, checksums) + && !isRequired(TYPE_WHOLE_SHA512, types, checksums) + && !isRequired(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, types, checksums) + && !isRequired(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, types, checksums)) { return false; } @@ -432,16 +432,16 @@ public class ApkChecksums { * * @param split split name, null for base * @param file to fetch checksums for - * @param kinds mask to forcefully calculate if not available + * @param types mask to forcefully calculate if not available * @param checksums resulting checksums */ private static void getRequiredApkChecksums(String split, File file, - @Checksum.Kind int kinds, + @Checksum.Type int types, Map<Integer, ApkChecksum> checksums) { final String filePath = file.getAbsolutePath(); // Manually calculating required checksums if not readily available. - if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) { + if (isRequired(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, types, checksums)) { try { byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash( filePath, /*salt=*/null, @@ -451,27 +451,28 @@ public class ApkChecksums { return ByteBuffer.allocate(capacity); } }); - checksums.put(WHOLE_MERKLE_ROOT_4K_SHA256, - new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, generatedRootHash)); + checksums.put(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, + new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, + generatedRootHash)); } catch (IOException | NoSuchAlgorithmException | DigestException e) { Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e); } } - calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_MD5); - calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA1); - calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA256); - calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA512); + calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_MD5); + calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_SHA1); + calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_SHA256); + calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_SHA512); - calculatePartialChecksumsIfRequested(checksums, split, file, kinds); + calculatePartialChecksumsIfRequested(checksums, split, file, types); } - private static boolean isRequired(@Checksum.Kind int kind, - @Checksum.Kind int kinds, Map<Integer, ApkChecksum> checksums) { - if ((kinds & kind) == 0) { + private static boolean isRequired(@Checksum.Type int type, + @Checksum.Type int types, Map<Integer, ApkChecksum> checksums) { + if ((types & type) == 0) { return false; } - if (checksums.containsKey(kind)) { + if (checksums.containsKey(type)) { return false; } return true; @@ -497,7 +498,7 @@ public class ApkChecksums { if (trusted == null) { return true; } - final Signature signature = new Signature(checksum.getSourceCertificateBytes()); + final Signature signature = new Signature(checksum.getInstallerCertificateBytes()); return trusted.contains(signature); } @@ -506,7 +507,7 @@ public class ApkChecksums { { byte[] hash = VerityUtils.getFsverityRootHash(filePath); if (hash != null) { - return new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash); + return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash); } } // v4 next @@ -516,7 +517,7 @@ public class ApkChecksums { byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256, null); if (hash != null) { - return new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash); + return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash); } } catch (SignatureNotFoundException e) { // Nothing @@ -527,7 +528,7 @@ public class ApkChecksums { } private static Map<Integer, ApkChecksum> extractHashFromV2V3Signature( - String split, String filePath, int kinds) { + String split, String filePath, int types) { Map<Integer, byte[]> contentDigests = null; try { contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath, @@ -544,56 +545,56 @@ public class ApkChecksums { } Map<Integer, ApkChecksum> checksums = new ArrayMap<>(); - if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) { + if ((types & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) { byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA256, null); if (hash != null) { - checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA256, - new ApkChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA256, hash)); + checksums.put(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, + new ApkChecksum(split, TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, hash)); } } - if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) { + if ((types & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) { byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA512, null); if (hash != null) { - checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA512, - new ApkChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA512, hash)); + checksums.put(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, + new ApkChecksum(split, TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, hash)); } } return checksums; } - private static String getMessageDigestAlgoForChecksumKind(int kind) + private static String getMessageDigestAlgoForChecksumKind(int type) throws NoSuchAlgorithmException { - switch (kind) { - case WHOLE_MD5: + switch (type) { + case TYPE_WHOLE_MD5: return ALGO_MD5; - case WHOLE_SHA1: + case TYPE_WHOLE_SHA1: return ALGO_SHA1; - case WHOLE_SHA256: + case TYPE_WHOLE_SHA256: return ALGO_SHA256; - case WHOLE_SHA512: + case TYPE_WHOLE_SHA512: return ALGO_SHA512; default: - throw new NoSuchAlgorithmException("Invalid checksum kind: " + kind); + throw new NoSuchAlgorithmException("Invalid checksum type: " + type); } } private static void calculateChecksumIfRequested(Map<Integer, ApkChecksum> checksums, - String split, File file, int required, int kind) { - if ((required & kind) != 0 && !checksums.containsKey(kind)) { - final byte[] checksum = getApkChecksum(file, kind); + String split, File file, int required, int type) { + if ((required & type) != 0 && !checksums.containsKey(type)) { + final byte[] checksum = getApkChecksum(file, type); if (checksum != null) { - checksums.put(kind, new ApkChecksum(split, kind, checksum)); + checksums.put(type, new ApkChecksum(split, type, checksum)); } } } - private static byte[] getApkChecksum(File file, int kind) { + private static byte[] getApkChecksum(File file, int type) { try (FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis)) { byte[] dataBytes = new byte[512 * 1024]; int nread = 0; - final String algo = getMessageDigestAlgoForChecksumKind(kind); + final String algo = getMessageDigestAlgoForChecksumKind(type); MessageDigest md = MessageDigest.getInstance(algo); while ((nread = bis.read(dataBytes)) != -1) { md.update(dataBytes, 0, nread); @@ -624,9 +625,9 @@ public class ApkChecksums { private static int getChecksumKindForContentDigestAlgo(int contentDigestAlgo) { switch (contentDigestAlgo) { case CONTENT_DIGEST_CHUNKED_SHA256: - return PARTIAL_MERKLE_ROOT_1M_SHA256; + return TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256; case CONTENT_DIGEST_CHUNKED_SHA512: - return PARTIAL_MERKLE_ROOT_1M_SHA512; + return TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512; default: return -1; } @@ -635,11 +636,11 @@ public class ApkChecksums { private static void calculatePartialChecksumsIfRequested(Map<Integer, ApkChecksum> checksums, String split, File file, int required) { boolean needSignatureSha256 = - (required & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey( - PARTIAL_MERKLE_ROOT_1M_SHA256); + (required & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey( + TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256); boolean needSignatureSha512 = - (required & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey( - PARTIAL_MERKLE_ROOT_1M_SHA512); + (required & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey( + TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512); if (!needSignatureSha256 && !needSignatureSha512) { return; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 2aafe9a6f9a1..37fa9a2e19db 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -28,6 +28,8 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE; import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; +import static android.content.pm.PackageManager.INSTALL_STAGED; +import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.PackageParser.APEX_FILE_EXTENSION; import static android.content.pm.PackageParser.APK_FILE_EXTENSION; import static android.system.OsConstants.O_CREAT; @@ -1458,7 +1460,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mChildSessionsRemaining.removeAt(sessionIndex); if (mChildSessionsRemaining.size() == 0) { destroyInternal(); - dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, + dispatchSessionFinished(INSTALL_SUCCEEDED, "Session installed", null); } } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) { @@ -1533,7 +1535,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); - assertPreparedAndNotDestroyedLocked("commit"); + assertPreparedAndNotDestroyedLocked("commit of session " + sessionId); assertNoWriteFileTransfersOpenLocked(); final boolean isSecureFrpEnabled = @@ -1663,7 +1665,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throws PackageManagerException { try { assertNoWriteFileTransfersOpenLocked(); - assertPreparedAndNotDestroyedLocked("sealing of session"); + assertPreparedAndNotDestroyedLocked("sealing of session " + sessionId); mSealed = true; } catch (Throwable e) { // Convert all exceptions into package manager exceptions as only those are handled @@ -1701,6 +1703,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + private void onSessionInstallationFailure(int error, String detailedMessage) { + Slog.e(TAG, "Install of session " + sessionId + " failed: " + detailedMessage); + destroyInternal(); + dispatchSessionFinished(error, detailedMessage, null); + } + private void onStorageHealthStatusChanged(int status) { final String packageName = getPackageName(); if (TextUtils.isEmpty(packageName)) { @@ -1757,15 +1765,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { try { sealLocked(); - if (isApexSession()) { - // APEX installations rely on certain fields to be populated after reboot. - // E.g. mPackageName. - validateApexInstallLocked(); - } else { - // Populate mPackageName for this APK session which is required by the staging - // manager to check duplicate apk-in-apex. - PackageInstallerSession parent = allSessions.get(mParentSessionId); - if (parent != null && parent.isStagedSessionReady()) { + // Session that are staged, ready and not multi package will be installed during + // this boot. As such, we need populate all the fields for successful installation. + if (isMultiPackage()) { + return; + } + final PackageInstallerSession root = hasParentSessionId() + ? allSessions.get(getParentSessionId()) + : this; + if (root != null && root.isStagedSessionReady()) { + if (isApexSession()) { + validateApexInstallLocked(); + } else { validateApkInstallLocked(); } } @@ -1829,7 +1840,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagingManager.commitSession(this); // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even // though ideally, we just need to send session committed broadcast. - dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null); + dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null); return; } @@ -1911,12 +1922,53 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + /** + * Installs apks of staged session while skipping the verification process for a committed and + * ready session. + */ + void installStagedSession(IntentSender statusReceiver) { + assertCallerIsOwnerOrRootOrSystemLocked(); + Preconditions.checkArgument(!hasParentSessionId()); // Don't allow installing child sessions + Preconditions.checkArgument(isCommitted() && isStagedSessionReady()); + + // Since staged sessions are installed during boot, the original reference to status + // receiver from the owner has already been lost. We can safely replace it with a + // status receiver from the system without effecting the flow. + updateRemoteStatusReceiver(statusReceiver); + install(); + } + + private void updateRemoteStatusReceiver(IntentSender remoteStatusReceiver) { + synchronized (mLock) { + mRemoteStatusReceiver = remoteStatusReceiver; + if (isMultiPackage()) { + final IntentSender childIntentSender = + new ChildStatusIntentReceiver(mChildSessions.clone(), remoteStatusReceiver) + .getIntentSender(); + for (int i = mChildSessions.size() - 1; i >= 0; --i) { + mChildSessions.valueAt(i).mRemoteStatusReceiver = childIntentSender; + } + } + } + } + + private void install() { + try { + installNonStaged(); + } catch (PackageManagerException e) { + final String completeMsg = ExceptionUtils.getCompleteMessage(e); + onSessionInstallationFailure(e.error, completeMsg); + } + } + private void installNonStaged() throws PackageManagerException { - final PackageManagerService.InstallParams installingSession = - makeInstallParams(); + Preconditions.checkArgument(containsApkSession()); + + final PackageManagerService.InstallParams installingSession = makeInstallParams(); if (installingSession == null) { - return; + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Session should contain at least one apk session for installation"); } if (isMultiPackage()) { final List<PackageInstallerSession> childSessions; @@ -2084,7 +2136,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) { - if (returnCode == PackageManager.INSTALL_SUCCEEDED) { + if (returnCode == INSTALL_SUCCEEDED) { onVerificationComplete(); } else { onSessionVerificationFailure(returnCode, msg); @@ -2126,20 +2178,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } - try { - installNonStaged(); - } catch (PackageManagerException e) { - final String completeMsg = ExceptionUtils.getCompleteMessage(e); - Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); - destroyInternal(); - dispatchSessionFinished(e.error, completeMsg, null); - } + install(); } /** * Stages this session for install and returns a * {@link PackageManagerService.InstallParams} representing this new staged state. */ + @Nullable private PackageManagerService.InstallParams makeInstallParams() throws PackageManagerException { synchronized (mLock) { @@ -2153,8 +2199,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - // We've reached point of no return; call into PMS to install the stage. - // Regardless of success or failure we always destroy session. + // Do not try to install apex session. Parent session will have at least one apk session. + if (!isMultiPackage() && isApexSession()) { + sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, + "Apex package should have been installed by apexd", null); + return null; + } + final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { @Override public void onUserActionRequired(Intent intent) { @@ -2164,8 +2215,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) { - destroyInternal(); - dispatchSessionFinished(returnCode, msg, extras); + if (isStaged()) { + sendUpdateToRemoteStatusReceiver(returnCode, msg, extras); + } else { + // We've reached point of no return; call into PMS to install the stage. + // Regardless of success or failure we always destroy session. + destroyInternal(); + dispatchSessionFinished(returnCode, msg, extras); + } } }; @@ -2176,6 +2233,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { user = new UserHandle(userId); } + if (params.isStaged) { + params.installFlags |= INSTALL_STAGED; + } + synchronized (mLock) { return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user, mSigningDetails, mInstallerUid); @@ -2952,7 +3013,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { handle = NativeLibraryHelper.Handle.create(packageDir); final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir, abiOverride, isIncrementalInstallation()); - if (res != PackageManager.INSTALL_SUCCEEDED) { + if (res != INSTALL_SUCCEEDED) { throw new PackageManagerException(res, "Failed to extract native libraries, res=" + res); } @@ -3586,16 +3647,35 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { - final IntentSender statusReceiver; - final String packageName; + sendUpdateToRemoteStatusReceiver(returnCode, msg, extras); + synchronized (mLock) { mFinalStatus = returnCode; mFinalMessage = msg; + } + final boolean success = (returnCode == INSTALL_SUCCEEDED); + + // Send broadcast to default launcher only if it's a new install + // TODO(b/144270665): Secure the usage of this broadcast. + final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); + if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) { + mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId); + } + + mCallback.onSessionFinished(this, success); + if (isDataLoaderInstallation()) { + logDataLoaderInstallationSession(returnCode); + } + } + + private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) { + final IntentSender statusReceiver; + final String packageName; + synchronized (mLock) { statusReceiver = mRemoteStatusReceiver; packageName = mPackageName; } - if (statusReceiver != null) { // Execute observer.onPackageInstalled on different thread as we don't want callers // inside the system server have to worry about catching the callbacks while they are @@ -3606,23 +3686,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { args.arg3 = extras; args.arg4 = statusReceiver; args.argi1 = returnCode; - mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget(); } - - final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); - - // Send broadcast to default launcher only if it's a new install - // TODO(b/144270665): Secure the usage of this broadcast. - final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); - if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) { - mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId); - } - - mCallback.onSessionFinished(this, success); - if (isDataLoaderInstallation()) { - logDataLoaderInstallationSession(returnCode); - } } /** {@hide} */ @@ -3834,7 +3899,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId, boolean showNotification, int userId, String basePackageName, int returnCode, String msg, Bundle extras) { - if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) { + if (INSTALL_SUCCEEDED == returnCode && showNotification) { boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING); Notification notification = PackageInstallerService.buildSuccessNotification(context, context.getResources() @@ -4055,7 +4120,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { CertifiedChecksum checksum = checksums.get(j); out.startTag(null, TAG_SESSION_CHECKSUM); writeStringAttribute(out, ATTR_NAME, fileName); - writeIntAttribute(out, ATTR_CHECKSUM_KIND, checksum.getChecksum().getKind()); + writeIntAttribute(out, ATTR_CHECKSUM_KIND, checksum.getChecksum().getType()); writeByteArrayAttribute(out, ATTR_CHECKSUM_VALUE, checksum.getChecksum().getValue()); writeStringAttribute(out, ATTR_CHECKSUM_PACKAGE, checksum.getPackageName()); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 54dbe2e470be..0f9a5cc331a0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -20,10 +20,8 @@ import static android.Manifest.permission.DELETE_PACKAGES; import static android.Manifest.permission.INSTALL_PACKAGES; import static android.Manifest.permission.MANAGE_DEVICE_ADMINS; import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS; -import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.REQUEST_DELETE_PACKAGES; import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS; -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.MODE_IGNORED; @@ -68,6 +66,7 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTEN import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE; import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP; +import static android.content.pm.PackageManager.INSTALL_STAGED; import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK; @@ -92,7 +91,6 @@ import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR; import static android.content.pm.PackageManager.MOVE_FAILED_LOCKED_USER; import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING; import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE; -import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.RESTRICTION_NONE; import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; @@ -2470,9 +2468,9 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public void getChecksums(@NonNull String packageName, boolean includeSplits, - @Checksum.Kind int optional, - @Checksum.Kind int required, @Nullable List trustedInstallers, + public void requestChecksums(@NonNull String packageName, boolean includeSplits, + @Checksum.Type int optional, + @Checksum.Type int required, @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId) { Objects.requireNonNull(packageName); Objects.requireNonNull(statusReceiver); @@ -14993,6 +14991,7 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable MultiPackageInstallParams mParentInstallParams; final boolean forceQueryableOverride; final int mDataLoaderType; + final long requiredInstalledVersionCode; InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, int installFlags, InstallSource installSource, String volumeUuid, @@ -15013,6 +15012,7 @@ public class PackageManagerService extends IPackageManager.Stub this.installReason = PackageManager.INSTALL_REASON_UNKNOWN; this.forceQueryableOverride = false; this.mDataLoaderType = DataLoaderType.NONE; + this.requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST; } InstallParams(File stagedDir, IPackageInstallObserver2 observer, @@ -15035,6 +15035,7 @@ public class PackageManagerService extends IPackageManager.Stub forceQueryableOverride = sessionParams.forceQueryableOverride; mDataLoaderType = (sessionParams.dataLoaderParams != null) ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE; + requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode; } @Override @@ -15181,6 +15182,18 @@ public class PackageManagerService extends IPackageManager.Stub public void handleStartCopy() { PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext, origin.resolvedPath, installFlags, packageAbiOverride); + + // For staged session, there is a delay between its verification and install. Device + // state can change within this delay and hence we need to re-verify certain conditions. + boolean isStaged = (installFlags & INSTALL_STAGED) != 0; + if (isStaged) { + mRet = verifyReplacingVersionCode( + pkgLite, requiredInstalledVersionCode, installFlags); + if (mRet != INSTALL_SUCCEEDED) { + return; + } + } + mRet = overrideInstallLocation(pkgLite); } @@ -15327,11 +15340,14 @@ public class PackageManagerService extends IPackageManager.Stub PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext, origin.resolvedPath, installFlags, packageAbiOverride); - mRet = verifyReplacingVersionCode(pkgLite); + mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags); + if (mRet != INSTALL_SUCCEEDED) { + return; + } // Perform package verification and enable rollback (unless we are simply moving the // package). - if (mRet == INSTALL_SUCCEEDED && !origin.existing) { + if (!origin.existing) { sendApkVerificationRequest(pkgLite); if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { sendEnableRollbackRequest(); @@ -15339,54 +15355,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private int verifyReplacingVersionCode(PackageInfoLite pkgLite) { - String packageName = pkgLite.packageName; - synchronized (mLock) { - // Package which currently owns the data that the new package will own if installed. - // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg - // will be null whereas dataOwnerPkg will contain information about the package - // which was uninstalled while keeping its data. - AndroidPackage dataOwnerPkg = mPackages.get(packageName); - if (dataOwnerPkg == null) { - PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps != null) { - dataOwnerPkg = ps.pkg; - } - } - - if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) { - if (dataOwnerPkg == null) { - Slog.w(TAG, "Required installed version code was " - + requiredInstalledVersionCode - + " but package is not installed"); - return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION; - } - - if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) { - Slog.w(TAG, "Required installed version code was " - + requiredInstalledVersionCode - + " but actual installed version is " - + dataOwnerPkg.getLongVersionCode()); - return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION; - } - } - - if (dataOwnerPkg != null) { - if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, - dataOwnerPkg.isDebuggable())) { - try { - checkDowngrade(dataOwnerPkg, pkgLite); - } catch (PackageManagerException e) { - Slog.w(TAG, "Downgrade detected: " + e.getMessage()); - return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; - } - } - } - } - return PackageManager.INSTALL_SUCCEEDED; - } - - void sendApkVerificationRequest(PackageInfoLite pkgLite) { final int verificationId = mPendingVerificationToken++; @@ -15980,7 +15948,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - final File targetDir = codeFile.getParentFile(); + final File targetDir = resolveTargetDir(); final File beforeCodeFile = codeFile; final File afterCodeFile = getNextCodePath(targetDir, parsedPackage.getPackageName()); @@ -16023,6 +15991,17 @@ public class PackageManagerService extends IPackageManager.Stub return true; } + // TODO(b/168126411): Once staged install flow starts using the same folder as non-staged + // flow, we won't need this method anymore. + private File resolveTargetDir() { + boolean isStagedInstall = (installFlags & INSTALL_STAGED) != 0; + if (isStagedInstall) { + return Environment.getDataAppDirectory(null); + } else { + return codeFile.getParentFile(); + } + } + int doPostInstall(int status, int uid) { if (status != PackageManager.INSTALL_SUCCEEDED) { cleanUp(); @@ -21920,24 +21899,18 @@ public class PackageManagerService extends IPackageManager.Stub mInjector.getStorageManagerInternal().addExternalStoragePolicy( new StorageManagerInternal.ExternalStorageMountPolicy() { - @Override - public int getMountMode(int uid, String packageName) { - if (Process.isIsolated(uid)) { - return Zygote.MOUNT_EXTERNAL_NONE; - } - if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) { - return Zygote.MOUNT_EXTERNAL_DEFAULT; - } - if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) { - return Zygote.MOUNT_EXTERNAL_READ; - } - return Zygote.MOUNT_EXTERNAL_WRITE; - } + @Override + public int getMountMode(int uid, String packageName) { + if (Process.isIsolated(uid)) { + return Zygote.MOUNT_EXTERNAL_NONE; + } + return Zygote.MOUNT_EXTERNAL_DEFAULT; + } - @Override - public boolean hasExternalStorage(int uid, String packageName) { - return true; - } + @Override + public boolean hasExternalStorage(int uid, String packageName) { + return true; + } }); // Now that we're mostly running, clean up stale users and apps @@ -24206,6 +24179,54 @@ public class PackageManagerService extends IPackageManager.Stub } } + private int verifyReplacingVersionCode(PackageInfoLite pkgLite, + long requiredInstalledVersionCode, int installFlags) { + String packageName = pkgLite.packageName; + synchronized (mLock) { + // Package which currently owns the data that the new package will own if installed. + // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg + // will be null whereas dataOwnerPkg will contain information about the package + // which was uninstalled while keeping its data. + AndroidPackage dataOwnerPkg = mPackages.get(packageName); + if (dataOwnerPkg == null) { + PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps != null) { + dataOwnerPkg = ps.pkg; + } + } + + if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) { + if (dataOwnerPkg == null) { + Slog.w(TAG, "Required installed version code was " + + requiredInstalledVersionCode + + " but package is not installed"); + return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION; + } + + if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) { + Slog.w(TAG, "Required installed version code was " + + requiredInstalledVersionCode + + " but actual installed version is " + + dataOwnerPkg.getLongVersionCode()); + return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION; + } + } + + if (dataOwnerPkg != null) { + if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, + dataOwnerPkg.isDebuggable())) { + try { + checkDowngrade(dataOwnerPkg, pkgLite); + } catch (PackageManagerException e) { + Slog.w(TAG, "Downgrade detected: " + e.getMessage()); + return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; + } + } + } + } + return PackageManager.INSTALL_SUCCEEDED; + } + /** * Check and throw if the given before/after packages would be considered a * downgrade. diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index afbf7d3a35af..c356fc72379f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -96,6 +96,7 @@ import android.system.Os; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArraySet; +import android.util.IntArray; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; @@ -1450,7 +1451,7 @@ class PackageManagerShellCommand extends ShellCommand { final PrintWriter pw = getOutPrintWriter(); final int parentSessionId = Integer.parseInt(getNextArg()); - List<Integer> otherSessionIds = new ArrayList<>(); + IntArray otherSessionIds = new IntArray(); String opt; while ((opt = getNextArg()) != null) { otherSessionIds.add(Integer.parseInt(opt)); @@ -1459,7 +1460,7 @@ class PackageManagerShellCommand extends ShellCommand { pw.println("Error: At least two sessions are required."); return 1; } - return doInstallAddSession(parentSessionId, ArrayUtils.convertToIntArray(otherSessionIds), + return doInstallAddSession(parentSessionId, otherSessionIds.toArray(), true /*logSuccess*/); } diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 2ff18f8bdf79..de9d3a09df35 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -28,7 +28,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.permission.AppIdPermissionState; +import com.android.server.pm.permission.LegacyPermissionState; import com.android.server.pm.pkg.PackageStateUnserialized; import java.io.File; @@ -214,11 +214,12 @@ public class PackageSetting extends PackageSettingBase { mimeGroups = updatedMimeGroups; } + @Deprecated @Override - public AppIdPermissionState getPermissionsState() { + public LegacyPermissionState getLegacyPermissionState() { return (sharedUser != null) - ? sharedUser.getPermissionsState() - : super.getPermissionsState(); + ? sharedUser.getLegacyPermissionState() + : super.getLegacyPermissionState(); } public int getAppId() { diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index c1258b1efd48..968c4b514ed0 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -19,23 +19,29 @@ package com.android.server.pm; import android.content.pm.ApplicationInfo; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.pm.permission.AppIdPermissionState; +import com.android.server.pm.permission.LegacyPermissionState; @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public abstract class SettingBase { int pkgFlags; int pkgPrivateFlags; - protected final AppIdPermissionState mPermissionsState; + /** + * The legacy permission state that is read from package settings persistence for migration. + * This state here can not reflect the current permission state and should not be used for + * purposes other than migration. + */ + @Deprecated + protected final LegacyPermissionState mLegacyPermissionsState; SettingBase(int pkgFlags, int pkgPrivateFlags) { setFlags(pkgFlags); setPrivateFlags(pkgPrivateFlags); - mPermissionsState = new AppIdPermissionState(); + mLegacyPermissionsState = new LegacyPermissionState(); } SettingBase(SettingBase orig) { - mPermissionsState = new AppIdPermissionState(); + mLegacyPermissionsState = new LegacyPermissionState(); doCopy(orig); } @@ -46,11 +52,12 @@ public abstract class SettingBase { private void doCopy(SettingBase orig) { pkgFlags = orig.pkgFlags; pkgPrivateFlags = orig.pkgPrivateFlags; - mPermissionsState.copyFrom(orig.mPermissionsState); + mLegacyPermissionsState.copyFrom(orig.mLegacyPermissionsState); } - public AppIdPermissionState getPermissionsState() { - return mPermissionsState; + @Deprecated + public LegacyPermissionState getLegacyPermissionState() { + return mLegacyPermissionsState; } void setFlags(int pkgFlags) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index c16bd5cdf1fa..1dc035f26d83 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -107,10 +107,10 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.permission.AppIdPermissionState; -import com.android.server.pm.permission.AppIdPermissionState.PermissionState; import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.LegacyPermissionDataProvider; +import com.android.server.pm.permission.LegacyPermissionState; +import com.android.server.pm.permission.LegacyPermissionState.PermissionState; import com.android.server.pm.permission.PermissionSettings; import com.android.server.utils.TimingsTraceAndSlog; @@ -733,7 +733,8 @@ public final class Settings { pkgSetting.signatures = new PackageSignatures(disabledPkg.signatures); pkgSetting.appId = disabledPkg.appId; // Clone permissions - pkgSetting.getPermissionsState().copyFrom(disabledPkg.getPermissionsState()); + pkgSetting.getLegacyPermissionState() + .copyFrom(disabledPkg.getLegacyPermissionState()); // Clone component info List<UserInfo> users = getAllUsers(userManager); if (users != null) { @@ -2114,7 +2115,7 @@ public final class Settings { } void readInstallPermissionsLPr(XmlPullParser parser, - AppIdPermissionState permissionsState) throws IOException, XmlPullParserException { + LegacyPermissionState permissionsState) throws IOException, XmlPullParserException { int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT @@ -2406,7 +2407,7 @@ public final class Settings { serializer.attribute(null, "userId", Integer.toString(usr.userId)); usr.signatures.writeXml(serializer, "sigs", mPastSignatures); - writePermissionsLPr(serializer, usr.getPermissionsState() + writePermissionsLPr(serializer, usr.getLegacyPermissionState() .getInstallPermissionStates()); serializer.endTag(null, "shared-user"); } @@ -2734,7 +2735,7 @@ public final class Settings { // If this is a shared user, the permissions will be written there. if (pkg.sharedUser == null) { - writePermissionsLPr(serializer, pkg.getPermissionsState() + writePermissionsLPr(serializer, pkg.getLegacyPermissionState() .getInstallPermissionStates()); } @@ -2825,7 +2826,8 @@ public final class Settings { serializer, "install-initiator-sigs", mPastSignatures); } - writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissionStates()); + writePermissionsLPr(serializer, + pkg.getLegacyPermissionState().getInstallPermissionStates()); writeSigningKeySetLPr(serializer, pkg.keySetData); writeUpgradeKeySetsLPr(serializer, pkg.keySetData); @@ -3551,7 +3553,7 @@ public final class Settings { } if (parser.getName().equals(TAG_PERMISSIONS)) { - readInstallPermissionsLPr(parser, ps.getPermissionsState()); + readInstallPermissionsLPr(parser, ps.getLegacyPermissionState()); } else if (parser.getName().equals(TAG_USES_STATIC_LIB)) { readUsesStaticLibLPw(parser, ps); } else { @@ -3848,7 +3850,7 @@ public final class Settings { packageSetting.signatures.readXml(parser, mPastSignatures); } else if (tagName.equals(TAG_PERMISSIONS)) { readInstallPermissionsLPr(parser, - packageSetting.getPermissionsState()); + packageSetting.getLegacyPermissionState()); packageSetting.installPermissionsFixed = true; } else if (tagName.equals("proper-signing-keyset")) { long id = Long.parseLong(parser.getAttributeValue(null, "identifier")); @@ -4074,7 +4076,7 @@ public final class Settings { if (tagName.equals("sigs")) { su.signatures.readXml(parser, mPastSignatures); } else if (tagName.equals("perms")) { - readInstallPermissionsLPr(parser, su.getPermissionsState()); + readInstallPermissionsLPr(parser, su.getLegacyPermissionState()); } else { PackageManagerService.reportSettingsProblem(Log.WARN, "Unknown element under <shared-user>: " + parser.getName()); @@ -4494,7 +4496,7 @@ public final class Settings { void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag, ArraySet<String> permissionNames, PackageSetting ps, - AppIdPermissionState permissionsState, SimpleDateFormat sdf, Date date, + LegacyPermissionState permissionsState, SimpleDateFormat sdf, Date date, List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) { AndroidPackage pkg = ps.pkg; if (checkinTag != null) { @@ -4944,8 +4946,8 @@ public final class Settings { && !packageName.equals(ps.name)) { continue; } - final AppIdPermissionState permissionsState = - mPermissionDataProvider.getAppIdPermissionState(ps.appId); + final LegacyPermissionState permissionsState = + mPermissionDataProvider.getLegacyPermissionState(ps.appId); if (permissionNames != null && !permissionsState.hasPermissionState(permissionNames)) { continue; @@ -5002,8 +5004,8 @@ public final class Settings { pw.println("Hidden system packages:"); printedSomething = true; } - final AppIdPermissionState permissionsState = - mPermissionDataProvider.getAppIdPermissionState(ps.appId); + final LegacyPermissionState permissionsState = + mPermissionDataProvider.getLegacyPermissionState(ps.appId); dumpPackageLPr(pw, " ", checkin ? "dis" : null, permissionNames, ps, permissionsState, sdf, date, users, packageName != null, dumpAllComponents); } @@ -5033,8 +5035,8 @@ public final class Settings { if (packageName != null && su != dumpState.getSharedUser()) { continue; } - final AppIdPermissionState permissionsState = - mPermissionDataProvider.getAppIdPermissionState(su.userId); + final LegacyPermissionState permissionsState = + mPermissionDataProvider.getLegacyPermissionState(su.userId); if (permissionNames != null && !permissionsState.hasPermissionState(permissionNames)) { continue; @@ -5178,7 +5180,7 @@ public final class Settings { } void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames, - AppIdPermissionState permissionsState) { + LegacyPermissionState permissionsState) { Collection<PermissionState> permissionStates = permissionsState.getInstallPermissionStates(); if (!permissionStates.isEmpty()) { @@ -5424,7 +5426,7 @@ public final class Settings { if (packageSetting.sharedUser == null) { List<RuntimePermissionsState.PermissionState> permissions = getPermissionsFromPermissionsState( - packageSetting.getPermissionsState(), userId); + packageSetting.getLegacyPermissionState(), userId); packagePermissions.put(packageName, permissions); } } @@ -5437,7 +5439,7 @@ public final class Settings { SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i); List<RuntimePermissionsState.PermissionState> permissions = getPermissionsFromPermissionsState( - sharedUserSetting.getPermissionsState(), userId); + sharedUserSetting.getLegacyPermissionState(), userId); sharedUserPermissions.put(sharedUserName, permissions); } @@ -5449,7 +5451,7 @@ public final class Settings { @NonNull private List<RuntimePermissionsState.PermissionState> getPermissionsFromPermissionsState( - @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) { + @NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) { Collection<PermissionState> permissionStates = permissionsState.getRuntimePermissionStates(userId); List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>(); @@ -5509,11 +5511,11 @@ public final class Settings { List<RuntimePermissionsState.PermissionState> permissions = packagePermissions.get(packageName); if (permissions != null) { - readPermissionsStateLpr(permissions, packageSetting.getPermissionsState(), + readPermissionsStateLpr(permissions, packageSetting.getLegacyPermissionState(), userId); } else if (packageSetting.sharedUser == null && !isUpgradeToR) { Slog.w(TAG, "Missing permission state for package: " + packageName); - packageSetting.getPermissionsState().setMissing(true, userId); + packageSetting.getLegacyPermissionState().setMissing(true, userId); } } @@ -5527,18 +5529,18 @@ public final class Settings { List<RuntimePermissionsState.PermissionState> permissions = sharedUserPermissions.get(sharedUserName); if (permissions != null) { - readPermissionsStateLpr(permissions, sharedUserSetting.getPermissionsState(), - userId); + readPermissionsStateLpr(permissions, + sharedUserSetting.getLegacyPermissionState(), userId); } else if (!isUpgradeToR) { Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName); - sharedUserSetting.getPermissionsState().setMissing(true, userId); + sharedUserSetting.getLegacyPermissionState().setMissing(true, userId); } } } private void readPermissionsStateLpr( @NonNull List<RuntimePermissionsState.PermissionState> permissions, - @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) { + @NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) { int permissionsSize = permissions.size(); for (int i = 0; i < permissionsSize; i++) { RuntimePermissionsState.PermissionState permission = permissions.get(i); @@ -5617,7 +5619,7 @@ public final class Settings { XmlUtils.skipCurrentTag(parser); continue; } - parsePermissionsLPr(parser, ps.getPermissionsState(), userId); + parsePermissionsLPr(parser, ps.getLegacyPermissionState(), userId); } break; case TAG_SHARED_USER: { @@ -5628,14 +5630,14 @@ public final class Settings { XmlUtils.skipCurrentTag(parser); continue; } - parsePermissionsLPr(parser, sus.getPermissionsState(), userId); + parsePermissionsLPr(parser, sus.getLegacyPermissionState(), userId); } break; } } } private void parsePermissionsLPr(XmlPullParser parser, - AppIdPermissionState permissionsState, int userId) + LegacyPermissionState permissionsState, int userId) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); int type; diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 35c26d6cd54e..529f16af883d 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -794,18 +794,18 @@ public class StagingManager { private void installApksInSession(PackageInstallerSession session) throws PackageManagerException { - final PackageInstallerSession apksToInstall = extractApksInSession(session); - if (apksToInstall == null) { + if (!session.containsApkSession()) { return; } - if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { + if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { // If rollback is available for this session, notify the rollback // manager of the apk session so it can properly enable rollback. final RollbackManagerInternal rm = LocalServices.getService(RollbackManagerInternal.class); try { - rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId); + // TODO(b/136257624): extra apk session id in rollback is now redundant. + rm.notifyStagedApkSession(session.sessionId, session.sessionId); } catch (RuntimeException re) { Slog.e(TAG, "Failed to notifyStagedApkSession for session: " + session.sessionId, re); @@ -813,7 +813,7 @@ public class StagingManager { } final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync(); - apksToInstall.commit(receiver.getIntentSender(), false); + session.installStagedSession(receiver.getIntentSender()); final Intent result = receiver.getResult(); final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java index 7452b522b20a..346a2c527fcb 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java @@ -31,7 +31,7 @@ public interface LegacyPermissionDataProvider { * @return the legacy permission state */ @NonNull - public abstract AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId); + LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId); /** * Get the GIDs computed from the permission state of a UID, either a package or a shared user. @@ -40,5 +40,5 @@ public interface LegacyPermissionDataProvider { * @return the GIDs for the UID */ @NonNull - public abstract int[] getGidsForUid(int uid); + int[] getGidsForUid(int uid); } diff --git a/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java index aabdafdd453a..63f69cede59c 100644 --- a/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java @@ -32,7 +32,7 @@ import java.util.Objects; * Legacy permission state that was associated with packages or shared users. */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) -public final class AppIdPermissionState { +public final class LegacyPermissionState { // Maps from user IDs to user states. @NonNull private final SparseArray<UserState> mUserStates = new SparseArray<>(); @@ -48,7 +48,7 @@ public final class AppIdPermissionState { * * @hide */ - public void copyFrom(@NonNull AppIdPermissionState other) { + public void copyFrom(@NonNull LegacyPermissionState other) { if (other == this) { return; } @@ -88,7 +88,7 @@ public final class AppIdPermissionState { if (getClass() != object.getClass()) { return false; } - final AppIdPermissionState other = (AppIdPermissionState) object; + final LegacyPermissionState other = (LegacyPermissionState) object; return Objects.equals(mUserStates, other.mUserStates) && Objects.equals(mMissing, other.mMissing); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index b293ba6ccc2d..d7ff9a117e9d 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -68,7 +68,6 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.ApplicationPackageManager; import android.app.IActivityManager; -import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.compat.annotation.ChangeId; @@ -104,7 +103,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.storage.StorageManager; -import android.os.storage.StorageManagerInternal; import android.permission.IOnPermissionsChangeListener; import android.permission.IPermissionManager; import android.permission.PermissionControllerManager; @@ -1554,24 +1552,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (bp.isRuntime()) { notifyRuntimePermissionStateChanged(packageName, userId); } - - // Only need to do this if user is initialized. Otherwise it's a new user - // and there are no processes running as the user yet and there's no need - // to make an expensive call to remount processes for the changed permissions. - if (READ_EXTERNAL_STORAGE.equals(permName) - || WRITE_EXTERNAL_STORAGE.equals(permName)) { - final long token = Binder.clearCallingIdentity(); - try { - if (mUserManagerInt.isUserInitialized(userId)) { - StorageManagerInternal storageManagerInternal = LocalServices.getService( - StorageManagerInternal.class); - storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName); - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - } @Override @@ -3551,9 +3531,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { private static boolean isProfileOwner(int uid) { DevicePolicyManagerInternal dpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class); + //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode. if (dpmInternal != null) { - return dpmInternal - .isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + return dpmInternal.isActiveProfileOwner(uid) || dpmInternal.isActiveDeviceOwner(uid); } return false; } @@ -4618,7 +4598,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int[] userIds = getAllUserIds(); mPackageManagerInt.forEachPackageSetting(ps -> { final int appId = ps.getAppId(); - final AppIdPermissionState appIdState = ps.getPermissionsState(); + final LegacyPermissionState legacyState = ps.getLegacyPermissionState(); synchronized (mLock) { for (final int userId : userIds) { @@ -4627,19 +4607,19 @@ public class PermissionManagerService extends IPermissionManager.Stub { userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed()); final UidPermissionState uidState = userState.getOrCreateUidState(appId); uidState.reset(); - uidState.setMissing(appIdState.isMissing(userId)); + uidState.setMissing(legacyState.isMissing(userId)); readStateFromPermissionStates(uidState, - appIdState.getInstallPermissionStates()); + legacyState.getInstallPermissionStates()); readStateFromPermissionStates(uidState, - appIdState.getRuntimePermissionStates(userId)); + legacyState.getRuntimePermissionStates(userId)); } } }); } private void readStateFromPermissionStates(@NonNull UidPermissionState uidState, - @NonNull Collection<AppIdPermissionState.PermissionState> permissionStates) { - for (final AppIdPermissionState.PermissionState permissionState : permissionStates) { + @NonNull Collection<LegacyPermissionState.PermissionState> permissionStates) { + for (final LegacyPermissionState.PermissionState permissionState : permissionStates) { uidState.putPermissionState(permissionState.getPermission(), permissionState.isGranted(), permissionState.getFlags()); } @@ -4649,8 +4629,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int[] userIds = mState.getUserIds(); mPackageManagerInt.forEachPackageSetting(ps -> { ps.setInstallPermissionsFixed(false); - final AppIdPermissionState appIdState = ps.getPermissionsState(); - appIdState.reset(); + final LegacyPermissionState legacyState = ps.getLegacyPermissionState(); + legacyState.reset(); final int appId = ps.getAppId(); synchronized (mLock) { @@ -4672,21 +4652,21 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } - appIdState.setMissing(uidState.isMissing(), userId); + legacyState.setMissing(uidState.isMissing(), userId); final List<PermissionState> permissionStates = uidState.getPermissionStates(); final int permissionStatesSize = permissionStates.size(); for (int i = 0; i < permissionStatesSize; i++) { final PermissionState permissionState = permissionStates.get(i); - final AppIdPermissionState.PermissionState legacyPermissionState = - new AppIdPermissionState.PermissionState( + final LegacyPermissionState.PermissionState legacyPermissionState = + new LegacyPermissionState.PermissionState( permissionState.getPermission(), permissionState.isGranted(), permissionState.getFlags()); if (permissionState.isRuntime()) { - appIdState.putRuntimePermissionState(legacyPermissionState, + legacyState.putRuntimePermissionState(legacyPermissionState, userId); } else { - appIdState.putInstallPermissionState(legacyPermissionState); + legacyState.putInstallPermissionState(legacyPermissionState); } } } @@ -4695,8 +4675,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @NonNull - private AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) { - final AppIdPermissionState appIdState = new AppIdPermissionState(); + private LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { + final LegacyPermissionState legacyState = new LegacyPermissionState(); final int[] userIds = mState.getUserIds(); for (final int userId : userIds) { final UidPermissionState uidState = getUidState(appId, userId); @@ -4711,17 +4691,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < permissionStatesSize; i++) { final PermissionState permissionState = permissionStates.get(i); - final AppIdPermissionState.PermissionState legacyPermissionState = - new AppIdPermissionState.PermissionState(permissionState.getPermission(), + final LegacyPermissionState.PermissionState legacyPermissionState = + new LegacyPermissionState.PermissionState(permissionState.getPermission(), permissionState.isGranted(), permissionState.getFlags()); if (permissionState.isRuntime()) { - appIdState.putRuntimePermissionState(legacyPermissionState, userId); + legacyState.putRuntimePermissionState(legacyPermissionState, userId); } else if (userId == UserHandle.USER_SYSTEM) { - appIdState.putInstallPermissionState(legacyPermissionState); + legacyState.putInstallPermissionState(legacyPermissionState); } } } - return appIdState; + return legacyState; } @NonNull @@ -5130,8 +5110,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @NonNull - public AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) { - return PermissionManagerService.this.getAppIdPermissionState(appId); + public LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { + return PermissionManagerService.this.getLegacyPermissionState(appId); } @NonNull diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index cfceaabfc229..7b044edfeea6 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -77,6 +77,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.InputChannel; @@ -709,8 +710,7 @@ public final class TvInputManagerService extends SystemService { } sessionState.isCurrent = false; sessionState.currentChannel = null; - notifyCurrentChannelInfosUpdatedLocked( - userState, getCurrentTvChannelInfosInternalLocked(userState)); + notifyCurrentChannelInfosUpdatedLocked(userState); } catch (RemoteException | SessionNotFoundException e) { Slog.e(TAG, "error in releaseSession", e); } finally { @@ -851,15 +851,18 @@ public final class TvInputManagerService extends SystemService { } } - private void notifyCurrentChannelInfosUpdatedLocked( - UserState userState, List<TvChannelInfo> infos) { + private void notifyCurrentChannelInfosUpdatedLocked(UserState userState) { if (DEBUG) { Slog.d(TAG, "notifyCurrentChannelInfosUpdatedLocked"); } int n = userState.mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { try { - userState.mCallbacks.getBroadcastItem(i).onCurrentTvChannelInfosUpdated(infos); + ITvInputManagerCallback callback = userState.mCallbacks.getBroadcastItem(i); + Pair<Integer, Integer> pidUid = userState.callbackPidUidMap.get(callback); + List<TvChannelInfo> infos = getCurrentTvChannelInfosInternalLocked( + userState, pidUid.first, pidUid.second); + callback.onCurrentTvChannelInfosUpdated(infos); } catch (RemoteException e) { Slog.e(TAG, "failed to report updated current channel infos to callback", e); } @@ -1063,14 +1066,19 @@ public final class TvInputManagerService extends SystemService { @Override public void registerCallback(final ITvInputManagerCallback callback, int userId) { - final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), - Binder.getCallingUid(), userId, "registerCallback"); + int callingPid = Binder.getCallingPid(); + int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, + "registerCallback"); final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { final UserState userState = getOrCreateUserStateLocked(resolvedUserId); if (!userState.mCallbacks.register(callback)) { Slog.e(TAG, "client process has already died"); + } else { + userState.callbackPidUidMap.put( + callback, Pair.create(callingPid, callingUid)); } } } finally { @@ -1087,6 +1095,7 @@ public final class TvInputManagerService extends SystemService { synchronized (mLock) { UserState userState = getOrCreateUserStateLocked(resolvedUserId); userState.mCallbacks.unregister(callback); + userState.callbackPidUidMap.remove(callback); } } finally { Binder.restoreCallingIdentity(identity); @@ -1419,8 +1428,8 @@ public final class TvInputManagerService extends SystemService { @Override public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) { final int callingUid = Binder.getCallingUid(); - final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, - userId, "tune"); + final int callingPid = Binder.getCallingPid(); + final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "tune"); final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -1432,8 +1441,7 @@ public final class TvInputManagerService extends SystemService { if (sessionState != null) { sessionState.isCurrent = true; sessionState.currentChannel = channelUri; - notifyCurrentChannelInfosUpdatedLocked( - userState, getCurrentTvChannelInfosInternalLocked(userState)); + notifyCurrentChannelInfosUpdatedLocked(userState); } if (TvContract.isChannelUriForPassthroughInput(channelUri)) { // Do not log the watch history for passthrough inputs. @@ -2090,16 +2098,13 @@ public final class TvInputManagerService extends SystemService { @Override public List<TvChannelInfo> getCurrentTvChannelInfos(@UserIdInt int userId) { - final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), - Binder.getCallingUid(), userId, "getTvCurrentChannelInfos"); - final long identity = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - UserState userState = getOrCreateUserStateLocked(resolvedUserId); - return getCurrentTvChannelInfosInternalLocked(userState); - } - } finally { - Binder.restoreCallingIdentity(identity); + int callingPid = Binder.getCallingPid(); + int callingUid = Binder.getCallingUid(); + final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, + "getTvCurrentChannelInfos"); + synchronized (mLock) { + UserState userState = getOrCreateUserStateLocked(resolvedUserId); + return getCurrentTvChannelInfosInternalLocked(userState, callingPid, callingUid); } } @@ -2273,14 +2278,15 @@ public final class TvInputManagerService extends SystemService { } } - private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(UserState userState) { + private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked( + UserState userState, int callingPid, int callingUid) { List<TvChannelInfo> channelInfos = new ArrayList<>(); - boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(); + boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(callingPid, callingUid); for (SessionState state : userState.sessionStateMap.values()) { if (state.isCurrent) { Integer appTag; int appType; - if (state.callingUid == Binder.getCallingUid()) { + if (state.callingUid == callingUid) { appTag = APP_TAG_SELF; appType = TvChannelInfo.APP_TYPE_SELF; } else { @@ -2322,8 +2328,8 @@ public final class TvInputManagerService extends SystemService { return false; } - private boolean hasAccessWatchedProgramsPermission() { - return mContext.checkCallingPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS) + private boolean hasAccessWatchedProgramsPermission(int callingPid, int callingUid) { + return mContext.checkPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; } @@ -2360,6 +2366,9 @@ public final class TvInputManagerService extends SystemService { private final RemoteCallbackList<ITvInputManagerCallback> mCallbacks = new RemoteCallbackList<ITvInputManagerCallback>(); + private final Map<ITvInputManagerCallback, Pair<Integer, Integer>> callbackPidUidMap = + new HashMap<>(); + // The token of a "main" TV input session. private IBinder mainSessionToken = null; @@ -2712,8 +2721,7 @@ public final class TvInputManagerService extends SystemService { mSessionState.isCurrent = true; mSessionState.currentChannel = channelUri; UserState userState = getOrCreateUserStateLocked(mSessionState.userId); - notifyCurrentChannelInfosUpdatedLocked( - userState, getCurrentTvChannelInfosInternalLocked(userState)); + notifyCurrentChannelInfosUpdatedLocked(userState); try { // TODO: Consider adding this channel change in the watch log. When we do // that, how we can protect the watch log from malicious tv inputs should diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index e2c130379df3..45d3fce1ac21 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -1468,13 +1468,13 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } } - private void removeStackInSurfaceTransaction(Task stack) { - if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) { - removePinnedStackInSurfaceTransaction(stack); + private void removeRootTaskInSurfaceTransaction(Task rootTask) { + if (rootTask.getWindowingMode() == WINDOWING_MODE_PINNED) { + removePinnedStackInSurfaceTransaction(rootTask); } else { final PooledConsumer c = PooledLambda.obtainConsumer( ActivityStackSupervisor::processRemoveTask, this, PooledLambda.__(Task.class)); - stack.forAllLeafTasks(c, true /* traverseTopToBottom */); + rootTask.forAllLeafTasks(c, true /* traverseTopToBottom */); c.recycle(); } } @@ -1484,12 +1484,12 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } /** - * Removes the stack associated with the given {@param stack}. If the {@param stack} is the - * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but - * instead moved back onto the fullscreen stack. + * Removes the root task associated with the given {@param task}. If the {@param task} is the + * pinned task, then its child tasks are not explicitly removed when the root task is + * destroyed, but instead moved back onto the TaskDisplayArea. */ - void removeStack(Task stack) { - mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stack)); + void removeRootTask(Task task) { + mWindowManager.inSurfaceTransaction(() -> removeRootTaskInSurfaceTransaction(task)); } /** diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index e65be41f44cd..eaa58390d39b 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -283,7 +283,7 @@ public abstract class ActivityTaskManagerInternal { /** * Cancels any currently running recents animation. */ - public abstract void cancelRecentsAnimation(boolean restoreHomeStackPosition); + public abstract void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition); /** * This enforces {@code func} can only be called if either the caller is Recents activity or diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index f1529bd14d57..e06c156bceab 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1585,10 +1585,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * Start the recents activity to perform the recents animation. * * @param intent The intent to start the recents activity. + * @param eventTime When the (touch) event is triggered to start recents activity. * @param recentsAnimationRunner Pass {@code null} to only preload the activity. */ @Override - public void startRecentsActivity(Intent intent, @Deprecated IAssistDataReceiver unused, + public void startRecentsActivity(Intent intent, long eventTime, @Nullable IRecentsAnimationRunner recentsAnimationRunner) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()"); final int callingPid = Binder.getCallingPid(); @@ -1608,7 +1609,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (recentsAnimationRunner == null) { anim.preloadRecentsActivity(); } else { - anim.startRecentsActivity(recentsAnimationRunner); + anim.startRecentsActivity(recentsAnimationRunner, eventTime); } } } finally { @@ -2186,19 +2187,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void setFocusedStack(int stackId) { - mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()"); - ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedStack: stackId=%d", stackId); + public void setFocusedRootTask(int taskId) { + mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedRootTask()"); + ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedRootTask: taskId=%d", taskId); final long callingId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final Task stack = mRootWindowContainer.getStack(stackId); - if (stack == null) { - Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId); + final Task task = mRootWindowContainer.getStack(taskId); + if (task == null) { + Slog.w(TAG, "setFocusedRootTask: No task with id=" + taskId); return; } - final ActivityRecord r = stack.topRunningActivity(); - if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) { + final ActivityRecord r = task.topRunningActivity(); + if (r != null && r.moveFocusableActivityToTop("setFocusedRootTask")) { mRootWindowContainer.resumeFocusedStacksTopActivities(); } } @@ -2252,8 +2253,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - return mStackSupervisor.removeTaskById(taskId, true, REMOVE_FROM_RECENTS, - "remove-task"); + final Task task = mRootWindowContainer.anyTaskForId(taskId, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); + if (task == null) { + Slog.w(TAG, "removeTask: No task remove with id=" + taskId); + return false; + } + + if (task.isLeafTask()) { + mStackSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task"); + } else { + mStackSupervisor.removeRootTask(task); + } + return true; } finally { Binder.restoreCallingIdentity(ident); } @@ -2749,31 +2761,31 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void moveTaskToStack(int taskId, int stackId, boolean toTop) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()"); + public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) { + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToRootTask()"); synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { final Task task = mRootWindowContainer.anyTaskForId(taskId); if (task == null) { - Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId); + Slog.w(TAG, "moveTaskToRootTask: No task for id=" + taskId); return; } - ProtoLog.d(WM_DEBUG_TASKS, "moveTaskToStack: moving task=%d to " - + "stackId=%d toTop=%b", taskId, stackId, toTop); + ProtoLog.d(WM_DEBUG_TASKS, "moveTaskToRootTask: moving task=%d to " + + "rootTaskId=%d toTop=%b", taskId, rootTaskId, toTop); - final Task stack = mRootWindowContainer.getStack(stackId); - if (stack == null) { + final Task rootTask = mRootWindowContainer.getStack(rootTaskId); + if (rootTask == null) { throw new IllegalStateException( - "moveTaskToStack: No stack for stackId=" + stackId); + "moveTaskToRootTask: No rootTask for rootTaskId=" + rootTaskId); } - if (!stack.isActivityTypeStandardOrUndefined()) { - throw new IllegalArgumentException("moveTaskToStack: Attempt to move task " - + taskId + " to stack " + stackId); + if (!rootTask.isActivityTypeStandardOrUndefined()) { + throw new IllegalArgumentException("moveTaskToRootTask: Attempt to move task " + + taskId + " to rootTask " + rootTaskId); } - task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, - "moveTaskToStack"); + task.reparent(rootTask, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, + "moveTaskToRootTask"); } finally { Binder.restoreCallingIdentity(ident); } @@ -2861,18 +2873,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } /** - * Removes stacks in the input windowing modes from the system if they are of activity type + * Removes root tasks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED */ @Override - public void removeStacksInWindowingModes(int[] windowingModes) { + public void removeRootTasksInWindowingModes(int[] windowingModes) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, - "removeStacksInWindowingModes()"); + "removeRootTasksInWindowingModes()"); synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - mRootWindowContainer.removeStacksInWindowingModes(windowingModes); + mRootWindowContainer.removeRootTasksInWindowingModes(windowingModes); } finally { Binder.restoreCallingIdentity(ident); } @@ -2880,14 +2892,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void removeStacksWithActivityTypes(int[] activityTypes) { + public void removeRootTasksWithActivityTypes(int[] activityTypes) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, - "removeStacksWithActivityTypes()"); + "removeRootTasksWithActivityTypes()"); synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - mRootWindowContainer.removeStacksWithActivityTypes(activityTypes); + mRootWindowContainer.removeRootTasksWithActivityTypes(activityTypes); } finally { Binder.restoreCallingIdentity(ident); } @@ -2961,14 +2973,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void cancelRecentsAnimation(boolean restoreHomeStackPosition) { + public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()"); final long callingUid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { // Cancel the recents animation synchronously (do not hold the WM lock) - mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition + mWindowManager.cancelRecentsAnimation(restoreHomeRootTaskPosition ? REORDER_MOVE_TO_ORIGINAL_POSITION : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid); } @@ -3046,7 +3058,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { // When a task is locked, dismiss the pinned stack if it exists - mRootWindowContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); + mRootWindowContainer.removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED); getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid); } finally { @@ -3468,37 +3480,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void removeStack(int stackId) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()"); - synchronized (mGlobalLock) { - final long ident = Binder.clearCallingIdentity(); - try { - final Task stack = mRootWindowContainer.getStack(stackId); - if (stack == null) { - Slog.w(TAG, "removeStack: No stack with id=" + stackId); - return; - } - if (!stack.isActivityTypeStandardOrUndefined()) { - throw new IllegalArgumentException( - "Removing non-standard stack is not allowed."); - } - mStackSupervisor.removeStack(stack); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - @Override - public void moveStackToDisplay(int stackId, int displayId) { - mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()"); + public void moveRootTaskToDisplay(int taskId, int displayId) { + mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveRootTaskToDisplay()"); synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - ProtoLog.d(WM_DEBUG_TASKS, "moveStackToDisplay: moving stackId=%d to " - + "displayId=%d", stackId, displayId); - mRootWindowContainer.moveStackToDisplay(stackId, displayId, ON_TOP); + ProtoLog.d(WM_DEBUG_TASKS, "moveRootTaskToDisplay: moving taskId=%d to " + + "displayId=%d", taskId, displayId); + mRootWindowContainer.moveStackToDisplay(taskId, displayId, ON_TOP); } finally { Binder.restoreCallingIdentity(ident); } @@ -3992,26 +3982,26 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } /** - * Moves the top activity in the input stackId to the pinned stack. + * Moves the top activity in the input rootTaskId to the pinned root task. * - * @param stackId Id of stack to move the top activity to pinned stack. - * @param bounds Bounds to use for pinned stack. - * @return True if the top activity of the input stack was successfully moved to the pinned - * stack. + * @param rootTaskId Id of root task to move the top activity to pinned root task. + * @param bounds Bounds to use for pinned root task. + * @return True if the top activity of the input stack was successfully moved to the pinned root + * task. */ @Override - public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) { + public boolean moveTopActivityToPinnedRootTask(int rootTaskId, Rect bounds) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, - "moveTopActivityToPinnedStack()"); + "moveTopActivityToPinnedRootTask()"); synchronized (mGlobalLock) { if (!mSupportsPictureInPicture) { - throw new IllegalStateException("moveTopActivityToPinnedStack:" + throw new IllegalStateException("moveTopActivityToPinnedRootTask:" + "Device doesn't support picture-in-picture mode"); } long ident = Binder.clearCallingIdentity(); try { - return mRootWindowContainer.moveTopStackActivityToPinnedStack(stackId); + return mRootWindowContainer.moveTopStackActivityToPinnedRootTask(rootTaskId); } finally { Binder.restoreCallingIdentity(ident); } @@ -4183,10 +4173,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // TODO(b/149338177): remove when CTS no-longer requires it @Override - public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds, + public void resizePrimarySplitScreen(Rect dockedBounds, Rect tempDockedTaskBounds, Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()"); + enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePrimarySplitScreen()"); long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -4709,7 +4699,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (disableNonVrUi) { // If we are in a VR mode where Picture-in-Picture mode is unsupported, // then remove the pinned stack. - mRootWindowContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); + mRootWindowContainer.removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED); } } }); @@ -6285,8 +6275,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void cancelRecentsAnimation(boolean restoreHomeStackPosition) { - ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition); + public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) { + ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeRootTaskPosition); } @Override diff --git a/services/core/java/com/android/server/wm/DisplayAreaGroup.java b/services/core/java/com/android/server/wm/DisplayAreaGroup.java new file mode 100644 index 000000000000..bcf8c7c901af --- /dev/null +++ b/services/core/java/com/android/server/wm/DisplayAreaGroup.java @@ -0,0 +1,57 @@ +/* + * 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.wm; + +import static android.content.pm.ActivityInfo.reverseOrientation; + +import android.content.pm.ActivityInfo; +import android.graphics.Rect; + +/** The root of a partition of the logical display. */ +class DisplayAreaGroup extends RootDisplayArea { + + DisplayAreaGroup(WindowManagerService wms, String name, int featureId) { + super(wms, name, featureId); + } + + @Override + boolean isOrientationDifferentFromDisplay() { + if (mDisplayContent == null) { + return false; + } + + final Rect bounds = getBounds(); + final Rect displayBounds = mDisplayContent.getBounds(); + + return (bounds.width() < bounds.height()) + != (displayBounds.width() < displayBounds.height()); + } + + @ActivityInfo.ScreenOrientation + @Override + int getOrientation(int candidate) { + int orientation = super.getOrientation(candidate); + + // Reverse the requested orientation if the orientation of this DAG is different from the + // display, so that when the display rotates to the reversed orientation, this DAG will be + // in the requested orientation, so as the requested app. + // For example, if the display is 1200x900 (landscape), and this DAG is 600x900 (portrait). + // When an app below this DAG is requesting landscape, it should actually request the + // display to be portrait, so that the DAG and the app will be in landscape. + return isOrientationDifferentFromDisplay() ? reverseOrientation(orientation) : orientation; + } +} diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 22b446d32ecc..4b2c41ddecec 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4929,18 +4929,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } /** - * Removes stacks in the input windowing modes from the system if they are of activity type + * Removes root tasks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED */ - void removeStacksInWindowingModes(int... windowingModes) { + void removeRootTasksInWindowingModes(int... windowingModes) { forAllTaskDisplayAreas(taskDisplayArea -> { - taskDisplayArea.removeStacksInWindowingModes(windowingModes); + taskDisplayArea.removeRootTasksInWindowingModes(windowingModes); }); } - void removeStacksWithActivityTypes(int... activityTypes) { + void removeRootTasksWithActivityTypes(int... activityTypes) { forAllTaskDisplayAreas(taskDisplayArea -> { - taskDisplayArea.removeStacksWithActivityTypes(activityTypes); + taskDisplayArea.removeRootTasksWithActivityTypes(activityTypes); }); } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index acf5f75f7e23..803a5330ecc5 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -285,10 +285,12 @@ final class InputMonitor { inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis(); inputWindowHandle.visible = isVisible; inputWindowHandle.focusable = focusable; + inputWindowHandle.touchOcclusionMode = child.getTouchOcclusionMode(); inputWindowHandle.hasWallpaper = hasWallpaper; inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false; inputWindowHandle.ownerPid = child.mSession.mPid; inputWindowHandle.ownerUid = child.mSession.mUid; + inputWindowHandle.packageName = child.getOwningPackage(); inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures; inputWindowHandle.displayId = child.getDisplayId(); diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java index 56e1187d51da..a0074a2d760c 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsController.java +++ b/services/core/java/com/android/server/wm/LaunchParamsController.java @@ -144,11 +144,11 @@ class LaunchParamsController { mTmpParams.mPreferredTaskDisplayArea, true /* onTop */); } - if (mTmpParams.hasWindowingMode() - && mTmpParams.mWindowingMode != task.getRootTask().getWindowingMode()) { + if (mTmpParams.hasWindowingMode() && task.isRootTask() + && mTmpParams.mWindowingMode != task.getWindowingMode()) { final int activityType = activity != null ? activity.getActivityType() : task.getActivityType(); - task.getRootTask().setWindowingMode(task.getDisplayArea().validateWindowingMode( + task.setWindowingMode(task.getDisplayArea().validateWindowingMode( mTmpParams.mWindowingMode, activity, task, activityType)); } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 35338bb67469..1cf50ab25240 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -161,7 +161,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } } - void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) { + void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner, long eventTime) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity"); @@ -249,8 +249,13 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // we fetch the visible tasks to be controlled by the animation mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + ActivityOptions options = null; + if (eventTime > 0) { + options = ActivityOptions.makeBasic(); + options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime); + } mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, - START_TASK_TO_FRONT, targetActivity, null /* options */); + START_TASK_TO_FRONT, targetActivity, options); // Register for stack order changes mDefaultTaskDisplayArea.registerStackOrderChangedListener(this); diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java index faed7fa99fdd..d9a87734e9e7 100644 --- a/services/core/java/com/android/server/wm/RootDisplayArea.java +++ b/services/core/java/com/android/server/wm/RootDisplayArea.java @@ -27,7 +27,8 @@ import java.util.Map; /** * Root of a {@link DisplayArea} hierarchy. It can be either the {@link DisplayContent} as the root - * of the whole logical display, or the root of a {@link DisplayArea} group. + * of the whole logical display, or a {@link DisplayAreaGroup} as the root of a partition of the + * logical display. */ class RootDisplayArea extends DisplayArea<DisplayArea> { @@ -50,6 +51,16 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { super(wms, Type.ANY, name, featureId); } + @Override + RootDisplayArea getRootDisplayArea() { + return this; + } + + /** Whether the orientation (based on dimensions) of this root is different from the Display. */ + boolean isOrientationDifferentFromDisplay() { + return false; + } + /** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */ DisplayArea.Tokens findAreaForToken(WindowToken token) { int windowLayerFromType = token.getWindowLayerFromType(); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index cabf1bf0db3d..7e76e7553a3b 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2038,7 +2038,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will // also cause all tasks to be moved to the fullscreen stack at a position that is // appropriate. - removeStacksInWindowingModes(WINDOWING_MODE_PINNED); + removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED); mUserStackInFront.put(mCurrentUser, focusStackId); mCurrentUser = userId; @@ -2141,27 +2141,27 @@ class RootWindowContainer extends WindowContainer<DisplayContent> moveStackToTaskDisplayArea(stackId, displayContent.getDefaultTaskDisplayArea(), onTop); } - boolean moveTopStackActivityToPinnedStack(int stackId) { - final Task stack = getStack(stackId); - if (stack == null) { + boolean moveTopStackActivityToPinnedRootTask(int rootTaskId) { + final Task rootTask = getStack(rootTaskId); + if (rootTask == null) { throw new IllegalArgumentException( - "moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId); + "moveTopStackActivityToPinnedRootTask: Unknown rootTaskId=" + rootTaskId); } - final ActivityRecord r = stack.topRunningActivity(); + final ActivityRecord r = rootTask.topRunningActivity(); if (r == null) { - Slog.w(TAG, "moveTopStackActivityToPinnedStack: No top running activity" - + " in stack=" + stack); + Slog.w(TAG, "moveTopStackActivityToPinnedRootTask: No top running activity" + + " in rootTask=" + rootTask); return false; } if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) { - Slog.w(TAG, "moveTopStackActivityToPinnedStack: Picture-In-Picture not supported for " - + " r=" + r); + Slog.w(TAG, "moveTopStackActivityToPinnedRootTask: Picture-In-Picture not supported " + + "for r=" + r); return false; } - moveActivityToPinnedStack(r, "moveTopActivityToPinnedStack"); + moveActivityToPinnedStack(r, "moveTopStackActivityToPinnedRootTask"); return true; } @@ -3310,18 +3310,18 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } /** - * Removes stacks in the input windowing modes from the system if they are of activity type + * Removes root tasks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED */ - void removeStacksInWindowingModes(int... windowingModes) { + void removeRootTasksInWindowingModes(int... windowingModes) { for (int i = getChildCount() - 1; i >= 0; --i) { - getChildAt(i).removeStacksInWindowingModes(windowingModes); + getChildAt(i).removeRootTasksInWindowingModes(windowingModes); } } - void removeStacksWithActivityTypes(int... activityTypes) { + void removeRootTasksWithActivityTypes(int... activityTypes) { for (int i = getChildCount() - 1; i >= 0; --i) { - getChildAt(i).removeStacksWithActivityTypes(activityTypes); + getChildAt(i).removeRootTasksWithActivityTypes(activityTypes); } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f984576e6e2e..249fe031b757 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -6911,7 +6911,7 @@ class Task extends WindowContainer<WindowContainer> { moveToBack("moveTaskToBackLocked", tr); if (inPinnedWindowingMode()) { - mStackSupervisor.removeStack(this); + mStackSupervisor.removeRootTask(this); return true; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 696e1cafae95..d69fb0b823d0 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1289,66 +1289,65 @@ final class TaskDisplayArea extends DisplayArea<Task> { } /** - * Removes stacks in the input windowing modes from the system if they are of activity type + * Removes root tasks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED */ - void removeStacksInWindowingModes(int... windowingModes) { + void removeRootTasksInWindowingModes(int... windowingModes) { if (windowingModes == null || windowingModes.length == 0) { return; } - // Collect the stacks that are necessary to be removed instead of performing the removal - // by looping mStacks, so that we don't miss any stacks after the stack size changed or - // stacks reordered. - final ArrayList<Task> stacks = new ArrayList<>(); + // Collect the root tasks that are necessary to be removed instead of performing the removal + // by looping the children, so that we don't miss any root tasks after the children size + // changed or reordered. + final ArrayList<Task> rootTasks = new ArrayList<>(); for (int j = windowingModes.length - 1; j >= 0; --j) { final int windowingMode = windowingModes[j]; - for (int i = getStackCount() - 1; i >= 0; --i) { - final Task stack = getStackAt(i); - if (!stack.isActivityTypeStandardOrUndefined()) { - continue; - } - if (stack.getWindowingMode() != windowingMode) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final Task rootTask = mChildren.get(i); + if (rootTask.mCreatedByOrganizer + || !rootTask.isActivityTypeStandardOrUndefined() + || rootTask.getWindowingMode() != windowingMode) { continue; } - stacks.add(stack); + rootTasks.add(rootTask); } } - for (int i = stacks.size() - 1; i >= 0; --i) { - mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i)); + for (int i = rootTasks.size() - 1; i >= 0; --i) { + mRootWindowContainer.mStackSupervisor.removeRootTask(rootTasks.get(i)); } } - void removeStacksWithActivityTypes(int... activityTypes) { + void removeRootTasksWithActivityTypes(int... activityTypes) { if (activityTypes == null || activityTypes.length == 0) { return; } - // Collect the stacks that are necessary to be removed instead of performing the removal - // by looping mStacks, so that we don't miss any stacks after the stack size changed or - // stacks reordered. - final ArrayList<Task> stacks = new ArrayList<>(); + // Collect the root tasks that are necessary to be removed instead of performing the removal + // by looping the children, so that we don't miss any root tasks after the children size + // changed or reordered. + final ArrayList<Task> rootTasks = new ArrayList<>(); for (int j = activityTypes.length - 1; j >= 0; --j) { final int activityType = activityTypes[j]; - for (int i = getStackCount() - 1; i >= 0; --i) { - final Task stack = getStackAt(i); + for (int i = mChildren.size() - 1; i >= 0; --i) { + final Task rootTask = mChildren.get(i); // Collect the root tasks that are currently being organized. - if (stack.mCreatedByOrganizer) { - for (int k = stack.getChildCount() - 1; k >= 0; --k) { - final Task childStack = (Task) stack.getChildAt(k); - if (childStack.getActivityType() == activityType) { - stacks.add(childStack); + if (rootTask.mCreatedByOrganizer) { + for (int k = rootTask.getChildCount() - 1; k >= 0; --k) { + final Task task = (Task) rootTask.getChildAt(k); + if (task.getActivityType() == activityType) { + rootTasks.add(task); } } - } else if (stack.getActivityType() == activityType) { - stacks.add(stack); + } else if (rootTask.getActivityType() == activityType) { + rootTasks.add(rootTask); } } } - for (int i = stacks.size() - 1; i >= 0; --i) { - mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i)); + for (int i = rootTasks.size() - 1; i >= 0; --i) { + mRootWindowContainer.mStackSupervisor.removeRootTask(rootTasks.get(i)); } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 2ece30d24b3a..38ec924ec670 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -22,6 +22,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.isFixedOrientationLandscape; import static android.content.pm.ActivityInfo.isFixedOrientationPortrait; +import static android.content.pm.ActivityInfo.reverseOrientation; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; @@ -809,6 +810,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return parent != null ? parent.getDisplayArea() : null; } + /** Get the first node of type {@link RootDisplayArea} above or at this node. */ + @Nullable + RootDisplayArea getRootDisplayArea() { + WindowContainer parent = getParent(); + return parent != null ? parent.getRootDisplayArea() : null; + } + boolean isAttached() { return getDisplayArea() != null; } @@ -1154,17 +1162,30 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * {@link Configuration#ORIENTATION_UNDEFINED}). */ int getRequestedConfigurationOrientation() { - if (mOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { + int requestedOrientation = mOrientation; + final RootDisplayArea root = getRootDisplayArea(); + if (root != null && root.isOrientationDifferentFromDisplay()) { + // Reverse the requested orientation if the orientation of its root is different from + // the display, so that when the display rotates to the reversed orientation, the + // requested app will be in the requested orientation. + // For example, if the display is 1200x900 (landscape), and the DAG is 600x900 + // (portrait). + // When an app below the DAG is requesting landscape, it should actually request the + // display to be portrait, so that the DAG and the app will be in landscape. + requestedOrientation = reverseOrientation(mOrientation); + } + + if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { // NOSENSOR means the display's "natural" orientation, so return that. if (mDisplayContent != null) { return mDisplayContent.getNaturalOrientation(); } - } else if (mOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { + } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { // LOCKED means the activity's orientation remains unchanged, so return existing value. return getConfiguration().orientation; - } else if (isFixedOrientationLandscape(mOrientation)) { + } else if (isFixedOrientationLandscape(requestedOrientation)) { return ORIENTATION_LANDSCAPE; - } else if (isFixedOrientationPortrait(mOrientation)) { + } else if (isFixedOrientationPortrait(requestedOrientation)) { return ORIENTATION_PORTRAIT; } return ORIENTATION_UNDEFINED; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 8f42b3f154f7..5defe478c49a 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.isSplitScreenWindowingMode; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.graphics.GraphicsProtos.dumpPointProto; +import static android.hardware.input.InputManager.BLOCK_UNTRUSTED_TOUCHES; import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.PowerManager.DRAW_WAKE_LOCK; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -182,6 +183,7 @@ import android.annotation.CallSuper; import android.annotation.Nullable; import android.app.AppOpsManager; import android.app.admin.DevicePolicyCache; +import android.app.compat.CompatChanges; import android.content.Context; import android.content.res.Configuration; import android.graphics.Matrix; @@ -198,6 +200,7 @@ import android.os.PowerManager.WakeReason; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; +import android.os.TouchOcclusionMode; import android.os.Trace; import android.os.WorkSource; import android.provider.Settings; @@ -434,7 +437,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** * Usually empty. Set to the task's tempInsetFrame. See - *{@link android.app.IActivityTaskManager#resizeDockedStack}. + *{@link android.app.IActivityTaskManager#resizePrimarySplitScreen}. */ private final Rect mInsetFrame = new Rect(); @@ -958,6 +961,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP : service.mAtmService.getProcessController(s.mPid, s.mUid); } + int getTouchOcclusionMode() { + if (!CompatChanges.isChangeEnabled(BLOCK_UNTRUSTED_TOUCHES, mOwnerUid)) { + return TouchOcclusionMode.ALLOW; + } + if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) { + return TouchOcclusionMode.USE_OPACITY; + } + return TouchOcclusionMode.BLOCK_UNTRUSTED; + } + void attach() { if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken); mSession.windowAddedLocked(mAttrs.packageName); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 9f653d6124b9..13d8dc4c6be1 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -23,12 +23,10 @@ import static android.graphics.Matrix.MSKEW_Y; import static android.graphics.Matrix.MTRANS_X; import static android.graphics.Matrix.MTRANS_Y; import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; -import static android.view.WindowManager.LayoutParams.FLAG_SCALED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; 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_INPUT_METHOD; -import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_NONE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DRAW; @@ -46,19 +44,16 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_CROP; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER; import static com.android.server.wm.WindowManagerService.logWithStack; import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE; -import static com.android.server.wm.WindowStateAnimatorProto.LAST_CLIP_RECT; import static com.android.server.wm.WindowStateAnimatorProto.SURFACE; import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT; import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; -import android.app.WindowConfiguration; import android.content.Context; import android.graphics.Matrix; import android.graphics.PixelFormat; @@ -365,7 +360,7 @@ class WindowStateAnimator { if (mSurfaceController != null && mPendingDestroySurface != null) { mPostDrawTransaction.reparentChildren( mSurfaceController.getClientViewRootSurface(), - mPendingDestroySurface.mSurfaceControl).apply(); + mPendingDestroySurface.getClientViewRootSurface()).apply(); } destroySurfaceLocked(); mSurfaceDestroyDeferred = true; @@ -399,7 +394,7 @@ class WindowStateAnimator { && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) { mPostDrawTransaction.reparentChildren( mPendingDestroySurface.getClientViewRootSurface(), - mSurfaceController.mSurfaceControl).apply(); + mSurfaceController.getClientViewRootSurface()).apply(); } destroyDeferredSurfaceLocked(); @@ -993,7 +988,7 @@ class WindowStateAnimator { if (!mPendingDestroySurface.mChildrenDetached) { mPostDrawTransaction.reparentChildren( mPendingDestroySurface.getClientViewRootSurface(), - mSurfaceController.mSurfaceControl); + mSurfaceController.getClientViewRootSurface()); } } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index e39a3d1e9cb5..d14780e59ab1 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -1453,6 +1453,21 @@ static void nativeSetInTouchMode(JNIEnv* /* env */, jclass /* clazz */, im->getInputManager()->getDispatcher()->setInTouchMode(inTouchMode); } +static void nativeSetMaximumObscuringOpacityForTouch(JNIEnv* /* env */, jclass /* clazz */, + jlong ptr, jfloat opacity) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->getInputManager()->getDispatcher()->setMaximumObscuringOpacityForTouch(opacity); +} + +static void nativeSetBlockUntrustedTouchesMode(JNIEnv* env, jclass /* clazz */, jlong ptr, + jint mode) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->getInputManager()->getDispatcher()->setBlockUntrustedTouchesMode( + static_cast<BlockUntrustedTouchesMode>(mode)); +} + static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */, jlong ptr, jobject inputEventObj, jint injectorPid, jint injectorUid, jint syncMode, jint timeoutMillis, jint policyFlags) { @@ -1790,6 +1805,9 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers}, {"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled}, {"nativeSetInTouchMode", "(JZ)V", (void*)nativeSetInTouchMode}, + {"nativeSetMaximumObscuringOpacityForTouch", "(JF)V", + (void*)nativeSetMaximumObscuringOpacityForTouch}, + {"nativeSetBlockUntrustedTouchesMode", "(JI)V", (void*)nativeSetBlockUntrustedTouchesMode}, {"nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIII)I", (void*)nativeInjectInputEvent}, {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", diff --git a/services/core/xsd/vts/Android.mk b/services/core/xsd/vts/Android.mk deleted file mode 100644 index 6dc2c437a2f9..000000000000 --- a/services/core/xsd/vts/Android.mk +++ /dev/null @@ -1,22 +0,0 @@ -# -# Copyright (C) 2019 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := VtsValidateDefaultPermissions -include test/vts/tools/build/Android.host_config.mk diff --git a/services/core/xsd/vts/AndroidTest.xml b/services/core/xsd/vts/AndroidTest.xml deleted file mode 100644 index 4f3b2ef1ace1..000000000000 --- a/services/core/xsd/vts/AndroidTest.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Config for VTS VtsValidateDefaultPermissions."> - <option name="config-descriptor:metadata" key="plan" value="vts-treble" /> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher"> - <option name="abort-on-push-failure" value="false"/> - <option name="push-group" value="HostDrivenTest.push"/> - <option name="push" value="DATA/etc/default-permissions.xsd->/data/local/tmp/default-permissions.xsd"/> - </target_preparer> - <test class="com.android.tradefed.testtype.VtsMultiDeviceTest"> - <option name="test-module-name" value="VtsValidateDefaultPermissions"/> - <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_defaultPermissions_validate_test/vts_defaultPermissions_validate_test" /> - <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_defaultPermissions_validate_test/vts_defaultPermissions_validate_test" /> - <option name="binary-test-type" value="gtest"/> - <option name="test-timeout" value="30s"/> - </test> -</configuration> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 09ba6efcfab8..5f6ac10bee40 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2128,10 +2128,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle, boolean parent) { ensureLocked(); - Preconditions.checkCallAuthorization(!parent || isManagedProfile(userHandle), - String.format("You can not call APIs on the parent profile outside a " - + "managed profile, userId = %d", userHandle)); - + if (parent) { + Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format( + "You can not call APIs on the parent profile outside a managed profile, " + + "userId = %d", userHandle)); + } ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); if (admin != null && parent) { admin = admin.getParentActiveAdmin(); @@ -2301,9 +2302,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean parent, @Nullable String permission) throws SecurityException { ensureLocked(); - Preconditions.checkCallingUser(!parent - || isManagedProfile(getCallerIdentity().getUserId())); - + if (parent) { + Preconditions.checkCallingUser(isManagedProfile(getCallerIdentity().getUserId())); + } ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked( who, reqPolicy, permission); return parent ? admin.getParentActiveAdmin() : admin; @@ -3178,10 +3179,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(adminReceiver, "ComponentName is null"); - - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call forceRemoveActiveAdmin"); - + enforceShell("forceRemoveActiveAdmin"); mInjector.binderWithCleanCallingIdentity(() -> { synchronized (getLockObject()) { if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) { @@ -3260,6 +3258,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return (admin != null) && admin.testOnlyAdmin; } + private void enforceShell(String method) { + final int callingUid = mInjector.binderGetCallingUid(); + if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) { + throw new SecurityException("Non-shell user attempted to call " + method); + } + } + @Override public void removeActiveAdmin(ComponentName adminReceiver, int userHandle) { if (!mHasFeature) { @@ -3269,8 +3274,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); - Preconditions.checkState(mUserManager.isUserUnlocked(userHandle), - "User must be running and unlocked"); + enforceUserUnlocked(userHandle); synchronized (getLockObject()) { ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle); @@ -3284,8 +3288,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { adminReceiver); return; } - Preconditions.checkCallAuthorization((admin.getUid() == caller.getUid()) - || hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); + if (admin.getUid() != mInjector.binderGetCallingUid()) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_DEVICE_ADMINS, null); + } mInjector.binderWithCleanCallingIdentity(() -> removeActiveAdminLocked(adminReceiver, userHandle)); } @@ -3293,8 +3299,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isSeparateProfileChallengeAllowed(int userHandle) { - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can query separate challenge support"); + enforceSystemCaller("query separate challenge support"); ComponentName profileOwner = getProfileOwner(userHandle); // Profile challenge is supported on N or newer release. @@ -4066,9 +4071,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); - Preconditions.checkState( - mUserManager.isUserUnlocked(parent ? getProfileParentId(userHandle) : userHandle), - "User must be running and unlocked"); + enforceUserUnlocked(userHandle, parent); synchronized (getLockObject()) { // This API can only be called by an active device admin, @@ -4109,15 +4112,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format( "can not call APIs refering to the parent profile outside a managed profile, " + "userId = %d", userHandle)); - Preconditions.checkState(mUserManager.isUserUnlocked(getProfileParentId(userHandle)), - "User must be running and unlocked"); synchronized (getLockObject()) { + final int targetUser = getProfileParentId(userHandle); + enforceUserUnlocked(targetUser, false); int credentialOwner = getCredentialOwner(userHandle, false); DevicePolicyData policy = getUserDataUnchecked(credentialOwner); PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner); - return isActivePasswordSufficientForUserLocked(policy.mPasswordValidAtLastCheckpoint, - metrics, getProfileParentId(userHandle), false); + return isActivePasswordSufficientForUserLocked( + policy.mPasswordValidAtLastCheckpoint, metrics, targetUser, false); } } @@ -4133,8 +4136,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(!isManagedProfile(userHandle), String.format( "You can not check password sufficiency for a managed profile, userId = %d", userHandle)); - Preconditions.checkState(mUserManager.isUserUnlocked(userHandle), - "User must be running and unlocked"); + enforceUserUnlocked(userHandle); synchronized (getLockObject()) { PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(userHandle); @@ -4192,22 +4194,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override @PasswordComplexity public int getPasswordComplexity(boolean parent) { - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkState(mUserManager.isUserUnlocked(caller.getUserId()), - "User must be running and unlocked"); - Preconditions.checkCallAuthorization(!parent || isCallerDeviceOwner(caller.getUid()) - || isCallerProfileOwner(caller.getUid()) || isSystemUid(caller)); - Preconditions.checkCallAuthorization( - hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY)); - DevicePolicyEventLogger .createEvent(DevicePolicyEnums.GET_USER_PASSWORD_COMPLEXITY_LEVEL) .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT, - mInjector.getPackageManager().getPackagesForUid(caller.getUid())) + mInjector.getPackageManager().getPackagesForUid( + mInjector.binderGetCallingUid())) .write(); + final int callingUserId = mInjector.userHandleGetCallingUserId(); + + if (parent) { + enforceProfileOwnerOrSystemUser(); + } + enforceUserUnlocked(callingUserId); + mContext.enforceCallingOrSelfPermission( + REQUEST_PASSWORD_COMPLEXITY, + "Must have " + REQUEST_PASSWORD_COMPLEXITY + " permission."); synchronized (getLockObject()) { - final int credentialOwner = getCredentialOwner(caller.getUserId(), parent); + final int credentialOwner = getCredentialOwner(callingUserId, parent); PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner); return metrics == null ? PASSWORD_COMPLEXITY_NONE : metrics.determineComplexity(); } @@ -4356,24 +4360,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slog.w(LOG_TAG, "Cannot reset password when the device has no lock screen"); return false; } + if (password == null) password = ""; + final int callingUid = mInjector.binderGetCallingUid(); + final int userHandle = mInjector.userHandleGetCallingUserId(); - final CallerIdentity caller = getCallerIdentity(); - // As of R, only privileged caller holding RESET_PASSWORD can call resetPassword() to + // As of R, only privlleged caller holding RESET_PASSWORD can call resetPassword() to // set password to an unsecured user. if (hasCallingPermission(permission.RESET_PASSWORD)) { - if (password == null) { - password = ""; - } - return setPasswordPrivileged(password, flags, caller.getUid()); + return setPasswordPrivileged(password, flags, callingUid); } synchronized (getLockObject()) { // If caller has PO (or DO) throw or fail silently depending on its target SDK level. ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked( - null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, caller.getUid()); + null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid); if (admin != null) { - if (getTargetSdk(admin.info.getPackageName(), - caller.getUserId()) < Build.VERSION_CODES.O) { + if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) { Slog.e(LOG_TAG, "DPC can no longer call resetPassword()"); return false; } @@ -4384,7 +4386,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { admin = getActiveAdminForCallerLocked( null, DeviceAdminInfo.USES_POLICY_RESET_PASSWORD, false); if (getTargetSdk(admin.info.getPackageName(), - caller.getUserId()) <= android.os.Build.VERSION_CODES.M) { + userHandle) <= android.os.Build.VERSION_CODES.M) { Slog.e(LOG_TAG, "Device admin can no longer call resetPassword()"); return false; } @@ -4480,9 +4482,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean getDoNotAskCredentialsOnBoot() { - Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( - permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT)); - + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT, null); synchronized (getLockObject()) { DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM); return policyData.mDoNotAskCredentialsOnBoot; @@ -5224,13 +5225,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias, final IBinder response) { - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isSystemUid(caller), - "Only the system can choose private key alias"); + enforceSystemCaller("choose private key alias"); + final UserHandle caller = mInjector.binderGetCallingUserHandle(); // If there is a profile owner, redirect to that; otherwise query the device owner. - ComponentName aliasChooser = getProfileOwner(caller.getUserId()); - if (aliasChooser == null && caller.getUserHandle().isSystem()) { + ComponentName aliasChooser = getProfileOwner(caller.getIdentifier()); + if (aliasChooser == null && caller.isSystem()) { synchronized (getLockObject()) { final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked(); if (deviceOwnerAdmin != null) { @@ -5252,7 +5252,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final ComponentName delegateReceiver; delegateReceiver = resolveDelegateReceiver(DELEGATION_CERT_SELECTION, - DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS, caller.getUserId()); + DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS, caller.getIdentifier()); final boolean isDelegate; if (delegateReceiver != null) { @@ -5264,8 +5264,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } mInjector.binderWithCleanCallingIdentity(() -> { - mContext.sendOrderedBroadcastAsUser(intent, caller.getUserHandle(), null, - new BroadcastReceiver() { + mContext.sendOrderedBroadcastAsUser(intent, caller, null, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String chosenAlias = getResultData(); @@ -5429,14 +5428,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String delegatePackage) throws SecurityException { Objects.requireNonNull(delegatePackage, "Delegate package is null"); - final CallerIdentity caller = getCallerIdentity(who, delegatePackage); - Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isDeviceOwner(caller) - || isProfileOwner(caller))) || (caller.hasPackage() && isCallingFromPackage( - delegatePackage, caller.getUid()))); - // Retrieve the user ID of the calling process. + final int callingUid = mInjector.binderGetCallingUid(); + final int userId = UserHandle.getUserId(callingUid); synchronized (getLockObject()) { - final DevicePolicyData policy = getUserData(caller.getUserId()); + // Ensure calling process is device/profile owner. + if (who != null) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + // Or ensure calling process is delegatePackage itself. + } else { + if (!isCallingFromPackage(delegatePackage, callingUid)) { + throw new SecurityException("Caller with uid " + callingUid + " is not " + + delegatePackage); + } + } + final DevicePolicyData policy = getUserData(userId); // Retrieve the scopes assigned to delegatePackage, or null if no scope was given. final List<String> scopes = policy.mDelegationMap.get(delegatePackage); return scopes == null ? Collections.EMPTY_LIST : scopes; @@ -5715,9 +5721,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public String getAlwaysOnVpnPackageForUser(int userHandle) { - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can get always on VPN package for user"); - + enforceSystemCaller("getAlwaysOnVpnPackageForUser"); synchronized (getLockObject()) { ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle); return admin != null ? admin.mAlwaysOnVpnPackage : null; @@ -5738,9 +5742,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isAlwaysOnVpnLockdownEnabledForUser(int userHandle) { - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can query always on VPN lockdown enabled for user"); - + enforceSystemCaller("isAlwaysOnVpnLockdownEnabledForUser"); synchronized (getLockObject()) { ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle); return admin != null ? admin.mAlwaysOnVpnLockdown : null; @@ -5971,18 +5973,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return null; } - final CallerIdentity caller = getCallerIdentityOptionalAdmin(who); final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow(); final ActiveAdmin admin; synchronized (getLockObject()) { if (who == null) { - Preconditions.checkCallAuthorization(frpManagementAgentUid == caller.getUid() + Preconditions.checkCallAuthorization( + frpManagementAgentUid == mInjector.binderGetCallingUid() || hasCallingPermission(permission.MASTER_CLEAR), "Must be called by the FRP management agent on device"); admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked( UserHandle.getUserId(frpManagementAgentUid)); } else { + final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization( isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); admin = getProfileOwnerOrDeviceOwnerLocked(caller); @@ -6046,7 +6049,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) { return; } - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity())); + + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(isSystemUid(caller)); // Managed Profile password can only be changed when it has a separate challenge. if (!isSeparateProfileChallengeEnabled(userId)) { Preconditions.checkCallAuthorization(!isManagedProfile(userId), String.format("You can " @@ -6566,8 +6571,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(!parent - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + if (parent) { + Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller)); + } synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked(who, @@ -6941,8 +6947,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(!parent - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + if (parent) { + Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller)); + } final int userHandle = caller.getUserId(); synchronized (getLockObject()) { @@ -7180,14 +7187,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + " as device owner for user " + userId); return false; } - Objects.requireNonNull(admin, "ComponentName is null"); - Preconditions.checkArgument(isPackageInstalledForUser(admin.getPackageName(), userId), - String.format("Invalid component %s for device owner", admin)); - - final CallerIdentity caller = getCallerIdentity(); + if (admin == null + || !isPackageInstalledForUser(admin.getPackageName(), userId)) { + throw new IllegalArgumentException("Invalid component " + admin + + " for device owner"); + } + final boolean hasIncompatibleAccountsOrNonAdb = + hasIncompatibleAccountsOrNonAdbNoLock(userId, admin); synchronized (getLockObject()) { - enforceCanSetDeviceOwnerLocked(caller, userId); - + enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccountsOrNonAdb); final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId); if (activeAdmin == null || getUserData(userId).mRemovingAdmins.contains(admin)) { @@ -7196,7 +7204,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Shutting down backup manager service permanently. toggleBackupServiceActive(UserHandle.USER_SYSTEM, /* makeActive= */ false); - if (isAdb(caller)) { + if (isAdb()) { // Log device owner provisioning was started using adb. MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER); DevicePolicyEventLogger @@ -7234,13 +7242,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean hasDeviceOwner() { - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization( - isCallerDeviceOwner(caller.getUid()) || canManageUsers(caller)); - + enforceDeviceOwnerOrManageUsers(); return mOwners.hasDeviceOwner(); } + boolean isDeviceOwner(ActiveAdmin admin) { + return isDeviceOwner(admin.info.getComponent(), admin.getUserHandle().getIdentifier()); + } + public boolean isDeviceOwner(ComponentName who, int userId) { synchronized (getLockObject()) { return mOwners.hasDeviceOwner() @@ -7406,20 +7415,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void clearDeviceOwner(String packageName) { Objects.requireNonNull(packageName, "packageName is null"); - - final CallerIdentity caller = getCallerIdentity(packageName); - Preconditions.checkCallAuthorization(isCallingFromPackage(packageName, caller.getUid()), - "Invalid packageName"); - + final int callingUid = mInjector.binderGetCallingUid(); + if (!isCallingFromPackage(packageName, callingUid)) { + throw new SecurityException("Invalid packageName"); + } synchronized (getLockObject()) { final ComponentName deviceOwnerComponent = mOwners.getDeviceOwnerComponent(); - final int deviceOwnerUserId = caller.getUserId(); - Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid()) - && deviceOwnerComponent.getPackageName().equals(packageName), - "clearDeviceOwner can only be called by the device owner"); - Preconditions.checkState(mUserManager.isUserUnlocked(deviceOwnerUserId), - "User must be running and unlocked"); - + final int deviceOwnerUserId = mOwners.getDeviceOwnerUserId(); + if (!mOwners.hasDeviceOwner() + || !deviceOwnerComponent.getPackageName().equals(packageName) + || (deviceOwnerUserId != UserHandle.getUserId(callingUid))) { + throw new SecurityException( + "clearDeviceOwner can only be called by the device owner"); + } + enforceUserUnlocked(deviceOwnerUserId); DevicePolicyData policy = getUserData(deviceOwnerUserId); if (policy.mPasswordTokenHandle != 0) { mLockPatternUtils.removeEscrowToken(policy.mPasswordTokenHandle, deviceOwnerUserId); @@ -7504,13 +7513,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + " as profile owner for user " + userHandle); return false; } - Objects.requireNonNull(who, "ComponentName is null"); - Preconditions.checkArgument(isPackageInstalledForUser(who.getPackageName(), userHandle), - String.format("Component %s not installed for userId: %d", who, userHandle)); + if (who == null + || !isPackageInstalledForUser(who.getPackageName(), userHandle)) { + throw new IllegalArgumentException("Component " + who + + " not installed for userId:" + userHandle); + } - final CallerIdentity caller = getCallerIdentity(); + final boolean hasIncompatibleAccountsOrNonAdb = + hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who); synchronized (getLockObject()) { - enforceCanSetProfileOwnerLocked(caller, userHandle); + enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccountsOrNonAdb); final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); if (admin == null || getUserData(userHandle).mRemovingAdmins.contains(who)) { @@ -7528,7 +7540,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } - if (isAdb(caller)) { + if (isAdb()) { // Log profile owner provisioning was started using adb. MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_PROFILE_OWNER); DevicePolicyEventLogger @@ -7561,47 +7573,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - /** - * The profile owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS - * permission. - * The profile owner can only be set before the user setup phase has completed, - * except for: - * - SYSTEM_UID - * - adb unless hasIncompatibleAccountsOrNonAdb is true. - */ - private void enforceCanSetProfileOwnerLocked(CallerIdentity caller, int userHandle) { - UserInfo info = getUserInfo(userHandle); - Preconditions.checkArgument(info != null, - String.format("Attempted to set profile owner for invalid userId: %d", userHandle)); - Preconditions.checkState(!info.isGuest(), "Cannot set a profile owner on a guest"); - Preconditions.checkState(!mOwners.hasProfileOwner(userHandle), - "Trying to set the profile owner, but profile owner is already set."); - Preconditions.checkState( - !mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != userHandle, - "Trying to set the profile owner, but the user already has a device owner."); - - - boolean hasUserSetupCompleted = mIsWatch || hasUserSetupCompleted(userHandle); - if (isAdb(caller)) { - Preconditions.checkState(!hasUserSetupCompleted - || !hasIncompatibleAccountsOrNonAdbNoLock(userHandle, caller), - "Not allowed to set the profile owner because there are already some accounts" - + " on the profile"); - return; - } - Preconditions.checkCallAuthorization( - hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); - if (hasUserSetupCompleted) { - Preconditions.checkState(isSystemUid(caller), - "Cannot set the profile owner on a user which is already set-up"); - if (!mIsWatch) { - Preconditions.checkState(isDefaultSupervisor(caller), - String.format("Unable to set non-default profile owner post-setup %s", - caller.getUserHandle())); - } - } - } - private void toggleBackupServiceActive(int userId, boolean makeActive) { long ident = mInjector.binderClearCallingIdentity(); try { @@ -7628,9 +7599,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); final int userId = caller.getUserId(); Preconditions.checkCallingUser(!isManagedProfile(userId)); - Preconditions.checkState(mUserManager.isUserUnlocked(userId), - "User must be running and unlocked"); + enforceUserUnlocked(userId); synchronized (getLockObject()) { // Check if this is the profile owner who is calling final ActiveAdmin admin = @@ -7763,24 +7733,28 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + userHandle); return; } - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkState(userHandle == mOwners.getDeviceOwnerUserId() - || hasProfileOwner(userHandle) || isManagedProfile(caller.getUserId()), - "Not allowed to change provisioning state unless " - + "a device or profile owner is set."); + + if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle) + && getManagedUserId(userHandle) == -1) { + // No managed device, user or profile, so setting provisioning state makes no sense. + throw new IllegalStateException("Not allowed to change provisioning state unless a " + + "device or profile owner is set."); + } synchronized (getLockObject()) { boolean transitionCheckNeeded = true; // Calling identity/permission checks. - if (isAdb(caller)) { + if (isAdb()) { // ADB shell can only move directly from un-managed to finalized as part of directly // setting profile-owner or device-owner. - Preconditions.checkState(getUserProvisioningState(userHandle) - == DevicePolicyManager.STATE_USER_UNMANAGED - && newState == DevicePolicyManager.STATE_USER_SETUP_FINALIZED, - "Not allowed to change provisioning state unless current provisioning " - + "state is unmanaged, and new state is finalized."); + if (getUserProvisioningState(userHandle) != + DevicePolicyManager.STATE_USER_UNMANAGED + || newState != DevicePolicyManager.STATE_USER_SETUP_FINALIZED) { + throw new IllegalStateException("Not allowed to change provisioning state " + + "unless current provisioning state is unmanaged, and new state is " + + "finalized."); + } transitionCheckNeeded = false; } else { Preconditions.checkCallAuthorization( @@ -8016,7 +7990,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) { - enforceCallerIdentityMatchesIfNotSystem(packageName, pid, uid); + ensureCallerIdentityMatchesIfNotSystem(packageName, pid, uid); // Verify that the specified packages matches the provided uid. if (!doesPackageMatchUid(packageName, uid)) { @@ -8080,16 +8054,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } - private void enforceCallerIdentityMatchesIfNotSystem(String packageName, int pid, int uid) { + private void ensureCallerIdentityMatchesIfNotSystem(String packageName, int pid, int uid) { // If the caller is not a system app then it should only be able to check its own device // identifier access. + int callingUid = mInjector.binderGetCallingUid(); int callingPid = mInjector.binderGetCallingPid(); - final CallerIdentity caller = getCallerIdentity(); - if (UserHandle.getAppId(caller.getUid()) >= Process.FIRST_APPLICATION_UID - && (caller.getUid() != uid || callingPid != pid)) { - String message = String.format("Calling uid %d, pid %d cannot check device identifier " - + "access for package %s (uid=%d, pid=%d)", - caller.getUid(), callingPid, packageName, uid, pid); + if (UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID + && (callingUid != uid || callingPid != pid)) { + String message = String.format( + "Calling uid %d, pid %d cannot check device identifier access for package %s " + + "(uid=%d, pid=%d)", callingUid, callingPid, packageName, uid, pid); Log.w(LOG_TAG, message); throw new SecurityException(message); } @@ -8127,18 +8101,83 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** + * The profile owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS + * permission. + * The profile owner can only be set before the user setup phase has completed, + * except for: + * - SYSTEM_UID + * - adb unless hasIncompatibleAccountsOrNonAdb is true. + */ + private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle, + boolean hasIncompatibleAccountsOrNonAdb) { + UserInfo info = getUserInfo(userHandle); + if (info == null) { + // User doesn't exist. + throw new IllegalArgumentException( + "Attempted to set profile owner for invalid userId: " + userHandle); + } + if (info.isGuest()) { + throw new IllegalStateException("Cannot set a profile owner on a guest"); + } + if (mOwners.hasProfileOwner(userHandle)) { + throw new IllegalStateException("Trying to set the profile owner, but profile owner " + + "is already set."); + } + if (mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerUserId() == userHandle) { + throw new IllegalStateException("Trying to set the profile owner, but the user " + + "already has a device owner."); + } + if (isAdb()) { + if ((mIsWatch || hasUserSetupCompleted(userHandle)) + && hasIncompatibleAccountsOrNonAdb) { + throw new IllegalStateException("Not allowed to set the profile owner because " + + "there are already some accounts on the profile"); + } + return; + } + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + + if ((mIsWatch || hasUserSetupCompleted(userHandle))) { + if (!isCallerWithSystemUid()) { + throw new IllegalStateException("Cannot set the profile owner on a user which is " + + "already set-up"); + } + + if (!mIsWatch) { + // Only the default supervision profile owner can be set as profile owner after SUW + final String supervisor = mContext.getResources().getString( + com.android.internal.R.string + .config_defaultSupervisionProfileOwnerComponent); + if (supervisor == null) { + throw new IllegalStateException("Unable to set profile owner post-setup, no" + + "default supervisor profile owner defined"); + } + + final ComponentName supervisorComponent = ComponentName.unflattenFromString( + supervisor); + if (!owner.equals(supervisorComponent)) { + throw new IllegalStateException("Unable to set non-default profile owner" + + " post-setup " + owner); + } + } + } + } + + /** * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS * permission. */ - private void enforceCanSetDeviceOwnerLocked(CallerIdentity caller, int userId) { - if (!isAdb(caller)) { + private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, + @UserIdInt int userId, + boolean hasIncompatibleAccountsOrNonAdb) { + if (!isAdb()) { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); } final int code = checkDeviceOwnerProvisioningPreConditionLocked( - caller.getComponentName(), userId, isAdb(caller), - hasIncompatibleAccountsOrNonAdbNoLock(userId, caller)); + owner, userId, isAdb(), hasIncompatibleAccountsOrNonAdb); if (code != CODE_OK) { throw new IllegalStateException(computeProvisioningErrorString(code, userId)); } @@ -8173,6 +8212,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } + private void enforceUserUnlocked(int userId) { + // Since we're doing this operation on behalf of an app, we only + // want to use the actual "unlocked" state. + Preconditions.checkState(mUserManager.isUserUnlocked(userId), + "User must be running and unlocked"); + } + + private void enforceUserUnlocked(@UserIdInt int userId, boolean parent) { + if (parent) { + enforceUserUnlocked(getProfileParentId(userId)); + } else { + enforceUserUnlocked(userId); + } + } + private boolean canManageUsers(CallerIdentity caller) { return isSystemUid(caller) || isRootUid(caller) || hasCallingOrSelfPermission(permission.MANAGE_USERS); @@ -8205,6 +8259,42 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS); } + private void enforceDeviceOwnerOrManageUsers() { + synchronized (getLockObject()) { + if (getActiveAdminWithPolicyForUidLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, + mInjector.binderGetCallingUid()) != null) { + return; + } + } + Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())); + } + + private void enforceProfileOwnerOrSystemUser() { + synchronized (getLockObject()) { + if (getActiveAdminWithPolicyForUidLocked(null, + DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid()) + != null) { + return; + } + } + Preconditions.checkState(isCallerWithSystemUid(), + "Only profile owner, device owner and system may call this method."); + } + + private void enforceProfileOwnerOrFullCrossUsersPermission(CallerIdentity caller, + int userId) { + if (userId == caller.getUserId()) { + synchronized (getLockObject()) { + if (getActiveAdminWithPolicyForUidLocked(null, + DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, caller.getUid()) != null) { + // Device Owner/Profile Owner may access the user it runs on. + return; + } + } + } + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId)); + } + private boolean canUserUseLockTaskLocked(int userId) { if (isUserAffiliatedWithDeviceLocked(userId)) { return true; @@ -8228,22 +8318,34 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } + private void enforceCanCallLockTaskLocked(ComponentName who) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final int userId = mInjector.userHandleGetCallingUserId(); + if (!canUserUseLockTaskLocked(userId)) { + throw new SecurityException("User " + userId + " is not allowed to use lock task"); + } + } + private void ensureCallerPackage(@Nullable String packageName) { - final CallerIdentity caller = getCallerIdentity(); if (packageName == null) { - Preconditions.checkCallAuthorization(isSystemUid(caller), - "Only the system can omit package name"); + enforceSystemCaller("omit package name"); } else { + final int callingUid = mInjector.binderGetCallingUid(); + final int userId = mInjector.userHandleGetCallingUserId(); try { final ApplicationInfo ai = mIPackageManager.getApplicationInfo( - packageName, 0, caller.getUserId()); - Preconditions.checkState(ai.uid == caller.getUid(), "Unmatching package name"); + packageName, 0, userId); + Preconditions.checkState(ai.uid == callingUid, "Unmatching package name"); } catch (RemoteException e) { // Shouldn't happen } } } + private boolean isCallerWithSystemUid() { + return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID); + } + private boolean isSystemUid(CallerIdentity caller) { return UserHandle.isSameApp(caller.getUid(), Process.SYSTEM_UID); } @@ -8431,9 +8533,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(admin); Preconditions.checkCallAuthorization(isDeviceOwner(caller) || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller))); - mInjector.binderWithCleanCallingIdentity(() -> Preconditions.checkArgument(!parent - || isSystemPackage(packageName, getProfileParentId(caller.getUserId())), - "The provided package is not a system package")); + if (parent) { + mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage( + packageName, getProfileParentId(mInjector.userHandleGetCallingUserId()))); + } mInjector.binderWithCleanCallingIdentity(() -> SmsApplication.setDefaultApplication(packageName, mContext)); @@ -8459,7 +8562,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isCallerApplicationRestrictionsManagingPackage(String callerPackage) { - return isCallerDelegate(callerPackage, getCallerIdentity().getUid(), + return isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(), DELEGATION_APP_RESTRICTIONS); } @@ -8574,9 +8677,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public ComponentName getRestrictionsProvider(int userHandle) { - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can query the permission provider"); - + enforceSystemCaller("query the permission provider"); synchronized (getLockObject()) { DevicePolicyData userData = getUserData(userHandle); return userData != null ? userData.mRestrictionsProvider : null; @@ -8844,9 +8945,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkStringNotEmpty(packageName, "packageName is null"); - - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can query if an accessibility service is disabled by admin"); + enforceSystemCaller("query if an accessibility service is disabled by admin"); synchronized (getLockObject()) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); @@ -8966,9 +9065,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkStringNotEmpty(packageName, "packageName is null"); - - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can query if an input method is disabled by admin"); + enforceSystemCaller("query if an input method is disabled by admin"); synchronized (getLockObject()) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); @@ -9025,10 +9122,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return true; } - Preconditions.checkStringNotEmpty(packageName, "packageName is null or empty"); - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can query if a notification listener service is permitted"); + Preconditions.checkStringNotEmpty(packageName, "packageName is null or empty"); + enforceSystemCaller("query if a notification listener service is permitted"); synchronized (getLockObject()) { ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId); @@ -9041,6 +9137,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void enforceSystemCaller(String action) { + if (!isCallerWithSystemUid()) { + throw new SecurityException("Only the system can " + action); + } + } + private void maybeSendAdminEnabledBroadcastLocked(int userHandle) { DevicePolicyData policyData = getUserData(userHandle); if (policyData.mAdminBroadcastPending) { @@ -9070,14 +9172,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ComponentName profileOwner, PersistableBundle adminExtras, int flags) { Objects.requireNonNull(admin, "admin is null"); Objects.requireNonNull(profileOwner, "profileOwner is null"); - Preconditions.checkArgument(admin.getPackageName().equals(profileOwner.getPackageName()), - String.format("profileOwner %s and admin %s are not in the same package", - profileOwner, admin)); - - final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(), - "createAndManageUser was called from non-system user"); - + if (!admin.getPackageName().equals(profileOwner.getPackageName())) { + throw new IllegalArgumentException("profileOwner " + profileOwner + " and admin " + + admin + " are not in the same package"); + } + // Only allow the system user to use this method + if (!mInjector.binderGetCallingUserHandle().isSystem()) { + throw new SecurityException("createAndManageUser was called from non-system user"); + } final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0; final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0 && UserManager.isDeviceInDemoMode(mContext); @@ -9087,12 +9189,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Create user. UserHandle user = null; synchronized (getLockObject()) { - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + final int callingUid = mInjector.binderGetCallingUid(); final long id = mInjector.binderClearCallingIdentity(); try { - targetSdkVersion = mInjector.getPackageManagerInternal() - .getUidTargetSdkVersion(caller.getUid()); + targetSdkVersion = mInjector.getPackageManagerInternal().getUidTargetSdkVersion( + callingUid); // Return detail error code for checks inside // UserManagerService.createUserInternalUnchecked. @@ -9620,8 +9723,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // API cannot be used to leak if certain non-system package exists in the person // profile. mInjector.binderWithCleanCallingIdentity(() -> - Preconditions.checkArgument(isSystemPackage(packageName, userId), - "The provided package is not a system package")); + enforcePackageIsSystemPackage(packageName, userId)); } result = mInjector.binderWithCleanCallingIdentity(() -> mIPackageManager .setApplicationHiddenSettingAsUser(packageName, hidden, userId)); @@ -9652,8 +9754,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { && isManagedProfile(caller.getUserId())); // Ensure the package provided is a system package. mInjector.binderWithCleanCallingIdentity(() -> - Preconditions.checkArgument(isSystemPackage(packageName, userId), - "The provided package is not a system package")); + enforcePackageIsSystemPackage(packageName, userId)); } return mInjector.binderWithCleanCallingIdentity( @@ -9661,12 +9762,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private boolean isSystemPackage(String packageName, int userId) + private void enforcePackageIsSystemPackage(String packageName, int userId) throws RemoteException { + boolean isSystem; try { - return isSystemApp(mIPackageManager, packageName, userId); + isSystem = isSystemApp(mIPackageManager, packageName, userId); } catch (IllegalArgumentException e) { - return false; + isSystem = false; + } + if (!isSystem) { + throw new IllegalArgumentException("The provided package is not a system package"); } } @@ -10215,12 +10320,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(packages, "packages is null"); - final CallerIdentity caller = getCallerIdentity(who); synchronized (getLockObject()) { - Preconditions.checkCallAuthorization((isDeviceOwner(caller) || isProfileOwner(caller)) - && canUserUseLockTaskLocked(caller.getUserId()), - String.format("User %d is not allowed to use lock task", caller.getUserId())); - setLockTaskPackagesLocked(caller.getUserId(), new ArrayList<>(Arrays.asList(packages))); + enforceCanCallLockTaskLocked(who); + final int userHandle = mInjector.userHandleGetCallingUserId(); + setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages))); } } @@ -10237,12 +10340,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public String[] getLockTaskPackages(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); - final CallerIdentity caller = getCallerIdentity(who); + final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier(); synchronized (getLockObject()) { - Preconditions.checkCallAuthorization((isDeviceOwner(caller) || isProfileOwner(caller)) - && canUserUseLockTaskLocked(caller.getUserId()), - String.format("User %d is not allowed to use lock task", caller.getUserId())); - final List<String> packages = getUserData(caller.getUserId()).mLockTaskPackages; + enforceCanCallLockTaskLocked(who); + final List<String> packages = getUserData(userHandle).mLockTaskPackages; return packages.toArray(new String[packages.size()]); } } @@ -10258,6 +10359,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setLockTaskFeatures(ComponentName who, int flags) { Objects.requireNonNull(who, "ComponentName is null"); + // Throw if Overview is used without Home. boolean hasHome = (flags & LOCK_TASK_FEATURE_HOME) != 0; boolean hasOverview = (flags & LOCK_TASK_FEATURE_OVERVIEW) != 0; @@ -10267,12 +10369,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkArgument(hasHome || !hasNotification, "Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME"); - final CallerIdentity caller = getCallerIdentity(who); + final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { - Preconditions.checkCallAuthorization((isDeviceOwner(caller) || isProfileOwner(caller)) - && canUserUseLockTaskLocked(caller.getUserId()), - String.format("User %d is not allowed to use lock task", caller.getUserId())); - setLockTaskFeaturesLocked(caller.getUserId(), flags); + enforceCanCallLockTaskLocked(who); + setLockTaskFeaturesLocked(userHandle, flags); } } @@ -10286,13 +10386,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public int getLockTaskFeatures(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); - - final CallerIdentity caller = getCallerIdentity(who); + final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { - Preconditions.checkCallAuthorization((isDeviceOwner(caller) || isProfileOwner(caller)) - && canUserUseLockTaskLocked(caller.getUserId()), - String.format("User %d is not allowed to use lock task", caller.getUserId())); - return getUserData(caller.getUserId()).mLockTaskFeatures; + enforceCanCallLockTaskLocked(who); + return getUserData(userHandle).mLockTaskFeatures; } } @@ -10323,9 +10420,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) { - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can notify lock task mode changed"); - + enforceSystemCaller("call notifyLockTaskModeChanged"); synchronized (getLockObject()) { final DevicePolicyData policy = getUserData(userHandle); @@ -10950,9 +11045,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public boolean isActiveAdminWithPolicy(int uid, int reqPolicy) { + public boolean isActiveDeviceOwner(int uid) { + synchronized (getLockObject()) { + return getActiveAdminWithPolicyForUidLocked( + null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, uid) != null; + } + } + + @Override + public boolean isActiveProfileOwner(int uid) { synchronized (getLockObject()) { - return getActiveAdminWithPolicyForUidLocked(null, reqPolicy, uid) != null; + return getActiveAdminWithPolicyForUidLocked( + null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, uid) != null; } } @@ -11076,8 +11180,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } if (isUserAffiliatedWithDevice(UserHandle.getUserId(callerUid)) - && isActiveAdminWithPolicy(callerUid, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) { + && (isActiveProfileOwner(callerUid) + || isActiveDeviceOwner(callerUid))) { // device owner or a profile owner affiliated with the device owner return true; } @@ -11299,13 +11403,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public Intent createAdminSupportIntent(String restriction) { Objects.requireNonNull(restriction); - - final CallerIdentity caller = getCallerIdentity(); + final int uid = mInjector.binderGetCallingUid(); + final int userId = UserHandle.getUserId(uid); Intent intent = null; if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) || DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) { synchronized (getLockObject()) { - final DevicePolicyData policy = getUserData(caller.getUserId()); + final DevicePolicyData policy = getUserData(userId); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { final ActiveAdmin admin = policy.mAdminList.get(i); @@ -11313,8 +11417,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) || (admin.disableScreenCapture && DevicePolicyManager .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) { - intent = createShowAdminSupportIntent(admin.info.getComponent(), - caller.getUserId()); + intent = createShowAdminSupportIntent(admin.info.getComponent(), userId); break; } } @@ -11331,8 +11434,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } else { // if valid, |restriction| can only be a user restriction - intent = mLocalService.createUserRestrictionSupportIntent( - caller.getUserId(), restriction); + intent = mLocalService.createUserRestrictionSupportIntent(userId, restriction); } if (intent != null) { intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, restriction); @@ -11465,9 +11567,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void clearSystemUpdatePolicyFreezePeriodRecord() { - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call clearSystemUpdatePolicyFreezePeriodRecord"); - + enforceShell("clearSystemUpdatePolicyFreezePeriodRecord"); synchronized (getLockObject()) { // Print out current record to help diagnosed CTS failures Slog.i(LOG_TAG, "Clear freeze period record: " @@ -11479,8 +11579,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** - * Checks if the caller of the method is the device owner app. This method should only be called - * if not componentName is available. + * Checks if the caller of the method is the device owner app. * * @param callerUid UID of the caller. * @return true if the caller is the device owner app @@ -11496,47 +11595,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent() .getPackageName(); - try { - String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(callerUid); - if (pkgs != null) { + try { + String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(callerUid); for (String pkg : pkgs) { if (deviceOwnerPackageName.equals(pkg)) { return true; } } + } catch (RemoteException e) { + return false; } - } catch (RemoteException e) { - return false; - } - } - return false; - } - - /** - * Checks if the caller of the method is the profile owner. This method should only be called - * if not componentName is available. - * - * @param callerUid UID of the caller. - * @return true if the caller is the profile owner - */ - private boolean isCallerProfileOwner(int callerUid) { - final int userId = UserHandle.getUserId(callerUid); - for (ActiveAdmin admin : getUserData(userId).mAdminList) { - if (admin.getUid() == callerUid && isProfileOwner(admin.info.getComponent(), userId)) { - return true; - } } return false; } @Override public void notifyPendingSystemUpdate(@Nullable SystemUpdateInfo info) { - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization( - hasCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE), + mContext.enforceCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE, "Only the system update service can broadcast update information"); - if (!caller.getUserHandle().isSystem()) { + if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) { Slog.w(LOG_TAG, "Only the system update service in the system user " + "can broadcast update information."); return; @@ -11764,12 +11842,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean isProvisioningAllowed(String action, String packageName) { Objects.requireNonNull(packageName); - final CallerIdentity caller = getCallerIdentity(); + final int callingUid = mInjector.binderGetCallingUid(); final long ident = mInjector.binderClearCallingIdentity(); try { final int uidForPackage = mInjector.getPackageManager().getPackageUidAsUser( - packageName, caller.getUserId()); - Preconditions.checkArgument(caller.getUid() == uidForPackage, + packageName, UserHandle.getUserId(callingUid)); + Preconditions.checkArgument(callingUid == uidForPackage, "Caller uid doesn't match the one for the provided package."); } catch (NameNotFoundException e) { throw new IllegalArgumentException("Invalid package provided " + packageName, e); @@ -12080,18 +12158,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(who, "ComponentName is null"); - - final CallerIdentity caller = getCallerIdentity(); + final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForUidLocked(who, caller.getUid()); + ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid()); if (!TextUtils.equals(admin.shortSupportMessage, message)) { admin.shortSupportMessage = message; - saveSettingsLocked(caller.getUserId()); + saveSettingsLocked(userHandle); } } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_SHORT_SUPPORT_MESSAGE) - .setAdmin(caller.getComponentName()) + .setAdmin(who) .write(); } @@ -12101,9 +12178,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } Objects.requireNonNull(who, "ComponentName is null"); - synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForUidLocked(who, getCallerIdentity().getUid()); + ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid()); return admin.shortSupportMessage; } } @@ -12114,18 +12190,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(who, "ComponentName is null"); - - final CallerIdentity caller = getCallerIdentity(); + final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForUidLocked(who, caller.getUid()); + ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid()); if (!TextUtils.equals(admin.longSupportMessage, message)) { admin.longSupportMessage = message; - saveSettingsLocked(caller.getUserId()); + saveSettingsLocked(userHandle); } } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_LONG_SUPPORT_MESSAGE) - .setAdmin(caller.getComponentName()) + .setAdmin(who) .write(); } @@ -12135,9 +12210,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } Objects.requireNonNull(who, "ComponentName is null"); - synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForUidLocked(who, getCallerIdentity().getUid()); + ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid()); return admin.longSupportMessage; } } @@ -12148,9 +12222,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } Objects.requireNonNull(who, "ComponentName is null"); - - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can query support message for user"); + enforceSystemCaller("query support message for user"); synchronized (getLockObject()) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); @@ -12167,9 +12239,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } Objects.requireNonNull(who, "ComponentName is null"); - - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can query support message for user"); + enforceSystemCaller("query support message for user"); synchronized (getLockObject()) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); @@ -12296,10 +12366,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return null; } - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization( - isCallerDeviceOwner(caller.getUid()) || canManageUsers(caller)); - + enforceDeviceOwnerOrManageUsers(); synchronized (getLockObject()) { final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked(); return deviceOwnerAdmin == null ? null : deviceOwnerAdmin.organizationName; @@ -12396,13 +12463,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isMeteredDataDisabledPackageForUser(ComponentName who, String packageName, int userId) { + Objects.requireNonNull(who); + if (!mHasFeature) { return false; } - Objects.requireNonNull(who, "ComponentName is null"); - - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can query restricted pkgs for a specific user"); + enforceSystemCaller("query restricted pkgs for a specific user"); synchronized (getLockObject()) { final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId); @@ -12415,27 +12481,32 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) { - if (!mHasFeature) { - return; - } // As the caller is the system, it must specify the component name of the profile owner // as a sanity / safety check. Objects.requireNonNull(who); - final CallerIdentity caller = getCallerIdentity(); + if (!mHasFeature) { + return; + } + // Only adb or system apps with the right permission can mark a profile owner on // organization-owned device. - Preconditions.checkCallAuthorization(isAdb(caller) - || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED), - "Only the system can mark a profile owner of organization-owned device."); - if (isAdb(caller)) { - Preconditions.checkCallAuthorization( - !hasIncompatibleAccountsOrNonAdbNoLock(userId, caller), - "Can only be called from ADB if the device has no accounts."); + if (!(isAdb() || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) { + throw new SecurityException( + "Only the system can mark a profile owner of organization-owned device."); + } + + if (isAdb()) { + if (hasIncompatibleAccountsOrNonAdbNoLock(userId, who)) { + throw new SecurityException( + "Can only be called from ADB if the device has no accounts."); + } } else { - Preconditions.checkState(!hasUserSetupCompleted(UserHandle.USER_SYSTEM), - "Cannot mark profile owner as managing an organization-owned device after " - + "set-up"); + if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) { + throw new IllegalStateException( + "Cannot mark profile owner as managing an organization-owned device after" + + " set-up"); + } } // Grant access under lock. @@ -12654,13 +12725,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return false; } - final CallerIdentity caller = getCallerIdentityOptionalAdmin(admin); synchronized (getLockObject()) { - if (!isSystemUid(caller)) { - Objects.requireNonNull(admin, "ComponentName is null"); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + if (!isCallerWithSystemUid()) { + Objects.requireNonNull(admin); + final CallerIdentity caller = getCallerIdentity(admin); + Preconditions.checkCallAuthorization( + isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller)); } return mInjector.securityLogGetLoggingEnabledProperty(); } @@ -12744,15 +12815,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long forceSecurityLogs() { - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isAdb(caller), - "Non-shell user attempted to call forceSecurityLogs"); - Preconditions.checkState(mInjector.securityLogGetLoggingEnabledProperty(), - "logging is not available"); - + enforceShell("forceSecurityLogs"); + if (!mInjector.securityLogGetLoggingEnabledProperty()) { + throw new IllegalStateException("logging is not available"); + } return mSecurityLogMonitor.forceLogs(); } + private void enforceCallerSystemUserHandle() { + final int callingUid = mInjector.binderGetCallingUid(); + final int userId = UserHandle.getUserId(callingUid); + if (userId != UserHandle.USER_SYSTEM) { + throw new SecurityException("Caller has to be in user 0"); + } + } + @Override public boolean isUninstallInQueue(final String packageName) { final CallerIdentity caller = getCallerIdentity(); @@ -12770,21 +12847,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkArgument(!TextUtils.isEmpty(packageName)); final CallerIdentity caller = getCallerIdentity(); - final int userId = caller.getUserId(); Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); - Preconditions.checkState(mUserManager.isUserUnlocked(userId), - "User must be running and unlocked"); + + final int userId = caller.getUserId(); + enforceUserUnlocked(userId); final ComponentName profileOwner = getProfileOwner(userId); - Preconditions.checkArgument( - profileOwner == null || !packageName.equals(profileOwner.getPackageName()), - "Cannot uninstall a package with a profile owner"); + if (profileOwner != null && packageName.equals(profileOwner.getPackageName())) { + throw new IllegalArgumentException("Cannot uninstall a package with a profile owner"); + } final ComponentName deviceOwner = getDeviceOwnerComponent(/* callingUserOnly= */ false); - Preconditions.checkArgument(deviceOwner == null || getDeviceOwnerUserId() != userId - || !packageName.equals(deviceOwner.getPackageName()), - "Cannot uninstall a package with a device owner"); + if (getDeviceOwnerUserId() == userId && deviceOwner != null + && packageName.equals(deviceOwner.getPackageName())) { + throw new IllegalArgumentException("Cannot uninstall a package with a device owner"); + } final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId); synchronized (getLockObject()) { @@ -12935,24 +13013,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { */ @Override public void forceUpdateUserSetupComplete() { - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); - Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(), - "Caller has to be in user 0"); + enforceCallerSystemUserHandle(); // no effect if it's called from user build if (!mInjector.isBuildDebuggable()) { return; } + final int userId = UserHandle.USER_SYSTEM; boolean isUserCompleted = mInjector.settingsSecureGetIntForUser( - Settings.Secure.USER_SETUP_COMPLETE, 0, caller.getUserId()) != 0; - DevicePolicyData policy = getUserData(caller.getUserId()); + Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0; + DevicePolicyData policy = getUserData(userId); policy.mUserSetupComplete = isUserCompleted; mStateCache.setDeviceProvisioned(isUserCompleted); synchronized (getLockObject()) { - saveSettingsLocked(caller.getUserId()); + saveSettingsLocked(userId); } } @@ -13107,8 +13183,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * * DO NOT CALL IT WITH THE DPMS LOCK HELD. */ - private boolean hasIncompatibleAccountsOrNonAdbNoLock(int userId, CallerIdentity caller) { - if (!isAdb(caller)) { + private boolean hasIncompatibleAccountsOrNonAdbNoLock( + int userId, @Nullable ComponentName owner) { + if (!isAdb()) { return true; } wtfIfInLock(); @@ -13120,8 +13197,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } synchronized (getLockObject()) { - if (caller.getComponentName() == null - || !isAdminTestOnlyLocked(caller.getComponentName(), userId)) { + if (owner == null || !isAdminTestOnlyLocked(owner, userId)) { Log.w(LOG_TAG, "Non test-only owner can't be installed with existing accounts."); return true; @@ -13164,8 +13240,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private boolean isAdb(CallerIdentity caller) { - return isShellUid(caller) || isRootUid(caller); + private boolean isAdb() { + final int callingUid = mInjector.binderGetCallingUid(); + return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID; } @Override @@ -13227,13 +13304,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long forceNetworkLogs() { - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call forceNetworkLogs"); - + enforceShell("forceNetworkLogs"); synchronized (getLockObject()) { - Preconditions.checkState(isNetworkLoggingEnabledInternalLocked(), - "logging is not available"); - + if (!isNetworkLoggingEnabledInternalLocked()) { + throw new IllegalStateException("logging is not available"); + } if (mNetworkLogger != null) { return mInjector.binderWithCleanCallingIdentity( () -> mNetworkLogger.forceBatchFinalization()); @@ -13433,28 +13508,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long getLastSecurityLogRetrievalTime() { - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization( - isCallerDeviceOwner(caller.getUid()) || canManageUsers(caller)); - + enforceDeviceOwnerOrManageUsers(); return getUserData(UserHandle.USER_SYSTEM).mLastSecurityLogRetrievalTime; } @Override public long getLastBugReportRequestTime() { - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization( - isCallerDeviceOwner(caller.getUid()) || canManageUsers(caller)); - + enforceDeviceOwnerOrManageUsers(); return getUserData(UserHandle.USER_SYSTEM).mLastBugReportRequestTime; } @Override public long getLastNetworkLogRetrievalTime() { - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization( - isCallerDeviceOwner(caller.getUid()) || canManageUsers(caller)); - + enforceDeviceOwnerOrManageUsers(); return getUserData(UserHandle.USER_SYSTEM).mLastNetworkLogsRetrievalTime; } @@ -13534,18 +13600,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) { return false; } - Objects.requireNonNull(admin); Objects.requireNonNull(token); - - final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); - synchronized (getLockObject()) { - DevicePolicyData policy = getUserData(caller.getUserId()); + final int userHandle = mInjector.userHandleGetCallingUserId(); + getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + + DevicePolicyData policy = getUserData(userHandle); if (policy.mPasswordTokenHandle != 0) { final String password = passwordOrNull != null ? passwordOrNull : ""; return resetPasswordInternal(password, policy.mPasswordTokenHandle, token, - flags, caller.getUid()); + flags, mInjector.binderGetCallingUid()); } else { Slog.w(LOG_TAG, "No saved token handle"); } @@ -13555,21 +13619,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isCurrentInputMethodSetByOwner() { - final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid()) - || isCallerProfileOwner(caller.getUid()) || isSystemUid(caller)); - - return getUserData(caller.getUserId()).mCurrentInputMethodSet; + enforceProfileOwnerOrSystemUser(); + return getUserData(mInjector.userHandleGetCallingUserId()).mCurrentInputMethodSet; } @Override public StringParceledListSlice getOwnerInstalledCaCerts(@NonNull UserHandle user) { final int userId = user.getIdentifier(); final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid()) - || isCallerProfileOwner(caller.getUid()) - || hasFullCrossUsersPermission(caller, userId)); - + enforceProfileOwnerOrFullCrossUsersPermission(caller, userId); synchronized (getLockObject()) { return new StringParceledListSlice( new ArrayList<>(getUserData(userId).mOwnerInstalledCaCerts)); @@ -14456,12 +14514,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkStringNotEmpty(packageName, "Package name is empty"); - final CallerIdentity caller = getCallerIdentity(packageName); - Preconditions.checkCallAuthorization(isCallingFromPackage(packageName, caller.getUid()), - "Input package name doesn't align with actual calling package."); - + final int callingUid = mInjector.binderGetCallingUid(); + final int callingUserId = mInjector.userHandleGetCallingUserId(); + if (!isCallingFromPackage(packageName, callingUid)) { + throw new SecurityException("Input package name doesn't align with actual " + + "calling package."); + } return mInjector.binderWithCleanCallingIdentity(() -> { - final int workProfileUserId = getManagedUserId(caller.getUserId()); + final int workProfileUserId = getManagedUserId(callingUserId); if (workProfileUserId < 0) { return false; } @@ -14945,9 +15005,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean canProfileOwnerResetPasswordWhenLocked(int userId) { - Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), - "Only the system can query profile owner can reset password when locked"); - + enforceSystemCaller("call canProfileOwnerResetPasswordWhenLocked"); synchronized (getLockObject()) { final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId); if (poAdmin == null diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 8b1e9c52ec58..91cb481a3b23 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -30,6 +30,7 @@ import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.Binder; import android.os.CancellationSignal; +import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; @@ -73,7 +74,7 @@ public class PeopleService extends SystemService { @Override public void onStart() { - publishBinderService(Context.PEOPLE_SERVICE, new BinderService()); + publishBinderService(Context.PEOPLE_SERVICE, mService); publishLocalService(PeopleServiceInternal.class, new LocalService()); } @@ -117,7 +118,7 @@ public class PeopleService extends SystemService { message); } - private final class BinderService extends IPeopleManager.Stub { + final IBinder mService = new IPeopleManager.Stub() { @Override public ParceledListSlice<ConversationChannel> getRecentConversations() { @@ -146,7 +147,7 @@ public class PeopleService extends SystemService { enforceSystemRootOrSystemUI(getContext(), "get last interaction"); return mDataManager.getLastInteraction(packageName, userId, shortcutId); } - } + }; @VisibleForTesting final class LocalService extends PeopleServiceInternal { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java index be258dc5963e..31ec4a53908c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java @@ -66,6 +66,7 @@ import android.os.IRemoteCallback; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; +import android.os.WorkSource; import android.platform.test.annotations.Presubmit; import android.util.Log; @@ -111,6 +112,7 @@ public class LocationProviderManagerTest { private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1, "mypackage", "attribution"); + private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid()); private Random mRandom; @@ -202,22 +204,20 @@ public class LocationProviderManagerTest { @Test public void testIsEnabled() { assertThat(mManager.isEnabled(CURRENT_USER)).isTrue(); + assertThat(mManager.isEnabled(OTHER_USER)).isTrue(); mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER); assertThat(mManager.isEnabled(CURRENT_USER)).isFalse(); + assertThat(mManager.isEnabled(OTHER_USER)).isTrue(); mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER); mProvider.setAllowed(false); assertThat(mManager.isEnabled(CURRENT_USER)).isFalse(); + assertThat(mManager.isEnabled(OTHER_USER)).isFalse(); mProvider.setAllowed(true); - mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER); - assertThat(mManager.isEnabled(CURRENT_USER)).isFalse(); - assertThat(mManager.isEnabled(OTHER_USER)).isTrue(); - - mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER); assertThat(mManager.isEnabled(CURRENT_USER)).isTrue(); - assertThat(mManager.isEnabled(OTHER_USER)).isFalse(); + assertThat(mManager.isEnabled(OTHER_USER)).isTrue(); } @Test @@ -237,23 +237,15 @@ public class LocationProviderManagerTest { mProvider.setAllowed(false); verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER, false); + verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER, + false); mProvider.setAllowed(true); verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER, true); - - mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER); - verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER, - false); verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER, true); - mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER); - verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER, - true); - verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER, - false); - mManager.removeEnabledListener(listener); mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER); verifyNoMoreInteractions(listener); @@ -343,7 +335,7 @@ public class LocationProviderManagerTest { @Test public void testPassive_Listener() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).build(); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); Location loc = createLocation(NAME, mRandom); @@ -368,8 +360,11 @@ public class LocationProviderManagerTest { ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY, - PERMISSION_FINE, listener); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + IDENTITY, + PERMISSION_FINE, + listener); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); @@ -397,16 +392,6 @@ public class LocationProviderManagerTest { mProvider.setAllowed(true); verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, true); - mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER); - verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, false); - loc = createLocation(NAME, mRandom); - mProvider.setProviderLocation(loc); - verify(listener, times(1)).onLocationChanged(any(Location.class), - nullable(IRemoteCallback.class)); - - mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER); - verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, true); - loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); verify(listener, times(2)).onLocationChanged(locationCaptor.capture(), @@ -422,8 +407,11 @@ public class LocationProviderManagerTest { "attribution"); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity, - PERMISSION_FINE, listener); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + identity, + PERMISSION_FINE, + listener); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); @@ -435,8 +423,11 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_Unregister() throws Exception { ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY, - PERMISSION_FINE, listener); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + IDENTITY, + PERMISSION_FINE, + listener); mManager.unregisterLocationRequest(listener); mProvider.setProviderLocation(createLocation(NAME, mRandom)); @@ -453,8 +444,11 @@ public class LocationProviderManagerTest { "attribution"); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity, - PERMISSION_FINE, listener); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + identity, + PERMISSION_FINE, + listener); CountDownLatch blocker = new CountDownLatch(1); IN_PROCESS_EXECUTOR.execute(() -> { @@ -475,7 +469,10 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_NumUpdates() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).setMaxUpdates(5).build(); + LocationRequest request = new LocationRequest.Builder(0) + .setMaxUpdates(5) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mProvider.setProviderLocation(createLocation(NAME, mRandom)); @@ -492,7 +489,10 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_ExpiringAlarm() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(5000).build(); + LocationRequest request = new LocationRequest.Builder(0) + .setDurationMillis(5000) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mInjector.getAlarmHelper().incrementAlarmTime(5000); @@ -504,7 +504,10 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_ExpiringNoAlarm() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(25).build(); + LocationRequest request = new LocationRequest.Builder(0) + .setDurationMillis(25) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); Thread.sleep(25); @@ -517,8 +520,10 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_FastestInterval() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateIntervalMillis( - 5000).build(); + LocationRequest request = new LocationRequest.Builder(5000) + .setMinUpdateIntervalMillis(5000) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mProvider.setProviderLocation(createLocation(NAME, mRandom)); @@ -531,8 +536,10 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_SmallestDisplacement() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateDistanceMeters( - 1f).build(); + LocationRequest request = new LocationRequest.Builder(5000) + .setMinUpdateDistanceMeters(1f) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); Location loc = createLocation(NAME, mRandom); @@ -546,7 +553,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_NoteOpFailure() throws Exception { ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).build(); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, IDENTITY.getPackageName(), @@ -564,8 +571,11 @@ public class LocationProviderManagerTest { "attribution"); ILocationListener listener = createMockLocationListener(); - mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity, - PERMISSION_FINE, listener); + mManager.registerLocationRequest( + new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(), + identity, + PERMISSION_FINE, + listener); CountDownLatch blocker = new CountDownLatch(1); IN_PROCESS_EXECUTOR.execute(() -> { @@ -592,8 +602,8 @@ public class LocationProviderManagerTest { ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class); ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); @@ -606,8 +616,8 @@ public class LocationProviderManagerTest { @Test public void testGetCurrentLocation_Cancel() throws Exception { ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - ICancellationSignal cancellationSignal = mManager.getCurrentLocation(locationRequest, + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + ICancellationSignal cancellationSignal = mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); cancellationSignal.cancel(); @@ -619,8 +629,8 @@ public class LocationProviderManagerTest { @Test public void testGetCurrentLocation_ProviderDisabled() throws Exception { ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); mProvider.setProviderAllowed(false); mProvider.setProviderAllowed(true); @@ -633,8 +643,8 @@ public class LocationProviderManagerTest { mProvider.setProviderAllowed(false); ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); mProvider.setProviderAllowed(true); mProvider.setProviderLocation(createLocation(NAME, mRandom)); @@ -649,8 +659,8 @@ public class LocationProviderManagerTest { mProvider.setProviderLocation(loc); ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); verify(listener, times(1)).onLocation(locationCaptor.capture()); assertThat(locationCaptor.getValue()).isEqualTo(loc); @@ -659,8 +669,8 @@ public class LocationProviderManagerTest { @Test public void testGetCurrentLocation_Timeout() throws Exception { ILocationCallback listener = createMockGetCurrentLocationListener(); - LocationRequest locationRequest = new LocationRequest.Builder(0).build(); - mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); + mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener); mInjector.getAlarmHelper().incrementAlarmTime(60000); verify(listener, times(1)).onLocation(isNull()); @@ -674,7 +684,7 @@ public class LocationProviderManagerTest { IDENTITY.getPackageName())).isFalse(); ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(0).build(); + LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION, @@ -703,7 +713,8 @@ public class LocationProviderManagerTest { assertThat(mProvider.getRequest().getLocationRequests()).isEmpty(); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(5).build(); + LocationRequest request1 = new LocationRequest.Builder(5).setWorkSource( + WORK_SOURCE).build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().isActive()).isTrue(); @@ -714,7 +725,10 @@ public class LocationProviderManagerTest { assertThat(mProvider.getRequest().getWorkSource()).isNotNull(); ILocationListener listener2 = createMockLocationListener(); - LocationRequest request2 = new LocationRequest.Builder(1).setLowPower(true).build(); + LocationRequest request2 = new LocationRequest.Builder(1) + .setLowPower(true) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); assertThat(mProvider.getRequest().isActive()).isTrue(); @@ -745,7 +759,9 @@ public class LocationProviderManagerTest { mProvider.setProviderLocation(createLocation(NAME, mRandom)); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(60000).build(); + LocationRequest request1 = new LocationRequest.Builder(60000) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); verify(listener1).onLocationChanged(any(Location.class), nullable(IRemoteCallback.class)); @@ -762,7 +778,9 @@ public class LocationProviderManagerTest { mProvider.setProviderLocation(createLocation(NAME, mRandom)); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(60000).build(); + LocationRequest request1 = new LocationRequest.Builder(60000) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().isActive()).isFalse(); @@ -777,7 +795,9 @@ public class LocationProviderManagerTest { @Test public void testProviderRequest_BackgroundThrottle() { ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(5).build(); + LocationRequest request1 = new LocationRequest.Builder(5) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5); @@ -793,7 +813,9 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(5).build(); + LocationRequest request1 = new LocationRequest.Builder(5) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().isActive()).isTrue(); @@ -801,8 +823,10 @@ public class LocationProviderManagerTest { assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse(); ILocationListener listener2 = createMockLocationListener(); - LocationRequest request2 = new LocationRequest.Builder(1).setLocationSettingsIgnored( - true).build(); + LocationRequest request2 = new LocationRequest.Builder(1) + .setLocationSettingsIgnored(true) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); assertThat(mProvider.getRequest().isActive()).isTrue(); @@ -816,12 +840,16 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(1).build(); + LocationRequest request1 = new LocationRequest.Builder(1) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); ILocationListener listener2 = createMockLocationListener(); - LocationRequest request2 = new LocationRequest.Builder(5).setLocationSettingsIgnored( - true).build(); + LocationRequest request2 = new LocationRequest.Builder(5) + .setLocationSettingsIgnored(true) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId()); @@ -838,8 +866,10 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(1).setLocationSettingsIgnored( - true).build(); + LocationRequest request = new LocationRequest.Builder(1) + .setLocationSettingsIgnored(true) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(Collections.emptySet()); @@ -855,8 +885,10 @@ public class LocationProviderManagerTest { Collections.singleton(IDENTITY.getPackageName())); ILocationListener listener1 = createMockLocationListener(); - LocationRequest request1 = new LocationRequest.Builder(5).setLocationSettingsIgnored( - true).build(); + LocationRequest request1 = new LocationRequest.Builder(5) + .setLocationSettingsIgnored(true) + .setWorkSource(WORK_SOURCE) + .build(); mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5); @@ -871,7 +903,7 @@ public class LocationProviderManagerTest { LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF); ILocationListener listener = createMockLocationListener(); - LocationRequest request = new LocationRequest.Builder(5).build(); + LocationRequest request = new LocationRequest.Builder(5).setWorkSource(WORK_SOURCE).build(); mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener); assertThat(mProvider.getRequest().isActive()).isTrue(); diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java index 9ee9259a23c2..292b7c6bf5ad 100644 --- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java +++ b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java @@ -77,6 +77,7 @@ public class ControllerImplTest { private TestThreadingDomain mTestThreadingDomain; private TestCallback mTestCallback; private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider; + private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider; @Before public void setUp() { @@ -87,26 +88,30 @@ public class ControllerImplTest { mTestCallback = new TestCallback(mTestThreadingDomain); mTestPrimaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(mTestThreadingDomain, "primary"); + mTestSecondaryLocationTimeZoneProvider = + new TestLocationTimeZoneProvider(mTestThreadingDomain, "secondary"); } @Test public void initialState_enabled() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout() .plus(testEnvironment.getProviderInitializationTimeoutFuzz()); - // Initialize. After initialization the provider must be initialized and should be + // Initialize. After initialization the providers must be initialized and one should be // enabled. controllerImpl.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertInitialized(); + mTestSecondaryLocationTimeZoneProvider.assertInitialized(); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestPrimaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); } @@ -114,17 +119,19 @@ public class ControllerImplTest { @Test public void initialState_disabled() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED); - // Initialize. After initialization the provider must be initialized but should not be + // Initialize. After initialization the providers must be initialized but neither should be // enabled. controllerImpl.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertInitialized(); + mTestSecondaryLocationTimeZoneProvider.assertInitialized(); mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); } @@ -132,7 +139,7 @@ public class ControllerImplTest { @Test public void enabled_uncertaintySuggestionSentIfNoEventReceived() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -141,6 +148,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -148,9 +156,24 @@ public class ControllerImplTest { mTestThreadingDomain.executeNext(); // The primary should have reported uncertainty, which should trigger the controller to - // start the uncertainty timeout. + // start the uncertainty timeout and enable the secondary. mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + + // Simulate time passing with no provider event being received from either the primary or + // secondary. + mTestThreadingDomain.executeNext(); + + // Now both initialization timeouts should have triggered. The uncertainty timeout should + // still not be triggered. + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); @@ -160,6 +183,8 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertUncertainSuggestionMadeAndCommit(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); } @@ -167,7 +192,7 @@ public class ControllerImplTest { @Test public void enabled_eventReceivedBeforeInitializationTimeout() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -176,6 +201,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -186,6 +212,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertSuggestionMadeAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds()); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -194,7 +221,7 @@ public class ControllerImplTest { @Test public void enabled_eventReceivedFromPrimaryAfterInitializationTimeout() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -203,6 +230,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -211,16 +239,59 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); // Simulate a location event being received from the primary provider. This should cause a - // suggestion to be made. + // suggestion to be made and the secondary to be shut down. mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestCallback.assertSuggestionMadeAndCommit( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds()); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + } + + @Test + public void enabled_eventReceivedFromSecondaryAfterInitializationTimeout() { + ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + TestEnvironment testEnvironment = new TestEnvironment( + mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + + // Initialize and check initial state. + controllerImpl.initialize(testEnvironment, mTestCallback); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate time passing with no provider event being received from the primary. + mTestThreadingDomain.executeNext(); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + + // Simulate a location event being received from the secondary provider. This should cause a + // suggestion to be made. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertSuggestionMadeAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds()); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -229,7 +300,7 @@ public class ControllerImplTest { @Test public void enabled_repeatedPrimaryCertainty() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -238,6 +309,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -248,6 +320,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertSuggestionMadeAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds()); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -258,6 +331,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -267,6 +341,70 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestCallback.assertSuggestionMadeAndCommit( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds()); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + } + + @Test + public void enabled_repeatedSecondaryCertainty() { + ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + TestEnvironment testEnvironment = new TestEnvironment( + mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + + // Initialize and check initial state. + controllerImpl.initialize(testEnvironment, mTestCallback); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate time passing with no provider event being received from the primary. + mTestThreadingDomain.executeNext(); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + + // Simulate a location event being received from the secondary provider. This should cause a + // suggestion to be made. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertSuggestionMadeAndCommit( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds()); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // A second, identical event should not cause another suggestion. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // And a third, different event should cause another suggestion. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertSuggestionMadeAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds()); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -275,7 +413,7 @@ public class ControllerImplTest { @Test public void enabled_uncertaintyTriggersASuggestionAfterUncertaintyTimeout() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -284,6 +422,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -294,18 +433,48 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertSuggestionMadeAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds()); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); // Simulate an uncertain event being received from the primary provider. This should not // cause a suggestion to be made straight away, but the uncertainty timeout should be - // started. + // started and the secondary should be enabled. mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + + // Simulate a location event being received from the secondary provider. This should cause a + // suggestion to be made, cancel the uncertainty timeout and ensure the secondary is + // considered initialized. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertSuggestionMadeAndCommit( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds()); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate an uncertain event being received from the secondary provider. This should not + // cause a suggestion to be made straight away, but the uncertainty timeout should be + // started. Both providers are now enabled, with no initialization timeout set. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); @@ -315,6 +484,8 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertUncertainSuggestionMadeAndCommit(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); } @@ -322,7 +493,7 @@ public class ControllerImplTest { @Test public void enabled_briefUncertaintyTriggersNoSuggestion() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -331,6 +502,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -341,27 +513,32 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertSuggestionMadeAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds()); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); // Uncertainty should not cause a suggestion to be made straight away, but the uncertainty - // timeout should be started. + // timeout should be started and the secondary should be enabled. mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); mTestCallback.assertNoSuggestionMade(); assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); // And a success event from the primary provider should cause the controller to make another - // suggestion, the uncertainty timeout should be cancelled. + // suggestion, the uncertainty timeout should be cancelled and the secondary should be + // disabled again. mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertSuggestionMadeAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds()); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -370,7 +547,7 @@ public class ControllerImplTest { @Test public void configChanges_enableAndDisableWithNoPreviousSuggestion() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED); @@ -378,6 +555,7 @@ public class ControllerImplTest { controllerImpl.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -386,6 +564,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -393,6 +572,7 @@ public class ControllerImplTest { testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED); mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); } @@ -400,7 +580,7 @@ public class ControllerImplTest { @Test public void configChanges_enableAndDisableWithPreviousSuggestion() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED); @@ -408,6 +588,7 @@ public class ControllerImplTest { controllerImpl.initialize(testEnvironment, mTestCallback); mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -416,6 +597,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -425,6 +607,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertSuggestionMadeAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds()); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -436,6 +619,7 @@ public class ControllerImplTest { testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED); mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertUncertainSuggestionMadeAndCommit(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); } @@ -443,7 +627,7 @@ public class ControllerImplTest { @Test public void configChanges_userSwitch_enabledToEnabled() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -452,6 +636,7 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -463,6 +648,7 @@ public class ControllerImplTest { // and also clear the scheduled uncertainty suggestion. mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertSuggestionMadeAndCommit( USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds()); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -477,13 +663,74 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateChangesAndCommit(expectedStateTransitions); mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfig( PROVIDER_STATE_ENABLED_INITIALIZING, USER2_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); } @Test + public void primaryPermFailure_secondaryEventsReceived() { + ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + TestEnvironment testEnvironment = new TestEnvironment( + mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + + // Initialize and check initial state. + controllerImpl.initialize(testEnvironment, mTestCallback); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate a failure location event being received from the primary provider. This should + // cause the secondary to be enabled. + mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate uncertainty from the secondary. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + + // And a success event from the secondary provider should cause the controller to make + // another suggestion, the uncertainty timeout should be cancelled. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); + + mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertSuggestionMadeAndCommit( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds()); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate uncertainty from the secondary. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + } + + @Test public void primaryPermFailure_disableAndEnable() { ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, - mTestPrimaryLocationTimeZoneProvider); + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); TestEnvironment testEnvironment = new TestEnvironment( mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); @@ -492,22 +739,26 @@ public class ControllerImplTest { mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); // Simulate a failure location event being received from the primary provider. This should - // cause an uncertain suggestion to be made. + // cause the secondary to be enabled. mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT); mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); - mTestCallback.assertUncertainSuggestionMadeAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); // Now signal a config change so that geo detection is disabled. testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED); mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); mTestCallback.assertNoSuggestionMade(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); @@ -515,6 +766,164 @@ public class ControllerImplTest { testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED); mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + } + + @Test + public void secondaryPermFailure_primaryEventsReceived() { + ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + TestEnvironment testEnvironment = new TestEnvironment( + mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + + // Initialize and check initial state. + controllerImpl.initialize(testEnvironment, mTestCallback); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate an uncertain event from the primary. This will enable the secondary, which will + // give this test the opportunity to simulate its failure. Then it will be possible to + // demonstrate controller behavior with only the primary working. + mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + + // Simulate failure event from the secondary. This should just affect the secondary's state. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + + // And a success event from the primary provider should cause the controller to make + // a suggestion, the uncertainty timeout should be cancelled. + mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestCallback.assertSuggestionMadeAndCommit( + USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds()); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate uncertainty from the primary. The secondary cannot be enabled. + mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + } + + @Test + public void secondaryPermFailure_disableAndEnable() { + ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + TestEnvironment testEnvironment = new TestEnvironment( + mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + + // Initialize and check initial state. + controllerImpl.initialize(testEnvironment, mTestCallback); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate an uncertain event from the primary. This will enable the secondary, which will + // give this test the opportunity to simulate its failure. Then it will be possible to + // demonstrate controller behavior with only the primary working. + mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + + // Simulate failure event from the secondary. This should just affect the secondary's state. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertUncertaintyTimeoutSet(testEnvironment, controllerImpl); + + // Now signal a config change so that geo detection is disabled. + testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED); + + mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Now signal a config change so that geo detection is enabled. Only the primary can be + // enabled. + testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + } + + @Test + public void bothPermFailure_disableAndEnable() { + ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain, + mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider); + TestEnvironment testEnvironment = new TestEnvironment( + mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED); + + // Initialize and check initial state. + controllerImpl.initialize(testEnvironment, mTestCallback); + + mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit(); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate a failure event from the primary. This will enable the secondary. + mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit( + PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED); + mTestCallback.assertNoSuggestionMade(); + assertFalse(controllerImpl.isUncertaintyTimeoutSet()); + + // Simulate failure event from the secondary. + mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent( + USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT); + + mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); + mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit(); mTestCallback.assertUncertainSuggestionMadeAndCommit(); assertFalse(controllerImpl.isUncertaintyTimeoutSet()); } diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java new file mode 100644 index 000000000000..62e135b09593 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java @@ -0,0 +1,316 @@ +/* + * 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.pm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.pm.IDataLoaderStatusListener; +import android.content.pm.PackageManager; +import android.os.ConditionVariable; +import android.os.incremental.IStorageHealthListener; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Unit tests for {@link IncrementalStates}. + * Run with: atest -c FrameworksServicesTests:com.android.server.pm.IncrementalStatesTest + */ +@Presubmit +@RunWith(AndroidJUnit4.class) +@MediumTest +public class IncrementalStatesTest { + private IncrementalStates mIncrementalStates; + private ConditionVariable mUnstartableCalled = new ConditionVariable(); + private ConditionVariable mStartableCalled = new ConditionVariable(); + private ConditionVariable mFullyLoadedCalled = new ConditionVariable(); + private AtomicInteger mUnstartableReason = new AtomicInteger(0); + private static final int WAIT_TIMEOUT_MILLIS = 1000; /* 1 second */ + private IncrementalStates.Callback mCallback = new IncrementalStates.Callback() { + @Override + public void onPackageUnstartable(int reason) { + mUnstartableCalled.open(); + mUnstartableReason.set(reason); + } + + @Override + public void onPackageStartable() { + mStartableCalled.open(); + } + + @Override + public void onPackageFullyLoaded() { + mFullyLoadedCalled.open(); + } + }; + + /** + * Setup the tests as if the package has just been committed. + * By default the package is now startable and is loading. + */ + @Before + public void setUp() { + mIncrementalStates = new IncrementalStates(); + assertFalse(mIncrementalStates.isStartable()); + mIncrementalStates.setCallback(mCallback); + mIncrementalStates.onCommit(true); + // Test that package is now startable and loading + assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + assertTrue(mIncrementalStates.isLoading()); + mStartableCalled.close(); + mUnstartableCalled.close(); + mFullyLoadedCalled.close(); + } + + /** + * Test that startable state changes to false when Incremental Storage is unhealthy. + */ + @Test + public void testStartableTransition_IncrementalStorageUnhealthy() { + mIncrementalStates.onStorageHealthStatusChanged( + IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); + // Test that package is now unstartable + assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get()); + } + + /** + * Test that the package is still startable when Incremental Storage has pending reads. + */ + @Test + public void testStartableTransition_IncrementalStorageReadsPending() + throws InterruptedException { + mIncrementalStates.onStorageHealthStatusChanged( + IStorageHealthListener.HEALTH_STATUS_READS_PENDING); + // Test that package is still startable + assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + } + + /** + * Test that the package is still startable when Incremental Storage is at blocked status. + */ + @Test + public void testStartableTransition_IncrementalStorageBlocked() { + mIncrementalStates.onStorageHealthStatusChanged( + IStorageHealthListener.HEALTH_STATUS_BLOCKED); + // Test that package is still startable + assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + } + + /** + * Test that the package is still startable when Data Loader has unknown transportation issues. + */ + @Test + public void testStartableTransition_DataLoaderTransportError() { + mIncrementalStates.onStreamStatusChanged( + IDataLoaderStatusListener.STREAM_TRANSPORT_ERROR); + // Test that package is still startable + assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + } + + /** + * Test that the package becomes unstartable when Data Loader has data integrity issues. + */ + @Test + public void testStartableTransition_DataLoaderIntegrityError() { + mIncrementalStates.onStreamStatusChanged( + IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR); + // Test that package is now unstartable + assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_TRANSPORT, + mUnstartableReason.get()); + } + + /** + * Test that the package becomes unstartable when Data Loader has data source issues. + */ + @Test + public void testStartableTransition_DataLoaderSourceError() { + mIncrementalStates.onStreamStatusChanged( + IDataLoaderStatusListener.STREAM_SOURCE_ERROR); + // Test that package is now unstartable + assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_TRANSPORT, + mUnstartableReason.get()); + } + + /** + * Test that the package becomes unstartable when Data Loader hits limited storage while + * Incremental storage has a pending reads. + */ + @Test + public void testStartableTransition_DataLoaderStorageErrorWhenIncrementalStoragePending() + throws InterruptedException { + mIncrementalStates.onStreamStatusChanged( + IDataLoaderStatusListener.STREAM_STORAGE_ERROR); + // Test that package is still startable + assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + mIncrementalStates.onStorageHealthStatusChanged( + IStorageHealthListener.HEALTH_STATUS_READS_PENDING); + // Test that package is now unstartable + assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE, + mUnstartableReason.get()); + } + + /** + * Test that the package becomes unstartable when Data Loader hits limited storage while + * Incremental storage is at blocked status. + */ + @Test + public void testStartableTransition_DataLoaderStorageErrorWhenIncrementalStorageBlocked() + throws InterruptedException { + mIncrementalStates.onStreamStatusChanged( + IDataLoaderStatusListener.STREAM_STORAGE_ERROR); + // Test that package is still startable + assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + mIncrementalStates.onStorageHealthStatusChanged( + IStorageHealthListener.HEALTH_STATUS_BLOCKED); + // Test that package is now unstartable + assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE, + mUnstartableReason.get()); + } + + /** + * Test that the package becomes unstartable when Incremental Storage is unhealthy, and it + * becomes startable again when Incremental Storage is healthy again. + */ + @Test + public void testStartableTransition_IncrementalStorageUnhealthyBackToHealthy() + throws InterruptedException { + mIncrementalStates.onStorageHealthStatusChanged( + IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); + // Test that package is unstartable + assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + + mIncrementalStates.onStorageHealthStatusChanged( + IStorageHealthListener.HEALTH_STATUS_OK); + // Test that package is now startable + assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + } + + /** + * Test that the package becomes unstartable when Data Loader has data integrity issue, and it + * becomes startable again when Data Loader is healthy again. + */ + @Test + public void testStartableTransition_DataLoaderUnhealthyBackToHealthy() + throws InterruptedException { + mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR); + // Test that package is unstartable + assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + + mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_HEALTHY); + // Test that package is now startable + assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + } + + /** + * Test that the package becomes unstartable when both Incremental Storage and Data Loader + * are unhealthy, and it becomes startable again when both Incremental Storage and Data Loader + * are healthy again. + */ + @Test + public void testStartableTransition_DataLoaderAndIncrementalStorageUnhealthyBackToHealthy() + throws InterruptedException { + mIncrementalStates.onStorageHealthStatusChanged( + IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); + mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR); + // Test that package is unstartable + assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + + mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_HEALTHY); + // Test that package is still unstartable + assertFalse(mStartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + mIncrementalStates.onStorageHealthStatusChanged(IStorageHealthListener.HEALTH_STATUS_OK); + // Test that package is now startable + assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + } + + /** + * Test that when loading progress is 1, the package becomes fully loaded, and the change of + * Incremental Storage health status does not affect the startable state. + */ + @Test + public void testStartableTransition_HealthStatusChangeWhenFullyLoaded() + throws InterruptedException { + mIncrementalStates.setProgress(1.0f); + // Test that package is now fully loaded + assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isLoading()); + mIncrementalStates.onStorageHealthStatusChanged( + IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); + // Test that package is still startable + assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + } + + /** + * Test that when loading progress is 1, the package becomes fully loaded, and if the package + * was unstartable, it becomes startable. + */ + @Test + public void testLoadingTransition_FullyLoadedWhenUnstartable() throws InterruptedException { + mIncrementalStates.onStorageHealthStatusChanged( + IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); + // Test that package is unstartable + assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + // Test that package is still loading + assertTrue(mIncrementalStates.isLoading()); + + mIncrementalStates.setProgress(0.5f); + // Test that package is still unstartable + assertFalse(mStartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isStartable()); + mIncrementalStates.setProgress(1.0f); + // Test that package is now startable + assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS)); + assertTrue(mIncrementalStates.isStartable()); + // Test that package is now fully loaded + assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS)); + assertFalse(mIncrementalStates.isLoading()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index bc747832e253..6255630712ae 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -891,8 +891,10 @@ public class PackageManagerSettingsTests { assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups); } assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups)); - assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState); - assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState)); + assertNotSame(origPkgSetting.mLegacyPermissionsState, + testPkgSetting.mLegacyPermissionsState); + assertThat(origPkgSetting.mLegacyPermissionsState, + is(testPkgSetting.mLegacyPermissionsState)); assertThat(origPkgSetting.name, is(testPkgSetting.name)); // mOldCodePaths is _not_ copied // assertNotSame(origPkgSetting.mOldCodePaths, testPkgSetting.mOldCodePaths); diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index e3c795d03381..9099272a0fd6 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -39,6 +39,8 @@ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.STATUS_BAR" /> <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" /> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" /> <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) --> <application android:debuggable="true" diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java index cf07183a007d..f026b852f08c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java @@ -300,15 +300,15 @@ public class ActivityDisplayTests extends WindowTestsBase { } @Test - public void testRemoveStackInWindowingModes() { - removeStackTests(() -> mRootWindowContainer.removeStacksInWindowingModes( + public void testRemoveRootTaskInWindowingModes() { + removeStackTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes( WINDOWING_MODE_FULLSCREEN)); } @Test public void testRemoveStackWithActivityTypes() { - removeStackTests( - () -> mRootWindowContainer.removeStacksWithActivityTypes(ACTIVITY_TYPE_STANDARD)); + removeStackTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes( + ACTIVITY_TYPE_STANDARD)); } private void removeStackTests(Runnable runnable) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 031165727207..0f805610af93 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -160,7 +160,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { }; mAtm.mWindowManager.registerDisplayWindowListener(listener); // Check that existing displays call added - assertEquals(1, added.size()); + assertEquals(mRootWindowContainer.getChildCount(), added.size()); assertEquals(0, changed.size()); assertEquals(0, removed.size()); added.clear(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java new file mode 100644 index 000000000000..9a668b91c656 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java @@ -0,0 +1,118 @@ +/* + * 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.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; + +import static com.android.server.wm.WindowContainer.POSITION_TOP; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for the {@link DisplayAreaGroup} container. + * + * Build/Install/Run: + * atest WmTests:DisplayAreaGroupTest + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class DisplayAreaGroupTest extends WindowTestsBase { + + private DisplayAreaGroup mDisplayAreaGroup; + private TaskDisplayArea mTaskDisplayArea; + private Task mStack; + private ActivityRecord mActivity; + + @Before + public void setUp() { + mDisplayAreaGroup = new DisplayAreaGroup( + mWm, "DisplayAreaGroup", FEATURE_VENDOR_FIRST); + final TaskDisplayArea defaultTda = mDisplayContent.getDefaultTaskDisplayArea(); + final WindowContainer parentDA = defaultTda.getParent(); + parentDA.addChild(mDisplayAreaGroup, parentDA.mChildren.indexOf(defaultTda) + 1); + mTaskDisplayArea = new TaskDisplayArea( + mDisplayContent, mWm, "TDA1", FEATURE_VENDOR_FIRST + 1); + mDisplayAreaGroup.addChild(mTaskDisplayArea, POSITION_TOP); + mStack = mTaskDisplayArea.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + mActivity = new ActivityBuilder(mAtm).setCreateTask(true).setStack(mStack).build(); + mDisplayContent.setLastFocusedTaskDisplayArea(mTaskDisplayArea); + } + + @Test + public void testIsOrientationDifferentFromDisplay() { + // Display is portrait, DisplayAreaGroup inherits that + mDisplayContent.setBounds(0, 0, 600, 900); + + assertThat(mDisplayAreaGroup.isOrientationDifferentFromDisplay()).isFalse(); + + // DisplayAreaGroup is landscape, different Display + mDisplayAreaGroup.setBounds(0, 0, 600, 450); + + assertThat(mDisplayAreaGroup.isOrientationDifferentFromDisplay()).isTrue(); + + // DisplayAreaGroup is portrait, same as Display + mDisplayAreaGroup.setBounds(0, 0, 300, 900); + + assertThat(mDisplayAreaGroup.isOrientationDifferentFromDisplay()).isFalse(); + } + + @Test + public void testGetOrientation() { + doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any(), any()); + mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + + // Display is portrait, DisplayAreaGroup inherits that + mDisplayContent.setBounds(0, 0, 600, 900); + + assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); + assertThat(mActivity.getRequestedConfigurationOrientation()) + .isEqualTo(ORIENTATION_PORTRAIT); + + // DisplayAreaGroup is landscape, different from Display + mDisplayAreaGroup.setBounds(0, 0, 600, 450); + + assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); + assertThat(mActivity.getRequestedConfigurationOrientation()) + .isEqualTo(ORIENTATION_LANDSCAPE); + + // DisplayAreaGroup is portrait, same as Display + mDisplayAreaGroup.setBounds(0, 0, 300, 900); + + assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); + assertThat(mActivity.getRequestedConfigurationOrientation()) + .isEqualTo(ORIENTATION_PORTRAIT); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java index 820eca4a49a8..bc7516f6514f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.Display.INVALID_DISPLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; @@ -319,6 +320,29 @@ public class LaunchParamsControllerTests extends WindowTestsBase { } /** + * Ensures that {@link LaunchParamsModifier} doesn't alter non-root tasks' windowingMode. + */ + @Test + public void testLayoutNonRootTaskWindowingModeChange() { + final LaunchParams params = new LaunchParams(); + final int windowingMode = WINDOWING_MODE_FREEFORM; + params.mWindowingMode = windowingMode; + final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params); + final Task task = new TaskBuilder(mAtm.mStackSupervisor).setCreateParentTask(true).build(); + task.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + + mController.registerModifier(positioner); + + final int beforeWindowMode = task.getWindowingMode(); + assertNotEquals(windowingMode, beforeWindowMode); + + mController.layoutTask(task, null /* windowLayout */); + + final int afterWindowMode = task.getWindowingMode(); + assertEquals(afterWindowMode, beforeWindowMode); + } + + /** * Ensures that {@link LaunchParamsModifier} requests specifying bounds during * layout are honored if window is in freeform. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index f4f172d8e5b5..1bf83aced590 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -1154,22 +1154,22 @@ public class RecentTasksTest extends WindowTestsBase { } private void doTestRecentTasksApis(boolean expectCallable) { - assertSecurityException(expectCallable, () -> mAtm.removeStack(INVALID_STACK_ID)); + assertSecurityException(expectCallable, () -> mAtm.removeTask(INVALID_STACK_ID)); assertSecurityException(expectCallable, - () -> mAtm.removeStacksInWindowingModes( + () -> mAtm.removeRootTasksInWindowingModes( new int[]{WINDOWING_MODE_UNDEFINED})); assertSecurityException(expectCallable, - () -> mAtm.removeStacksWithActivityTypes( + () -> mAtm.removeRootTasksWithActivityTypes( new int[]{ACTIVITY_TYPE_UNDEFINED})); assertSecurityException(expectCallable, () -> mAtm.removeTask(0)); assertSecurityException(expectCallable, () -> mAtm.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true)); assertSecurityException(expectCallable, - () -> mAtm.moveTaskToStack(0, INVALID_STACK_ID, true)); + () -> mAtm.moveTaskToRootTask(0, INVALID_STACK_ID, true)); assertSecurityException(expectCallable, () -> mAtm.setTaskWindowingModeSplitScreenPrimary(0, true)); assertSecurityException(expectCallable, - () -> mAtm.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect())); + () -> mAtm.moveTopActivityToPinnedRootTask(INVALID_STACK_ID, new Rect())); assertSecurityException(expectCallable, () -> mAtm.getAllRootTaskInfos()); assertSecurityException(expectCallable, () -> mAtm.getRootTaskInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED)); @@ -1188,7 +1188,7 @@ public class RecentTasksTest extends WindowTestsBase { () -> mAtm.unregisterTaskStackListener(null)); assertSecurityException(expectCallable, () -> mAtm.getTaskDescription(0)); assertSecurityException(expectCallable, () -> mAtm.cancelTaskWindowTransition(0)); - assertSecurityException(expectCallable, () -> mAtm.startRecentsActivity(null, null, + assertSecurityException(expectCallable, () -> mAtm.startRecentsActivity(null, 0, null)); assertSecurityException(expectCallable, () -> mAtm.cancelRecentsAnimation(true)); assertSecurityException(expectCallable, () -> mAtm.stopAppSwitches()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index d821d38ea297..c10d4fa7f189 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -147,7 +147,7 @@ public class RecentsAnimationTest extends WindowTestsBase { Intent recentsIntent = new Intent().setComponent(mRecentsComponent); // Null animation indicates to preload. - mAtm.startRecentsActivity(recentsIntent, null /* assistDataReceiver */, + mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */, null /* recentsAnimationRunner */); Task recentsStack = defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, @@ -167,7 +167,7 @@ public class RecentsAnimationTest extends WindowTestsBase { spyOn(recentsActivity); // Start when the recents activity exists. It should ensure the configuration. - mAtm.startRecentsActivity(recentsIntent, null /* assistDataReceiver */, + mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */, null /* recentsAnimationRunner */); verify(recentsActivity).ensureActivityConfiguration(anyInt() /* globalChanges */, @@ -381,7 +381,7 @@ public class RecentsAnimationTest extends WindowTestsBase { Intent recentsIntent = new Intent(); recentsIntent.setComponent(recentsComponent); - mAtm.startRecentsActivity(recentsIntent, null /* assistDataReceiver */, + mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */, mock(IRecentsAnimationRunner.class)); return recentsAnimation[0]; } diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java index d7eedd990f04..d0a5644f5025 100644 --- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -147,13 +147,6 @@ public class StubTransaction extends SurfaceControl.Transaction { } @Override - public SurfaceControl.Transaction deferTransactionUntilSurface(SurfaceControl sc, - Surface barrierSurface, - long frameNumber) { - return this; - } - - @Override public SurfaceControl.Transaction reparentChildren(SurfaceControl sc, SurfaceControl newParent) { return this; diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index f151d9ca2420..8e56e5bb2d85 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -37,7 +37,6 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.IUidObserver; import android.app.PendingIntent; -import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.AppStandbyInfo; import android.app.usage.ConfigurationStats; @@ -1431,10 +1430,11 @@ public class UsageStatsService extends SystemService implements private boolean hasObserverPermission() { final int callingUid = Binder.getCallingUid(); DevicePolicyManagerInternal dpmInternal = getDpmInternal(); + //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode. if (callingUid == Process.SYSTEM_UID || (dpmInternal != null - && dpmInternal.isActiveAdminWithPolicy(callingUid, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))) { + && (dpmInternal.isActiveProfileOwner(callingUid) + || dpmInternal.isActiveDeviceOwner(callingUid)))) { // Caller is the system or the profile owner, so proceed. return true; } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 7b84385aead6..12e56cc26546 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -85,8 +85,6 @@ import android.telephony.emergency.EmergencyNumber; import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; import android.telephony.ims.ImsMmTelManager; import android.telephony.ims.aidl.IImsConfig; -import android.telephony.ims.aidl.IImsMmTelFeature; -import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; @@ -94,7 +92,6 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; -import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CellNetworkScanResult; @@ -7384,80 +7381,6 @@ public class TelephonyManager { } /** - * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id and MMTel - * feature or {@link null} if the service is not available. If an MMTelFeature is available, the - * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates. - * @param slotIndex The SIM slot that we are requesting the {@link IImsMmTelFeature} for. - * @param callback Listener that will send updates to ImsManager when there are updates to - * ImsServiceController. - * @return {@link IImsMmTelFeature} interface for the feature specified or {@code null} if - * it is unavailable. - * @hide - */ - public @Nullable IImsMmTelFeature getImsMmTelFeatureAndListen(int slotIndex, - IImsServiceFeatureCallback callback) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - return telephony.getMmTelFeatureAndListen(slotIndex, callback); - } - } catch (RemoteException e) { - Rlog.e(TAG, "getImsMmTelFeatureAndListen, RemoteException: " - + e.getMessage()); - } - return null; - } - - /** - * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id and RCS - * feature for emergency calling or {@link null} if the service is not available. If an - * RcsFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a - * listener for feature updates. - * @param slotIndex The SIM slot that we are requesting the {@link IImsRcsFeature} for. - * @param callback Listener that will send updates to ImsManager when there are updates to - * ImsServiceController. - * @return {@link IImsRcsFeature} interface for the feature specified or {@code null} if - * it is unavailable. - * @hide - */ - public @Nullable IImsRcsFeature getImsRcsFeatureAndListen(int slotIndex, - IImsServiceFeatureCallback callback) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - return telephony.getRcsFeatureAndListen(slotIndex, callback); - } - } catch (RemoteException e) { - Rlog.e(TAG, "getImsRcsFeatureAndListen, RemoteException: " - + e.getMessage()); - } - return null; - } - - /** - * Unregister a IImsServiceFeatureCallback previously associated with an ImsFeature through - * {@link #getImsMmTelFeatureAndListen(int, IImsServiceFeatureCallback)} or - * {@link #getImsRcsFeatureAndListen(int, IImsServiceFeatureCallback)}. - * @param slotIndex The SIM slot associated with the callback. - * @param featureType The {@link android.telephony.ims.feature.ImsFeature.FeatureType} - * associated with the callback. - * @param callback The callback to be unregistered. - * @hide - */ - public void unregisterImsFeatureCallback(int slotIndex, int featureType, - IImsServiceFeatureCallback callback) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - telephony.unregisterImsFeatureCallback(slotIndex, featureType, callback); - } - } catch (RemoteException e) { - Rlog.e(TAG, "unregisterImsFeatureCallback, RemoteException: " - + e.getMessage()); - } - } - - /** * @return the {@IImsRegistration} interface that corresponds with the slot index and feature. * @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for. * @param feature An integer indicating the feature that we wish to get the ImsRegistration for. diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index f6c14e67306b..ee2fce7e7dd5 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -59,6 +59,7 @@ import java.util.function.Consumer; * manager. */ public class ImsMmTelManager implements RegistrationManager { + private static final String TAG = "ImsMmTelManager"; /** * @hide @@ -809,7 +810,7 @@ public class ImsMmTelManager implements RegistrationManager { } try { - getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() { + iTelephony.isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() { @Override public void accept(int result) { executor.execute(() -> callback.accept(result == 1)); diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index da7311c08307..8a05bdfc8401 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -16,6 +16,7 @@ package android.telephony.ims; +import android.annotation.LongDef; import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.Service; @@ -41,6 +42,11 @@ import android.util.SparseArray; import com.android.ims.internal.IImsFeatureStatusCallback; import com.android.internal.annotations.VisibleForTesting; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; +import java.util.Map; + /** * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend * ImsService must register the service in their AndroidManifest to be detected by the framework. @@ -98,6 +104,32 @@ public class ImsService extends Service { private static final String LOG_TAG = "ImsService"; /** + * This ImsService supports the capability to place emergency calls over MMTEL. + * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be + * adding other capabilities in a central location, so track this capability here as well. + */ + public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0; + + /** + * @hide + */ + @LongDef(flag = true, + prefix = "CAPABILITY_", + value = { + CAPABILITY_EMERGENCY_OVER_MMTEL + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsServiceCapability {} + + /** + * Used for logging purposes, see {@link #getCapabilitiesString(long)} + * @hide + */ + private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{ + put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL"); + }}; + + /** * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. * @hide */ @@ -409,4 +441,30 @@ public class ImsService extends Service { public ImsRegistrationImplBase getRegistration(int slotId) { return new ImsRegistrationImplBase(); } + + /** + * @return A string representation of the ImsService capabilties for logging. + * @hide + */ + public static String getCapabilitiesString(@ImsServiceCapability long caps) { + StringBuffer result = new StringBuffer(); + result.append("capabilities={ "); + // filter incrementally fills 0s from left to right. This is used to keep filtering out + // more bits in the long until the remaining leftmost bits are all zero. + long filter = 0xFFFFFFFFFFFFFFFFL; + // position of iterator to potentially print capability. + long i = 0; + while ((caps & filter) != 0 && i <= 63) { + long bitToCheck = (1L << i); + if ((caps & bitToCheck) != 0) { + result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?")); + result.append(" "); + } + // shift left by one and fill in another 1 on the leftmost bit. + filter <<= 1; + i++; + } + result.append("}"); + return result.toString(); + } }
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index 9e461420e126..e01ea9179452 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -22,6 +22,8 @@ import android.telephony.ims.aidl.IRcsUceControllerCallback; import android.telephony.ims.aidl.IRcsUcePublishStateCallback; import android.telephony.ims.aidl.IImsRegistrationCallback; +import com.android.ims.ImsFeatureContainer; +import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.telephony.IIntegerConsumer; /** @@ -50,4 +52,8 @@ interface IImsRcsController { void setUceSettingEnabled(int subId, boolean isEnabled); void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c); void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c); + + // Internal commands that should not be made public + void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback); + void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback); } diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.aidl b/telephony/java/com/android/ims/ImsFeatureContainer.aidl new file mode 100644 index 000000000000..9706f20c59ca --- /dev/null +++ b/telephony/java/com/android/ims/ImsFeatureContainer.aidl @@ -0,0 +1,19 @@ +/* + * 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.ims; + +parcelable ImsFeatureContainer;
\ No newline at end of file diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.java b/telephony/java/com/android/ims/ImsFeatureContainer.java new file mode 100644 index 000000000000..b259679ea1bf --- /dev/null +++ b/telephony/java/com/android/ims/ImsFeatureContainer.java @@ -0,0 +1,172 @@ +/* + * 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.ims; + +import android.annotation.NonNull; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.ImsService; +import android.telephony.ims.aidl.IImsConfig; +import android.telephony.ims.aidl.IImsRegistration; +import android.telephony.ims.feature.ImsFeature; + +import java.util.Objects; + +/** + * Contains an IBinder linking to the appropriate ImsFeature as well as the associated + * interfaces. + * @hide + */ +public final class ImsFeatureContainer implements Parcelable { + /** + * ImsFeature that is being tracked. + */ + public final IBinder imsFeature; + + /** + * IImsConfig interface that should be associated with the ImsFeature. + */ + public final android.telephony.ims.aidl.IImsConfig imsConfig; + + /** + * IImsRegistration interface that should be associated with this ImsFeature. + */ + public final IImsRegistration imsRegistration; + + /** + * State of the feature that is being tracked. + */ + private @ImsFeature.ImsState int mState = ImsFeature.STATE_UNAVAILABLE; + + /** + * Capabilities of this ImsService. + */ + private @ImsService.ImsServiceCapability long mCapabilities; + /** + * Contains the ImsFeature IBinder as well as the ImsService interfaces associated with + * that feature. + * @param iFace IBinder connection to the ImsFeature. + * @param iConfig IImsConfig interface associated with the ImsFeature. + * @param iReg IImsRegistration interface associated with the ImsFeature + * @param initialCaps The initial capabilities that the ImsService supports. + */ + public ImsFeatureContainer(@NonNull IBinder iFace, @NonNull IImsConfig iConfig, + @NonNull IImsRegistration iReg, long initialCaps) { + imsFeature = iFace; + imsConfig = iConfig; + imsRegistration = iReg; + mCapabilities = initialCaps; + } + + /** + * Create an ImsFeatureContainer from a Parcel. + */ + private ImsFeatureContainer(Parcel in) { + imsFeature = in.readStrongBinder(); + imsConfig = IImsConfig.Stub.asInterface(in.readStrongBinder()); + imsRegistration = IImsRegistration.Stub.asInterface(in.readStrongBinder()); + mState = in.readInt(); + mCapabilities = in.readLong(); + } + + /** + * @return the capabilties that are associated with the ImsService that this ImsFeature + * belongs to. + */ + public @ImsService.ImsServiceCapability long getCapabilities() { + return mCapabilities; + } + + /** + * Update the capabilities that are associated with the ImsService that this ImsFeature + * belongs to. + */ + public void setCapabilities(@ImsService.ImsServiceCapability long caps) { + mCapabilities = caps; + } + + /** + * @return The state of the ImsFeature. + */ + public @ImsFeature.ImsState int getState() { + return mState; + } + + /** + * Set the state that is associated with the ImsService that this ImsFeature + * belongs to. + */ + public void setState(@ImsFeature.ImsState int state) { + mState = state; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ImsFeatureContainer that = (ImsFeatureContainer) o; + return imsFeature.equals(that.imsFeature) && + imsConfig.equals(that.imsConfig) && + imsRegistration.equals(that.imsRegistration) && + mState == that.getState() && + mCapabilities == that.getCapabilities(); + } + + @Override + public int hashCode() { + return Objects.hash(imsFeature, imsConfig, imsRegistration, mState, mCapabilities); + } + + @Override + public String toString() { + return "FeatureContainer{" + + "imsFeature=" + imsFeature + + ", imsConfig=" + imsConfig + + ", imsRegistration=" + imsRegistration + + ", state=" + ImsFeature.STATE_LOG_MAP.get(mState) + + ", capabilities = " + ImsService.getCapabilitiesString(mCapabilities) + + '}'; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(imsFeature); + dest.writeStrongInterface(imsConfig); + dest.writeStrongInterface(imsRegistration); + dest.writeInt(mState); + dest.writeLong(mCapabilities); + } + + + public static final Creator<ImsFeatureContainer> CREATOR = new Creator<ImsFeatureContainer>() { + @Override + public ImsFeatureContainer createFromParcel(Parcel source) { + return new ImsFeatureContainer(source); + } + + @Override + public ImsFeatureContainer[] newArray(int size) { + return new ImsFeatureContainer[size]; + } + }; +} diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl index 9a9cf5325310..f5f67bd36ec3 100644 --- a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl +++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl @@ -16,13 +16,18 @@ package com.android.ims.internal; +import com.android.ims.ImsFeatureContainer; /** - * Interface from ImsResolver to ImsServiceProxy in ImsManager. - * Callback to ImsManager when a feature changes in the ImsServiceController. + * Interface from ImsResolver to FeatureConnections. + * Callback to FeatureConnections when a feature's status changes. * {@hide} */ oneway interface IImsServiceFeatureCallback { - void imsFeatureCreated(int slotId, int feature); - void imsFeatureRemoved(int slotId, int feature); - void imsStatusChanged(int slotId, int feature, int status); + void imsFeatureCreated(in ImsFeatureContainer feature); + // Reason defined in FeatureConnector.UnavailableReason + void imsFeatureRemoved(int reason); + // Status defined in ImsFeature.ImsState. + void imsStatusChanged(int status); + //Capabilities defined in ImsService.ImsServiceCapability + void updateCapabilities(long capabilities); }
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index d67384c6f582..53069a1691a0 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -829,22 +829,14 @@ interface ITelephony { * as well as registering the MmTelFeature for callbacks using the IImsServiceFeatureCallback * interface. */ - IImsMmTelFeature getMmTelFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback); - - /** - * Get IImsRcsFeature binder from ImsResolver that corresponds to the subId and RCS feature - * as well as registering the RcsFeature for callbacks using the IImsServiceFeatureCallback - * interface. - */ - IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback); + void registerMmTelFeatureCallback(int slotId, in IImsServiceFeatureCallback callback); /** * Unregister a callback that was previously registered through - * {@link #getMmTelFeatureAndListen} or {@link #getRcsFeatureAndListen}. This should always be - * called when the callback is no longer being used. + * {@link #registerMmTelFeatureCallback}. This should always be called when the callback is no + * longer being used. */ - void unregisterImsFeatureCallback(int slotId, int featureType, - in IImsServiceFeatureCallback callback); + void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback); /** * Returns the IImsRegistration associated with the slot and feature specified. diff --git a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java index 858358c74f80..8b730af76951 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.Manifest.permission; import android.app.AppOpsManager; -import android.app.admin.DeviceAdminInfo; import android.app.admin.DevicePolicyManagerInternal; import android.content.Context; import android.content.pm.PackageManager; @@ -167,13 +166,11 @@ public class NetworkStatsAccessTest { } private void setIsDeviceOwner(boolean isOwner) { - when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER)) - .thenReturn(isOwner); + when(mDpmi.isActiveDeviceOwner(TEST_UID)).thenReturn(isOwner); } private void setIsProfileOwner(boolean isOwner) { - when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) - .thenReturn(isOwner); + when(mDpmi.isActiveProfileOwner(TEST_UID)).thenReturn(isOwner); } private void setHasAppOpsPermission(int appOpsMode, boolean hasPermission) { diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp index 74978696fdd5..8862405189c0 100644 --- a/tools/aapt2/dump/DumpManifest.cpp +++ b/tools/aapt2/dump/DumpManifest.cpp @@ -1063,17 +1063,23 @@ class UsesPermission : public ManifestExtractor::Element { public: UsesPermission() = default; std::string name; - std::string requiredFeature; - std::string requiredNotFeature; + std::vector<std::string> requiredFeatures; + std::vector<std::string> requiredNotFeatures; int32_t required = true; int32_t maxSdkVersion = -1; void Extract(xml::Element* element) override { name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), ""); - requiredFeature = GetAttributeStringDefault( - FindAttribute(element, REQUIRED_FEATURE_ATTR), ""); - requiredNotFeature = GetAttributeStringDefault( - FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), ""); + std::string feature = + GetAttributeStringDefault(FindAttribute(element, REQUIRED_FEATURE_ATTR), ""); + if (!feature.empty()) { + requiredFeatures.push_back(feature); + } + feature = GetAttributeStringDefault(FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), ""); + if (!feature.empty()) { + requiredNotFeatures.push_back(feature); + } + required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1); maxSdkVersion = GetAttributeIntegerDefault( FindAttribute(element, MAX_SDK_VERSION_ATTR), -1); @@ -1090,13 +1096,13 @@ class UsesPermission : public ManifestExtractor::Element { if (maxSdkVersion >= 0) { printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion)); } - if (!requiredFeature.empty()) { - printer->Print(StringPrintf(" requiredFeature='%s'", requiredFeature.data())); + printer->Print("\n"); + for (const std::string& requiredFeature : requiredFeatures) { + printer->Print(StringPrintf(" required-feature='%s'\n", requiredFeature.data())); } - if (!requiredNotFeature.empty()) { - printer->Print(StringPrintf(" requiredNotFeature='%s'", requiredNotFeature.data())); + for (const std::string& requiredNotFeature : requiredNotFeatures) { + printer->Print(StringPrintf(" required-not-feature='%s'\n", requiredNotFeature.data())); } - printer->Print("\n"); if (required == 0) { printer->Print(StringPrintf("optional-permission: name='%s'", name.data())); if (maxSdkVersion >= 0) { @@ -1116,6 +1122,38 @@ class UsesPermission : public ManifestExtractor::Element { } }; +/** Represents <required-feature> elements. **/ +class RequiredFeature : public ManifestExtractor::Element { + public: + RequiredFeature() = default; + std::string name; + + void Extract(xml::Element* element) override { + name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), ""); + auto parent_stack = extractor()->parent_stack(); + if (!name.empty() && ElementCast<UsesPermission>(parent_stack[0])) { + UsesPermission* uses_permission = ElementCast<UsesPermission>(parent_stack[0]); + uses_permission->requiredFeatures.push_back(name); + } + } +}; + +/** Represents <required-not-feature> elements. **/ +class RequiredNotFeature : public ManifestExtractor::Element { + public: + RequiredNotFeature() = default; + std::string name; + + void Extract(xml::Element* element) override { + name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), ""); + auto parent_stack = extractor()->parent_stack(); + if (!name.empty() && ElementCast<UsesPermission>(parent_stack[0])) { + UsesPermission* uses_permission = ElementCast<UsesPermission>(parent_stack[0]); + uses_permission->requiredNotFeatures.push_back(name); + } + } +}; + /** Represents <uses-permission-sdk-23> elements. **/ class UsesPermissionSdk23 : public ManifestExtractor::Element { public: @@ -1845,7 +1883,8 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) { for (xml::Element* child : element->GetChildElements()) { if (child->name == "uses-permission" || child->name == "uses-permission-sdk-23" || child->name == "permission") { - auto permission_element = ManifestExtractor::Element::Inflate(this, child); + // Inflate the element and its descendants + auto permission_element = Visit(child); manifest->AddChild(permission_element); } } @@ -2237,38 +2276,40 @@ T* ElementCast(ManifestExtractor::Element* element) { } const std::unordered_map<std::string, bool> kTagCheck = { - {"action", std::is_base_of<Action, T>::value}, - {"activity", std::is_base_of<Activity, T>::value}, - {"application", std::is_base_of<Application, T>::value}, - {"category", std::is_base_of<Category, T>::value}, - {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value}, - {"feature-group", std::is_base_of<FeatureGroup, T>::value}, - {"input-type", std::is_base_of<InputType, T>::value}, - {"intent-filter", std::is_base_of<IntentFilter, T>::value}, - {"meta-data", std::is_base_of<MetaData, T>::value}, - {"manifest", std::is_base_of<Manifest, T>::value}, - {"original-package", std::is_base_of<OriginalPackage, T>::value}, - {"overlay", std::is_base_of<Overlay, T>::value}, - {"package-verifier", std::is_base_of<PackageVerifier, T>::value}, - {"permission", std::is_base_of<Permission, T>::value}, - {"provider", std::is_base_of<Provider, T>::value}, - {"receiver", std::is_base_of<Receiver, T>::value}, - {"screen", std::is_base_of<Screen, T>::value}, - {"service", std::is_base_of<Service, T>::value}, - {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value}, - {"supports-input", std::is_base_of<SupportsInput, T>::value}, - {"supports-screens", std::is_base_of<SupportsScreen, T>::value}, - {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value}, - {"uses-feature", std::is_base_of<UsesFeature, T>::value}, - {"uses-permission", std::is_base_of<UsesPermission, T>::value}, - {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value}, - {"uses-library", std::is_base_of<UsesLibrary, T>::value}, - {"uses-package", std::is_base_of<UsesPackage, T>::value}, - {"static-library", std::is_base_of<StaticLibrary, T>::value}, - {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value}, - {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value}, - {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value}, - {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value}, + {"action", std::is_base_of<Action, T>::value}, + {"activity", std::is_base_of<Activity, T>::value}, + {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value}, + {"application", std::is_base_of<Application, T>::value}, + {"category", std::is_base_of<Category, T>::value}, + {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value}, + {"feature-group", std::is_base_of<FeatureGroup, T>::value}, + {"input-type", std::is_base_of<InputType, T>::value}, + {"intent-filter", std::is_base_of<IntentFilter, T>::value}, + {"meta-data", std::is_base_of<MetaData, T>::value}, + {"manifest", std::is_base_of<Manifest, T>::value}, + {"original-package", std::is_base_of<OriginalPackage, T>::value}, + {"overlay", std::is_base_of<Overlay, T>::value}, + {"package-verifier", std::is_base_of<PackageVerifier, T>::value}, + {"permission", std::is_base_of<Permission, T>::value}, + {"provider", std::is_base_of<Provider, T>::value}, + {"receiver", std::is_base_of<Receiver, T>::value}, + {"required-feature", std::is_base_of<RequiredFeature, T>::value}, + {"required-not-feature", std::is_base_of<RequiredNotFeature, T>::value}, + {"screen", std::is_base_of<Screen, T>::value}, + {"service", std::is_base_of<Service, T>::value}, + {"static-library", std::is_base_of<StaticLibrary, T>::value}, + {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value}, + {"supports-input", std::is_base_of<SupportsInput, T>::value}, + {"supports-screens", std::is_base_of<SupportsScreen, T>::value}, + {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value}, + {"uses-feature", std::is_base_of<UsesFeature, T>::value}, + {"uses-library", std::is_base_of<UsesLibrary, T>::value}, + {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value}, + {"uses-package", std::is_base_of<UsesPackage, T>::value}, + {"uses-permission", std::is_base_of<UsesPermission, T>::value}, + {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value}, + {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value}, + {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value}, }; auto check = kTagCheck.find(element->tag()); @@ -2288,39 +2329,41 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate( const std::unordered_map<std::string, std::function<std::unique_ptr<ManifestExtractor::Element>()>> kTagCheck = { - {"action", &CreateType<Action>}, - {"activity", &CreateType<Activity>}, - {"application", &CreateType<Application>}, - {"category", &CreateType<Category>}, - {"compatible-screens", &CreateType<CompatibleScreens>}, - {"feature-group", &CreateType<FeatureGroup>}, - {"input-type", &CreateType<InputType>}, - {"intent-filter",&CreateType<IntentFilter>}, - {"manifest", &CreateType<Manifest>}, - {"meta-data", &CreateType<MetaData>}, - {"original-package", &CreateType<OriginalPackage>}, - {"overlay", &CreateType<Overlay>}, - {"package-verifier", &CreateType<PackageVerifier>}, - {"permission", &CreateType<Permission>}, - {"provider", &CreateType<Provider>}, - {"receiver", &CreateType<Receiver>}, - {"screen", &CreateType<Screen>}, - {"service", &CreateType<Service>}, - {"supports-gl-texture", &CreateType<SupportsGlTexture>}, - {"supports-input", &CreateType<SupportsInput>}, - {"supports-screens", &CreateType<SupportsScreen>}, - {"uses-configuration", &CreateType<UsesConfiguarion>}, - {"uses-feature", &CreateType<UsesFeature>}, - {"uses-permission", &CreateType<UsesPermission>}, - {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>}, - {"uses-library", &CreateType<UsesLibrary>}, - {"static-library", &CreateType<StaticLibrary>}, - {"uses-static-library", &CreateType<UsesStaticLibrary>}, - {"uses-package", &CreateType<UsesPackage>}, - {"additional-certificate", &CreateType<AdditionalCertificate>}, - {"uses-sdk", &CreateType<UsesSdkBadging>}, - {"uses-native-library", &CreateType<UsesNativeLibrary>}, - }; + {"action", &CreateType<Action>}, + {"activity", &CreateType<Activity>}, + {"additional-certificate", &CreateType<AdditionalCertificate>}, + {"application", &CreateType<Application>}, + {"category", &CreateType<Category>}, + {"compatible-screens", &CreateType<CompatibleScreens>}, + {"feature-group", &CreateType<FeatureGroup>}, + {"input-type", &CreateType<InputType>}, + {"intent-filter", &CreateType<IntentFilter>}, + {"manifest", &CreateType<Manifest>}, + {"meta-data", &CreateType<MetaData>}, + {"original-package", &CreateType<OriginalPackage>}, + {"overlay", &CreateType<Overlay>}, + {"package-verifier", &CreateType<PackageVerifier>}, + {"permission", &CreateType<Permission>}, + {"provider", &CreateType<Provider>}, + {"receiver", &CreateType<Receiver>}, + {"required-feature", &CreateType<RequiredFeature>}, + {"required-not-feature", &CreateType<RequiredNotFeature>}, + {"screen", &CreateType<Screen>}, + {"service", &CreateType<Service>}, + {"static-library", &CreateType<StaticLibrary>}, + {"supports-gl-texture", &CreateType<SupportsGlTexture>}, + {"supports-input", &CreateType<SupportsInput>}, + {"supports-screens", &CreateType<SupportsScreen>}, + {"uses-configuration", &CreateType<UsesConfiguarion>}, + {"uses-feature", &CreateType<UsesFeature>}, + {"uses-library", &CreateType<UsesLibrary>}, + {"uses-native-library", &CreateType<UsesNativeLibrary>}, + {"uses-package", &CreateType<UsesPackage>}, + {"uses-permission", &CreateType<UsesPermission>}, + {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>}, + {"uses-sdk", &CreateType<UsesSdkBadging>}, + {"uses-static-library", &CreateType<UsesStaticLibrary>}, + }; // Attempt to map the xml tag to a element inflater std::unique_ptr<ManifestExtractor::Element> element; diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index dac21d7e9200..3d8c25ebcbdd 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -393,6 +393,8 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, manifest_action["protected-broadcast"]; manifest_action["adopt-permissions"]; manifest_action["uses-permission"]; + manifest_action["uses-permission"]["required-feature"].Action(RequiredNameIsNotEmpty); + manifest_action["uses-permission"]["required-not-feature"].Action(RequiredNameIsNotEmpty); manifest_action["uses-permission-sdk-23"]; manifest_action["permission"]; manifest_action["permission"]["meta-data"] = meta_data_action; |