diff options
141 files changed, 3283 insertions, 2384 deletions
diff --git a/Android.bp b/Android.bp index 622b2c6120bd..9690969305bf 100644 --- a/Android.bp +++ b/Android.bp @@ -665,9 +665,8 @@ java_defaults { ], required: [ "framework-platform-compat-config", - // TODO: remove gps_debug, cec_config.xml and protolog.conf.json when the build system propagates "required" properly. + // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly. "gps_debug.conf", - "cec_config.xml", "icu4j-platform-compat-config", "libcore-platform-compat-config", "protolog.conf.json.gz", diff --git a/core/api/current.txt b/core/api/current.txt index bc25561cf1aa..61d2db931843 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -329,6 +329,7 @@ package android { field public static final int apiKey = 16843281; // 0x1010211 field public static final int appCategory = 16844101; // 0x1010545 field public static final int appComponentFactory = 16844154; // 0x101057a + field public static final int attributionTags = 16844353; // 0x1010641 field public static final int author = 16843444; // 0x10102b4 field public static final int authorities = 16842776; // 0x1010018 field public static final int autoAdvanceViewId = 16843535; // 0x101030f @@ -26137,10 +26138,6 @@ package android.net { ctor public NetworkSpecifier(); } - public class ParseException extends java.lang.RuntimeException { - field public String response; - } - public abstract class PlatformVpnProfile { method public final int getType(); method @NonNull public final String getTypeString(); @@ -40614,14 +40611,6 @@ package android.telephony { public static final class CarrierConfigManager.Iwlan { field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1 field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0 - field public static final int DH_GROUP_1024_BIT_MODP = 2; // 0x2 - field public static final int DH_GROUP_1536_BIT_MODP = 5; // 0x5 - field public static final int DH_GROUP_2048_BIT_MODP = 14; // 0xe - field public static final int DH_GROUP_3072_BIT_MODP = 15; // 0xf - field public static final int DH_GROUP_4096_BIT_MODP = 16; // 0x10 - field public static final int DH_GROUP_NONE = 0; // 0x0 - field public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; // 0xc - field public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13; // 0xd field public static final int EPDG_ADDRESS_CELLULAR_LOC = 3; // 0x3 field public static final int EPDG_ADDRESS_PCO = 2; // 0x2 field public static final int EPDG_ADDRESS_PLMN = 1; // 0x1 @@ -40629,12 +40618,6 @@ package android.telephony { field public static final int ID_TYPE_FQDN = 2; // 0x2 field public static final int ID_TYPE_KEY_ID = 11; // 0xb field public static final int ID_TYPE_RFC822_ADDR = 3; // 0x3 - field public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; // 0x5 - field public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; // 0x2 - field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; // 0xc - field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; // 0xd - field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe - field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0 field public static final String KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL = "iwlan.add_ke_to_child_session_rekey_bool"; field public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT = "iwlan.child_sa_rekey_hard_timer_sec_int"; field public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = "iwlan.child_sa_rekey_soft_timer_sec_int"; @@ -40654,10 +40637,6 @@ package android.telephony { field public static final String KEY_IKE_REMOTE_ID_TYPE_INT = "iwlan.ike_remote_id_type_int"; field public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_cbc_key_size_int_array"; field public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_ctr_key_size_int_array"; - field public static final int KEY_LEN_AES_128 = 128; // 0x80 - field public static final int KEY_LEN_AES_192 = 192; // 0xc0 - field public static final int KEY_LEN_AES_256 = 256; // 0x100 - field public static final int KEY_LEN_UNUSED = 0; // 0x0 field public static final String KEY_MAX_RETRIES_INT = "iwlan.max_retries_int"; field public static final String KEY_MCC_MNCS_STRING_ARRAY = "iwlan.mcc_mncs_string_array"; field public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT = "iwlan.natt_keep_alive_timer_sec_int"; @@ -40667,11 +40646,6 @@ package android.telephony { field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array"; field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array"; field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array"; - field public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; // 0x4 - field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2 - field public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5; // 0x5 - field public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6; // 0x6 - field public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7; // 0x7 } public abstract class CellIdentity implements android.os.Parcelable { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index eddf5a4a92b9..6019ab56dea7 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2901,7 +2901,7 @@ package android.graphics.fonts { } public class FontManager { - method @NonNull public android.text.FontConfig getFontConfig(); + method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public android.text.FontConfig getFontConfig(); method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int); field public static final int RESULT_ERROR_DOWNGRADING = -5; // 0xfffffffb field public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1; // 0xffffffff diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 8fa46591e514..a7ca44cd4a5c 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -706,6 +706,10 @@ package android.content { method public int getDisplayId(); } + public class SyncAdapterType implements android.os.Parcelable { + method @Nullable public String getPackageName(); + } + } package android.content.integrity { @@ -968,7 +972,7 @@ package android.graphics.drawable { package android.graphics.fonts { public class FontManager { - method @NonNull public android.text.FontConfig getFontConfig(); + method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public android.text.FontConfig getFontConfig(); method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int); field public static final int RESULT_ERROR_DOWNGRADING = -5; // 0xfffffffb field public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1; // 0xffffffff @@ -2921,7 +2925,7 @@ package android.window { method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo); method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo); method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer(); - method @BinderThread public void removeStartingWindow(int); + method @BinderThread public void removeStartingWindow(int, @Nullable android.view.SurfaceControl, @Nullable android.graphics.Rect, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean); method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void unregisterOrganizer(); } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 8e53b5ba1c9f..7dbbc54665e9 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -57,6 +57,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.DeadSystemException; +import android.os.Environment; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; @@ -120,6 +121,8 @@ public class WallpaperManager { /** {@hide} */ private static final String VALUE_CMF_COLOR = android.os.SystemProperties.get("ro.boot.hardware.color"); + /** {@hide} */ + private static final String WALLPAPER_CMF_PATH = "/wallpaper/image/"; /** * Activity Action: Show settings for choosing wallpaper. Do not use directly to construct @@ -2066,31 +2069,45 @@ public class WallpaperManager { return null; } else { whichProp = PROP_WALLPAPER; - final int defaultColorResId = context.getResources().getIdentifier( - "default_wallpaper_" + VALUE_CMF_COLOR, "drawable", "android"); - defaultResId = - defaultColorResId == 0 ? com.android.internal.R.drawable.default_wallpaper - : defaultColorResId; + defaultResId = com.android.internal.R.drawable.default_wallpaper; } final String path = SystemProperties.get(whichProp); + final InputStream wallpaperInputStream = getWallpaperInputStream(path); + if (wallpaperInputStream != null) { + return wallpaperInputStream; + } + final String cmfPath = getCmfWallpaperPath(); + final InputStream cmfWallpaperInputStream = getWallpaperInputStream(cmfPath); + if (cmfWallpaperInputStream != null) { + return cmfWallpaperInputStream; + } + try { + return context.getResources().openRawResource(defaultResId); + } catch (NotFoundException e) { + // no default defined for this device; this is not a failure + } + return null; + } + + private static InputStream getWallpaperInputStream(String path) { if (!TextUtils.isEmpty(path)) { final File file = new File(path); if (file.exists()) { try { return new FileInputStream(file); } catch (IOException e) { - // Ignored, fall back to platform default below + // Ignored, fall back to platform default } } } - try { - return context.getResources().openRawResource(defaultResId); - } catch (NotFoundException e) { - // no default defined for this device; this is not a failure - } return null; } + private static String getCmfWallpaperPath() { + return Environment.getProductDirectory() + WALLPAPER_CMF_PATH + "default_wallpaper_" + + VALUE_CMF_COLOR; + } + /** * Return {@link ComponentName} of the default live wallpaper, or * {@code null} if none is defined. diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java index 1c21b2aa73a5..47c333ceb931 100644 --- a/core/java/android/content/SyncAdapterType.java +++ b/core/java/android/content/SyncAdapterType.java @@ -17,6 +17,7 @@ package android.content; import android.annotation.Nullable; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -168,6 +169,7 @@ public class SyncAdapterType implements Parcelable { * * @hide */ + @TestApi public @Nullable String getPackageName() { return packageName; } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 5a17753bf9ad..58f83a73ff16 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1086,6 +1086,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { */ public WindowLayout windowLayout; + /** + * Attribution tags for finer grained calls if a {@android.content.Context#sendBroadcast(Intent, + * String)} is used with a permission. + * @hide + */ + public String[] attributionTags; + public ActivityInfo() { } @@ -1114,6 +1121,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { maxAspectRatio = orig.maxAspectRatio; minAspectRatio = orig.minAspectRatio; supportsSizeChanges = orig.supportsSizeChanges; + attributionTags = orig.attributionTags; } /** @@ -1361,6 +1369,15 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { if (supportsSizeChanges) { pw.println(prefix + "supportsSizeChanges=true"); } + if (attributionTags != null && attributionTags.length > 0) { + StringBuilder tags = new StringBuilder(); + tags.append(attributionTags[0]); + for (int i = 1; i < attributionTags.length; i++) { + tags.append(", "); + tags.append(attributionTags[i]); + } + pw.println(prefix + "attributionTags=[" + tags + "]"); + } super.dumpBack(pw, prefix, dumpFlags); } @@ -1406,6 +1423,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { dest.writeFloat(maxAspectRatio); dest.writeFloat(minAspectRatio); dest.writeBoolean(supportsSizeChanges); + dest.writeString8Array(attributionTags); } /** @@ -1525,6 +1543,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { maxAspectRatio = source.readFloat(); minAspectRatio = source.readFloat(); supportsSizeChanges = source.readBoolean(); + attributionTags = source.createString8Array(); } /** diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java index 9a84ded99c67..b660a00443a4 100644 --- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java +++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java @@ -482,6 +482,7 @@ public class PackageInfoWithoutStateUtils { ai.rotationAnimation = a.getRotationAnimation(); ai.colorMode = a.getColorMode(); ai.windowLayout = a.getWindowLayout(); + ai.attributionTags = a.getAttributionTags(); if ((flags & PackageManager.GET_META_DATA) != 0) { ai.metaData = a.getMetaData(); } diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java index 6f478accedd7..9285ccb3cf0c 100644 --- a/core/java/android/content/pm/parsing/component/ParsedActivity.java +++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java @@ -82,6 +82,9 @@ public class ParsedActivity extends ParsedMainComponent { @Nullable ActivityInfo.WindowLayout windowLayout; + @Nullable + String[] attributionTags; + public ParsedActivity(ParsedActivity other) { super(other); this.theme = other.theme; @@ -107,6 +110,7 @@ public class ParsedActivity extends ParsedMainComponent { this.rotationAnimation = other.rotationAnimation; this.colorMode = other.colorMode; this.windowLayout = other.windowLayout; + this.attributionTags = other.attributionTags; } /** @@ -172,6 +176,7 @@ public class ParsedActivity extends ParsedMainComponent { alias.requestedVrComponent = target.requestedVrComponent; alias.directBootAware = target.directBootAware; alias.setProcessName(target.getProcessName()); + alias.attributionTags = target.attributionTags; return alias; // Not all attributes from the target ParsedActivity are copied to the alias. @@ -299,6 +304,7 @@ public class ParsedActivity extends ParsedMainComponent { } else { dest.writeBoolean(false); } + dest.writeString8Array(this.attributionTags); } public ParsedActivity() { @@ -332,6 +338,7 @@ public class ParsedActivity extends ParsedMainComponent { if (in.readBoolean()) { windowLayout = new ActivityInfo.WindowLayout(in); } + this.attributionTags = in.createString8Array(); } public static final Parcelable.Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() { @@ -445,4 +452,9 @@ public class ParsedActivity extends ParsedMainComponent { public ActivityInfo.WindowLayout getWindowLayout() { return windowLayout; } + + @Nullable + public String[] getAttributionTags() { + return attributionTags; + } } diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java index 0f4aa061b72d..d99c4109e5ad 100644 --- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java @@ -210,6 +210,11 @@ public class ParsedActivityUtils { pkg.setVisibleToInstantApps(true); } + String attributionTags = sa.getString(R.styleable.AndroidManifestActivity_attributionTags); + if (attributionTags != null) { + activity.attributionTags = attributionTags.split("\\|"); + } + return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver, false /*isAlias*/, visibleToEphemeral, input, R.styleable.AndroidManifestActivity_parentActivityName, diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java index 7bf692f1d318..fa2ccbc189ad 100644 --- a/core/java/android/graphics/fonts/FontManager.java +++ b/core/java/android/graphics/fonts/FontManager.java @@ -195,6 +195,7 @@ public class FontManager { * @return The current font configuration. null if failed to fetch information from the system * service. */ + @RequiresPermission(Manifest.permission.UPDATE_FONTS) public @NonNull FontConfig getFontConfig() { try { return mIFontManager.getFontConfig(); diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 788afe3bdb8e..365dea691489 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -65,7 +65,7 @@ public class SystemSensorManager extends SensorManager { private static final int CAPPED_SAMPLING_RATE_LEVEL = SensorDirectChannel.RATE_NORMAL; private static final String HIGH_SAMPLING_RATE_SENSORS_PERMISSION = - "android.permisison.HIGH_SAMPLING_RATE_SENSORS"; + "android.permission.HIGH_SAMPLING_RATE_SENSORS"; /** * For apps targeting S and above, a SecurityException is thrown when they do not have * HIGH_SAMPLING_RATE_SENSORS permission, run in debug mode, and request sampling rates that diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 124c0b00b2b2..21bf8b8c30e4 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -728,11 +728,11 @@ public class Environment { /** * Standard directory in which to place any audio files that should be * in the regular list of music for the user. - * This may be combined with + * This may be combined with {@link #DIRECTORY_AUDIOBOOKS}, * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, - * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series - * of directories to categories a particular audio file as more than one - * type. + * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and + * {@link #DIRECTORY_RECORDINGS} as a series of directories to + * categorize a particular audio file as more than one type. */ public static String DIRECTORY_MUSIC = "Music"; @@ -741,10 +741,10 @@ public class Environment { * in the list of podcasts that the user can select (not as regular * music). * This may be combined with {@link #DIRECTORY_MUSIC}, - * {@link #DIRECTORY_NOTIFICATIONS}, - * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series - * of directories to categories a particular audio file as more than one - * type. + * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_NOTIFICATIONS}, + * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and + * {@link #DIRECTORY_RECORDINGS} as a series of directories to + * categorize a particular audio file as more than one type. */ public static String DIRECTORY_PODCASTS = "Podcasts"; @@ -753,10 +753,10 @@ public class Environment { * in the list of ringtones that the user can select (not as regular * music). * This may be combined with {@link #DIRECTORY_MUSIC}, - * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and - * {@link #DIRECTORY_ALARMS} as a series - * of directories to categories a particular audio file as more than one - * type. + * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS}, + * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_ALARMS}, + * and {@link #DIRECTORY_RECORDINGS} as a series of directories + * to categorize a particular audio file as more than one type. */ public static String DIRECTORY_RINGTONES = "Ringtones"; @@ -765,10 +765,10 @@ public class Environment { * in the list of alarms that the user can select (not as regular * music). * This may be combined with {@link #DIRECTORY_MUSIC}, - * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, - * and {@link #DIRECTORY_RINGTONES} as a series - * of directories to categories a particular audio file as more than one - * type. + * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS}, + * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_RINGTONES}, + * and {@link #DIRECTORY_RECORDINGS} as a series of directories + * to categorize a particular audio file as more than one type. */ public static String DIRECTORY_ALARMS = "Alarms"; @@ -777,10 +777,10 @@ public class Environment { * in the list of notifications that the user can select (not as regular * music). * This may be combined with {@link #DIRECTORY_MUSIC}, - * {@link #DIRECTORY_PODCASTS}, - * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series - * of directories to categories a particular audio file as more than one - * type. + * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS}, + * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and + * {@link #DIRECTORY_RECORDINGS} as a series of directories to + * categorize a particular audio file as more than one type. */ public static String DIRECTORY_NOTIFICATIONS = "Notifications"; @@ -831,14 +831,26 @@ public class Environment { public static String DIRECTORY_SCREENSHOTS = "Screenshots"; /** - * Standard directory in which to place any audio files which are - * audiobooks. + * Standard directory in which to place any audio files that should be + * in the list of audiobooks that the user can select (not as regular + * music). + * This may be combined with {@link #DIRECTORY_MUSIC}, + * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, + * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, + * and {@link #DIRECTORY_RECORDINGS} as a series of directories + * to categorize a particular audio file as more than one type. */ public static String DIRECTORY_AUDIOBOOKS = "Audiobooks"; /** - * Standard directory in which to place any audio files which are - * recordings. + * Standard directory in which to place any audio files that should be + * in the list of voice recordings recorded by voice recorder apps that + * the user can select (not as regular music). + * This may be combined with {@link #DIRECTORY_MUSIC}, + * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS}, + * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_ALARMS}, + * and {@link #DIRECTORY_RINGTONES} as a series of directories + * to categorize a particular audio file as more than one type. */ @NonNull // The better way is that expose a static method getRecordingDirectories. diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index dc6f63a94685..047c05a8734b 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -30,6 +30,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -265,6 +266,13 @@ public final class IncrementalManager { } /** + * Checks if an fd corresponds to a file on a mounted Incremental File System. + */ + public static boolean isIncrementalFileFd(@NonNull FileDescriptor fd) { + return nativeIsIncrementalFd(fd.getInt$()); + } + + /** * Returns raw signature for file if it's on Incremental File System. * Unsafe, use only if you are sure what you are doing. */ @@ -421,9 +429,22 @@ public final class IncrementalManager { storage.unregisterStorageHealthListener(); } + /** + * Returns the metrics of an Incremental Storage. + */ + public IncrementalMetrics getMetrics(@NonNull String codePath) { + final IncrementalStorage storage = openStorage(codePath); + if (storage == null) { + // storage does not exist, package not installed + return null; + } + return new IncrementalMetrics(storage.getMetrics()); + } + /* Native methods */ private static native boolean nativeIsEnabled(); private static native boolean nativeIsV2Available(); private static native boolean nativeIsIncrementalPath(@NonNull String path); + private static native boolean nativeIsIncrementalFd(@NonNull int fd); private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path); } diff --git a/core/java/android/os/incremental/IncrementalMetrics.java b/core/java/android/os/incremental/IncrementalMetrics.java new file mode 100644 index 000000000000..44dea1be50f0 --- /dev/null +++ b/core/java/android/os/incremental/IncrementalMetrics.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os.incremental; + +import android.annotation.NonNull; +import android.os.PersistableBundle; + +/** + * Provides methods to access metrics about an app installed via Incremental + * @hide + */ +public class IncrementalMetrics { + @NonNull private final PersistableBundle mData; + + public IncrementalMetrics(@NonNull PersistableBundle data) { + mData = data; + } + + /** + * @return Milliseconds between now and when the oldest pending read happened + */ + public long getMillisSinceOldestPendingRead() { + return mData.getLong(IIncrementalService.METRICS_MILLIS_SINCE_OLDEST_PENDING_READ, -1); + } +} diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java index e6ce8cd56d28..7cf0144d71f7 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.DataLoaderParams; import android.content.pm.IDataLoaderStatusListener; +import android.os.PersistableBundle; import android.os.RemoteException; import java.io.File; @@ -601,4 +602,17 @@ public final class IncrementalStorage { return; } } + + /** + * Returns the metrics of the current storage. + * {@see IIncrementalService} for metrics keys. + */ + public PersistableBundle getMetrics() { + try { + return mService.getMetrics(mId); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + return null; + } + } } diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index c97c995641d1..7e6175c03d35 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -200,10 +200,9 @@ public final class ApkSigningBlockUtils { // physical memory. DataSource beforeApkSigningBlock = - new MemoryMappedFileDataSource(apkFileDescriptor, 0, - signatureInfo.apkSigningBlockOffset); + DataSource.create(apkFileDescriptor, 0, signatureInfo.apkSigningBlockOffset); DataSource centralDir = - new MemoryMappedFileDataSource( + DataSource.create( apkFileDescriptor, signatureInfo.centralDirOffset, signatureInfo.eocdOffset - signatureInfo.centralDirOffset); diff --git a/core/java/android/util/apk/DataSource.java b/core/java/android/util/apk/DataSource.java index 82f3800aa6d3..dd6389d012bc 100644 --- a/core/java/android/util/apk/DataSource.java +++ b/core/java/android/util/apk/DataSource.java @@ -16,6 +16,10 @@ package android.util.apk; +import android.annotation.NonNull; +import android.os.incremental.IncrementalManager; + +import java.io.FileDescriptor; import java.io.IOException; import java.security.DigestException; @@ -35,4 +39,22 @@ interface DataSource { */ void feedIntoDataDigester(DataDigester md, long offset, int size) throws IOException, DigestException; + + /** + * Creates a DataSource that can handle the passed fd in the most efficient and safe manner. + * @param fd file descriptor to read from + * @param pos starting offset + * @param size size of the region + * @return created DataSource object + */ + static @NonNull DataSource create(@NonNull FileDescriptor fd, long pos, long size) { + if (IncrementalManager.isIncrementalFileFd(fd)) { + // IncFS-based files may have missing pages, and reading those via mmap() results + // in a SIGBUS signal. Java doesn't have a good way of catching it, ending up killing + // the process by default. Going back to read() is the safest option for these files. + return new ReadFileDataSource(fd, pos, size); + } else { + return new MemoryMappedFileDataSource(fd, pos, size); + } + } } diff --git a/core/java/android/util/apk/MemoryMappedFileDataSource.java b/core/java/android/util/apk/MemoryMappedFileDataSource.java index 8d2b1e328862..69a526d09ad9 100644 --- a/core/java/android/util/apk/MemoryMappedFileDataSource.java +++ b/core/java/android/util/apk/MemoryMappedFileDataSource.java @@ -40,6 +40,7 @@ class MemoryMappedFileDataSource implements DataSource { /** * Constructs a new {@code MemoryMappedFileDataSource} for the specified region of the file. * + * @param fd file descriptor to read from. * @param position start position of the region in the file. * @param size size (in bytes) of the region. */ diff --git a/core/java/android/util/apk/ReadFileDataSource.java b/core/java/android/util/apk/ReadFileDataSource.java new file mode 100644 index 000000000000..d0e1140c0eb4 --- /dev/null +++ b/core/java/android/util/apk/ReadFileDataSource.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util.apk; + +import android.system.ErrnoException; +import android.system.Os; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.security.DigestException; + +/** + * {@link DataSource} which provides data from a file descriptor by reading the sections + * of the file via raw read() syscall. This is slower than memory-mapping but safer. + */ +class ReadFileDataSource implements DataSource { + private final FileDescriptor mFd; + private final long mFilePosition; + private final long mSize; + + private static final int CHUNK_SIZE = 1024 * 1024; + + /** + * Constructs a new {@code ReadFileDataSource} for the specified region of the file. + * + * @param fd file descriptor to read from. + * @param position start position of the region in the file. + * @param size size (in bytes) of the region. + */ + ReadFileDataSource(FileDescriptor fd, long position, long size) { + mFd = fd; + mFilePosition = position; + mSize = size; + } + + @Override + public long size() { + return mSize; + } + + @Override + public void feedIntoDataDigester(DataDigester md, long offset, int size) + throws IOException, DigestException { + try { + final byte[] buffer = new byte[Math.min(size, CHUNK_SIZE)]; + final long start = mFilePosition + offset; + final long end = start + size; + for (long pos = start, curSize = Math.min(size, CHUNK_SIZE); + pos < end; curSize = Math.min(end - pos, CHUNK_SIZE)) { + final int readSize = Os.pread(mFd, buffer, 0, (int) curSize, pos); + md.consume(ByteBuffer.wrap(buffer, 0, readSize)); + pos += readSize; + } + } catch (ErrnoException e) { + throw new IOException(e); + } + } +} diff --git a/core/java/android/util/apk/TEST_MAPPING b/core/java/android/util/apk/TEST_MAPPING index 8544e82e04e0..4598b4ffe4f6 100644 --- a/core/java/android/util/apk/TEST_MAPPING +++ b/core/java/android/util/apk/TEST_MAPPING @@ -1,6 +1,17 @@ { "presubmit": [ { + "name": "CtsContentTestCases", + "options": [ + { + "include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest" + }, + { + "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest" + } + ] + }, + { "name": "FrameworksCoreTests", "options": [ { diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java index 4596c6e8f83d..b0a5992230bd 100644 --- a/core/java/android/util/apk/VerityBuilder.java +++ b/core/java/android/util/apk/VerityBuilder.java @@ -294,7 +294,7 @@ public abstract class VerityBuilder { // 1. Digest the whole file by chunks. consumeByChunk(digester, - new MemoryMappedFileDataSource(file.getFD(), 0, file.length()), + DataSource.create(file.getFD(), 0, file.length()), MMAP_REGION_SIZE_BYTES); // 2. Pad 0s up to the nearest 4096-byte block before hashing. @@ -315,7 +315,7 @@ public abstract class VerityBuilder { // 1. Digest from the beginning of the file, until APK Signing Block is reached. consumeByChunk(digester, - new MemoryMappedFileDataSource(apk.getFD(), 0, signatureInfo.apkSigningBlockOffset), + DataSource.create(apk.getFD(), 0, signatureInfo.apkSigningBlockOffset), MMAP_REGION_SIZE_BYTES); // 2. Skip APK Signing Block and continue digesting, until the Central Directory offset @@ -323,7 +323,7 @@ public abstract class VerityBuilder { long eocdCdOffsetFieldPosition = signatureInfo.eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET; consumeByChunk(digester, - new MemoryMappedFileDataSource(apk.getFD(), signatureInfo.centralDirOffset, + DataSource.create(apk.getFD(), signatureInfo.centralDirOffset, eocdCdOffsetFieldPosition - signatureInfo.centralDirOffset), MMAP_REGION_SIZE_BYTES); @@ -338,7 +338,7 @@ public abstract class VerityBuilder { long offsetAfterEocdCdOffsetField = eocdCdOffsetFieldPosition + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE; consumeByChunk(digester, - new MemoryMappedFileDataSource(apk.getFD(), offsetAfterEocdCdOffsetField, + DataSource.create(apk.getFD(), offsetAfterEocdCdOffsetField, apk.length() - offsetAfterEocdCdOffsetField), MMAP_REGION_SIZE_BYTES); diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java index 49ff237403b2..b28cfb87e28d 100644 --- a/core/java/android/util/imetracing/ImeTracing.java +++ b/core/java/android/util/imetracing/ImeTracing.java @@ -23,7 +23,6 @@ import android.inputmethodservice.AbstractInputMethodService; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; -import android.os.ShellCommand; import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.inputmethod.InputMethodManager; @@ -104,12 +103,6 @@ public abstract class ImeTracing { public abstract void addToBuffer(ProtoOutputStream proto, int source); /** - * @param shell The shell command to process - * @return {@code 0} if the command was successfully processed, {@code -1} otherwise - */ - public abstract int onShellCommand(ShellCommand shell); - - /** * Starts a proto dump of the client side information. * * @param where Place where the trace was triggered. diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java index 2c2763988d14..35a81b7aeea5 100644 --- a/core/java/android/util/imetracing/ImeTracingClientImpl.java +++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.inputmethodservice.AbstractInputMethodService; import android.os.RemoteException; import android.os.ServiceManager.ServiceNotFoundException; -import android.os.ShellCommand; import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.inputmethod.InputMethodManager; @@ -45,11 +44,6 @@ class ImeTracingClientImpl extends ImeTracing { } @Override - public int onShellCommand(ShellCommand shell) { - return -1; - } - - @Override public void triggerClientDump(String where, @NonNull InputMethodManager immInstance, ProtoOutputStream icProto) { if (!isEnabled() || !isAvailable()) { diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java index e793c280afbc..77f017a4654a 100644 --- a/core/java/android/util/imetracing/ImeTracingServerImpl.java +++ b/core/java/android/util/imetracing/ImeTracingServerImpl.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.inputmethodservice.AbstractInputMethodService; import android.os.RemoteException; import android.os.ServiceManager.ServiceNotFoundException; -import android.os.ShellCommand; import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto; @@ -106,32 +105,6 @@ class ImeTracingServerImpl extends ImeTracing { } } - /** - * Responds to a shell command of the format "adb shell cmd input_method ime tracing <command>" - * - * @param shell The shell command to process - * @return {@code 0} if the command was valid and successfully processed, {@code -1} otherwise - */ - @Override - public int onShellCommand(ShellCommand shell) { - PrintWriter pw = shell.getOutPrintWriter(); - String cmd = shell.getNextArgRequired(); - switch (cmd) { - case "start": - startTrace(pw); - return 0; - case "stop": - stopTrace(pw); - return 0; - default: - pw.println("Unknown command: " + cmd); - pw.println("Input method trace options:"); - pw.println(" start: Start tracing"); - pw.println(" stop: Stop tracing"); - return -1; - } - } - @Override public void triggerClientDump(String where, InputMethodManager immInstance, ProtoOutputStream icProto) { diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index a8aaeb7846a2..9aaf5c066073 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -636,7 +636,9 @@ public final class DisplayInfo implements Parcelable { public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo, Configuration configuration) { Rect bounds = configuration.windowConfiguration.getMaxBounds(); - getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height()); + // Pass in null configuration to ensure width and height are not overridden to app bounds. + getMetricsWithSize(outMetrics, compatInfo, /* configuration= */ null, + bounds.width(), bounds.height()); } public int getNaturalWidth() { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 9fc415d6401f..35726c0c1f04 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -8572,6 +8572,17 @@ public final class ViewRootImpl implements ViewParent, } @Override + public void onDragEvent(boolean isExiting, float x, float y) { + // force DRAG_EXITED_EVENT if appropriate + DragEvent event = DragEvent.obtain( + isExiting ? DragEvent.ACTION_DRAG_EXITED : DragEvent.ACTION_DRAG_LOCATION, + x, y, 0 /* offsetX */, 0 /* offsetY */, null/* localState */, + null/* description */, null /* data */, null /* dragSurface */, + null /* dragAndDropPermissions */, false /* result */); + dispatchDragEvent(event); + } + + @Override public void dispose() { unscheduleConsumeBatchedInput(); super.dispose(); diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index 34fe51e82e8f..42d75353e5df 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -200,6 +200,8 @@ public class AnalogClock extends View { mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone)); createClock(); + a.recycle(); + mDialWidth = mDial.getIntrinsicWidth(); mDialHeight = mDial.getIntrinsicHeight(); } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 012352d0a0f5..7517b805da69 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3790,7 +3790,7 @@ public class Editor { } public SuggestionsPopupWindow() { - mCursorWasVisibleBeforeSuggestions = mCursorVisible; + mCursorWasVisibleBeforeSuggestions = mTextView.isCursorVisibleFromAttr(); } @Override @@ -3957,7 +3957,7 @@ public class Editor { } if (updateSuggestions()) { - mCursorWasVisibleBeforeSuggestions = mCursorVisible; + mCursorWasVisibleBeforeSuggestions = mTextView.isCursorVisibleFromAttr(); mTextView.setCursorVisible(false); mIsShowingUp = true; super.show(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 177a9f164cb3..dba7fa915f35 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -503,7 +503,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mImeIsConsumingInput; // Whether cursor is visible without regard to {@link mImeConsumesInput}. - // {code true} is the default value. + // {@code true} is the default value. private boolean mCursorVisibleFromAttr = true; static class Drawables { @@ -10571,6 +10571,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mEditor == null ? true : mEditor.mCursorVisible; } + /** + * @return whether cursor is visible without regard to {@code mImeIsConsumingInput}. + * {@code true} is the default value. + * + * @see #setCursorVisible(boolean) + * @hide + */ + public boolean isCursorVisibleFromAttr() { + return mCursorVisibleFromAttr; + } + private boolean canMarquee() { int width = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(); return width > 0 && (mLayout.getLineWidth(0) > width diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl index 8f541d0bd194..3eb35c2c5e8a 100644 --- a/core/java/android/window/ITaskOrganizer.aidl +++ b/core/java/android/window/ITaskOrganizer.aidl @@ -18,6 +18,7 @@ package android.window; import android.view.SurfaceControl; import android.app.ActivityManager; +import android.graphics.Rect; import android.window.StartingWindowInfo; import android.window.WindowContainerToken; @@ -38,8 +39,12 @@ oneway interface ITaskOrganizer { /** * Called when the Task want to remove the starting window. + * @param leash A persistent leash for the top window in this task. + * @param frame Window frame of the top window. + * @param playRevealAnimation Play vanish animation. */ - void removeStartingWindow(int taskId); + void removeStartingWindow(int taskId, in SurfaceControl leash, in Rect frame, + in boolean playRevealAnimation); /** * Called when the Task want to copy the splash screen. diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index 35ccfca101d3..da445b8b9f33 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -46,6 +46,8 @@ import android.widget.FrameLayout; import com.android.internal.R; import com.android.internal.policy.DecorView; +import java.util.function.Consumer; + /** * <p>The view which allows an activity to customize its splash screen exit animation.</p> * @@ -77,7 +79,8 @@ public final class SplashScreenView extends FrameLayout { private Animatable mAnimatableIcon; private ValueAnimator mAnimator; - + private Runnable mAnimationFinishListener; + private Consumer<Canvas> mOnDrawCallback; // cache original window and status private Window mWindow; private boolean mDrawBarBackground; @@ -85,7 +88,7 @@ public final class SplashScreenView extends FrameLayout { private int mNavigationBarColor; /** - * Internal builder to create a SplashScreenWindowView object. + * Internal builder to create a SplashScreenView object. * @hide */ public static class Builder { @@ -391,7 +394,7 @@ public final class SplashScreenView extends FrameLayout { * Get the initial background color of this view. * @hide */ - @ColorInt int getInitBackgroundColor() { + public @ColorInt int getInitBackgroundColor() { return mInitBackgroundColor; } diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java index d1c1e40d1578..c7672dc9fe7d 100644 --- a/core/java/android/window/StartingWindowInfo.java +++ b/core/java/android/window/StartingWindowInfo.java @@ -126,6 +126,12 @@ public final class StartingWindowInfo implements Parcelable { */ public int splashScreenThemeResId; + /** + * Is keyguard occluded on default display. + * @hide + */ + public boolean isKeyguardOccluded = false; + public StartingWindowInfo() { } @@ -147,6 +153,7 @@ public final class StartingWindowInfo implements Parcelable { dest.writeTypedObject(topOpaqueWindowLayoutParams, flags); dest.writeTypedObject(mainWindowLayoutParams, flags); dest.writeInt(splashScreenThemeResId); + dest.writeBoolean(isKeyguardOccluded); } void readFromParcel(@NonNull Parcel source) { @@ -157,6 +164,7 @@ public final class StartingWindowInfo implements Parcelable { WindowManager.LayoutParams.CREATOR); mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR); splashScreenThemeResId = source.readInt(); + isKeyguardOccluded = source.readBoolean(); } @Override diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index 04020ec3ee8a..3340cf4fb707 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -24,6 +24,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.app.ActivityManager; +import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; import android.view.SurfaceControl; @@ -100,9 +101,14 @@ public class TaskOrganizer extends WindowOrganizer { /** * Called when the Task want to remove the starting window. + * @param leash A persistent leash for the top window in this task. Release it once exit + * animation has finished. + * @param frame Window frame of the top window. + * @param playRevealAnimation Play vanish animation. */ @BinderThread - public void removeStartingWindow(int taskId) {} + public void removeStartingWindow(int taskId, @Nullable SurfaceControl leash, + @Nullable Rect frame, boolean playRevealAnimation) {} /** * Called when the Task want to copy the splash screen. @@ -217,15 +223,16 @@ public class TaskOrganizer extends WindowOrganizer { private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() { @Override - public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo, appToken)); } @Override - public void removeStartingWindow(int taskId) { - mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskId)); + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { + mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskId, leash, frame, + playRevealAnimation)); } @Override diff --git a/core/jni/android_os_incremental_IncrementalManager.cpp b/core/jni/android_os_incremental_IncrementalManager.cpp index 2384efaf1a54..413bcef57a64 100644 --- a/core/jni/android_os_incremental_IncrementalManager.cpp +++ b/core/jni/android_os_incremental_IncrementalManager.cpp @@ -41,6 +41,10 @@ static jboolean nativeIsIncrementalPath(JNIEnv* env, return (jboolean)IncFs_IsIncFsPath(path.c_str()); } +static jboolean nativeIsIncrementalFd(JNIEnv* env, jobject clazz, jint fd) { + return (jboolean)IncFs_IsIncFsFd(fd); +} + static jbyteArray nativeUnsafeGetFileSignature(JNIEnv* env, jobject clazz, jstring javaPath) { ScopedUtfChars path(env, javaPath); @@ -61,6 +65,7 @@ static const JNINativeMethod method_table[] = {{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled}, {"nativeIsV2Available", "()Z", (void*)nativeIsV2Available}, {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath}, + {"nativeIsIncrementalFd", "(I)Z", (void*)nativeIsIncrementalFd}, {"nativeUnsafeGetFileSignature", "(Ljava/lang/String;)[B", (void*)nativeUnsafeGetFileSignature}}; diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 96326f591998..9746a07a1b77 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -34,8 +34,6 @@ #include "core_jni_helpers.h" -using android::base::Result; - namespace android { // Log debug messages about the dispatch cycle. @@ -199,9 +197,16 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { ScopedLocalRef<jobject> senderObj(env, NULL); bool skipCallbacks = false; for (;;) { - Result<InputPublisher::Finished> result = mInputPublisher.receiveFinishedSignal(); - if (!result.ok()) { - const status_t status = result.error().code(); + uint32_t publishedSeq; + bool handled; + std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback = + [&publishedSeq, &handled](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + publishedSeq = inSeq; + handled = inHandled; + }; + status_t status = mInputPublisher.receiveFinishedSignal(callback); + if (status) { if (status == WOULD_BLOCK) { return OK; } @@ -210,7 +215,7 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { return status; } - auto it = mPublishedSeqMap.find(result->seq); + auto it = mPublishedSeqMap.find(publishedSeq); if (it == mPublishedSeqMap.end()) { continue; } @@ -220,9 +225,9 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, " - "pendingEvents=%zu.", - getInputChannelName().c_str(), seq, result->handled ? "true" : "false", - mPublishedSeqMap.size()); + "pendingEvents=%zu.", + getInputChannelName().c_str(), seq, handled ? "true" : "false", + mPublishedSeqMap.size()); } if (!skipCallbacks) { @@ -236,9 +241,8 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { } env->CallVoidMethod(senderObj.get(), - gInputEventSenderClassInfo.dispatchInputEventFinished, - static_cast<jint>(result->seq), - static_cast<jboolean>(result->handled)); + gInputEventSenderClassInfo.dispatchInputEventFinished, + jint(seq), jboolean(handled)); if (env->ExceptionCheck()) { ALOGE("Exception dispatching finished signal."); skipCallbacks = true; diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index cf96fe63f82c..0bed29b7ba28 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -193,7 +193,7 @@ static constexpr unsigned int STORAGE_DIR_CHECK_MAX_INTERVAL_US = 1000; * If it exceeds 2s, PROC_START_TIMEOUT_MSG will kill the starting app anyway, * so it's fine to assume max retries is 5 mins. */ -static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 60 * 5; +static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 1000 * 60 * 5; /** * A helper class containing accounting information for USAPs. diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index f26bf7cdb6c1..a7127ad79401 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -346,6 +346,7 @@ message ActivityRecordProto { optional int32 proc_id = 29; optional bool translucent = 30; optional bool pip_auto_enter_enabled = 31; + optional bool in_size_compat_mode = 32; } /* represents WindowToken */ @@ -406,7 +407,7 @@ message WindowStateProto { optional int64 finished_seamless_rotation_frame = 40; optional WindowFramesProto window_frames = 41; optional bool force_seamless_rotation = 42; - optional bool in_size_compat_mode = 43; + optional bool has_compat_scale = 43; optional float global_scale = 44; } diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index cc52655ad7d2..601d66ec7b39 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -829,7 +829,6 @@ {@code FLAG_ACTIVITY_MULTIPLE_TASK} is set.--> <enum name="singleInstancePerTask" value="4" /> </attr> - <!-- Specify the orientation an activity should be run in. If not specified, it will run in the current preferred orientation of the screen. @@ -1603,6 +1602,12 @@ <enum name="sync" value="2" /> </attr> + <!-- Attribution tag to be used for permission sub-attribution if a + permission is checked in {@link android.content.Context#sendBroadcast(Intent, String)}. + Multiple tags can be specified separated by '|'. + --> + <attr name="attributionTags" format="string" /> + <!-- The <code>manifest</code> tag is the root of an <code>AndroidManifest.xml</code> file, describing the contents of an Android package (.apk) file. One @@ -2825,6 +2830,10 @@ <p> See {@link android.content.pm.ActivityInfo#FLAG_PREFER_MINIMAL_POST_PROCESSING} --> <attr name="preferMinimalPostProcessing" format="boolean"/> + <!-- Specify the attributionTags to be used if a permission is required due to + {@link android.content.Context#sendBroadcast(Intent, String)} being used. + Multiple tags can be specified separated by '|'. --> + <attr name="attributionTags"/> </declare-styleable> <!-- The <code>activity-alias</code> tag declares a new diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a3d0ab52ba57..f6fee880bf2f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4722,4 +4722,97 @@ <!-- Whether to allow the caching of the SIM PIN for verification after unattended reboot --> <bool name="config_allow_pin_storage_for_unattended_reboot">true</bool> + + <!-- CEC Configuration --> + <bool name="config_cecHdmiCecEnabled_userConfigurable">true</bool> + <bool name="config_cecHdmiCecControlEnabled_allowed">true</bool> + <bool name="config_cecHdmiCecControlEnabled_default">true</bool> + <bool name="config_cecHdmiCecControlDisabled_allowed">true</bool> + <bool name="config_cecHdmiCecControlDisabled_default">false</bool> + + <bool name="config_cecHdmiCecVersion_userConfigurable">true</bool> + <bool name="config_cecHdmiCecVersion14b_allowed">true</bool> + <bool name="config_cecHdmiCecVersion14b_default">true</bool> + <bool name="config_cecHdmiCecVersion20_allowed">true</bool> + <bool name="config_cecHdmiCecVersion20_default">false</bool> + + <bool name="config_cecSendStandbyOnSleep_userConfigurable">true</bool> + <bool name="config_cecPowerControlModeTv_allowed">true</bool> + <bool name="config_cecPowerControlModeTv_default">true</bool> + <bool name="config_cecPowerControlModeBroadcast_allowed">true</bool> + <bool name="config_cecPowerControlModeBroadcast_default">false</bool> + <bool name="config_cecPowerControlModeNone_allowed">true</bool> + <bool name="config_cecPowerControlModeNone_default">false</bool> + + <bool name="config_cecPowerStateChangeOnActiveSourceLost_userConfigurable">true</bool> + <bool name="config_cecPowerStateChangeOnActiveSourceLostNone_allowed">true</bool> + <bool name="config_cecPowerStateChangeOnActiveSourceLostNone_default">true</bool> + <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed">true</bool> + <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default">false</bool> + + <bool name="config_cecSystemAudioModeMuting_userConfigurable">true</bool> + <bool name="config_cecSystemAudioModeMutingEnabled_allowed">true</bool> + <bool name="config_cecSystemAudioModeMutingEnabled_default">true</bool> + <bool name="config_cecSystemAudioModeMutingDisabled_allowed">true</bool> + <bool name="config_cecSystemAudioModeMutingDisabled_default">false</bool> + + <bool name="config_cecVolumeControlMode_userConfigurable">true</bool> + <bool name="config_cecVolumeControlModeEnabled_allowed">true</bool> + <bool name="config_cecVolumeControlModeEnabled_default">true</bool> + <bool name="config_cecVolumeControlModeDisabled_allowed">true</bool> + <bool name="config_cecVolumeControlModeDisabled_default">false</bool> + + <bool name="config_cecTvWakeOnOneTouchPlay_userConfigurable">true</bool> + <bool name="config_cecTvWakeOnOneTouchPlayEnabled_allowed">true</bool> + <bool name="config_cecTvWakeOnOneTouchPlayEnabled_default">true</bool> + <bool name="config_cecTvWakeOnOneTouchPlayDisabled_allowed">true</bool> + <bool name="config_cecTvWakeOnOneTouchPlayDisabled_default">false</bool> + + <bool name="config_cecTvSendStandbyOnSleep_userConfigurable">true</bool> + <bool name="config_cecTvSendStandbyOnSleepEnabled_allowed">true</bool> + <bool name="config_cecTvSendStandbyOnSleepEnabled_default">true</bool> + <bool name="config_cecTvSendStandbyOnSleepDisabled_allowed">true</bool> + <bool name="config_cecTvSendStandbyOnSleepDisabled_default">false</bool> + + <bool name="config_cecRcProfileTv_userConfigurable">true</bool> + <bool name="config_cecRcProfileTvNone_allowed">true</bool> + <bool name="config_cecRcProfileTvNone_default">true</bool> + <bool name="config_cecRcProfileTvOne_allowed">true</bool> + <bool name="config_cecRcProfileTvOne_default">false</bool> + <bool name="config_cecRcProfileTvTwo_allowed">true</bool> + <bool name="config_cecRcProfileTvTwo_default">false</bool> + <bool name="config_cecRcProfileTvThree_allowed">true</bool> + <bool name="config_cecRcProfileTvThree_default">false</bool> + <bool name="config_cecRcProfileTvFour_allowed">true</bool> + <bool name="config_cecRcProfileTvFour_default">false</bool> + + <bool name="config_cecRcProfileSourceRootMenu_userConfigurable">true</bool> + <bool name="config_cecRcProfileSourceRootMenuHandled_allowed">true</bool> + <bool name="config_cecRcProfileSourceRootMenuHandled_default">true</bool> + <bool name="config_cecRcProfileSourceRootMenuNotHandled_allowed">true</bool> + <bool name="config_cecRcProfileSourceRootMenuNotHandled_default">false</bool> + + <bool name="config_cecRcProfileSourceSetupMenu_userConfigurable">true</bool> + <bool name="config_cecRcProfileSourceSetupMenuHandled_allowed">true</bool> + <bool name="config_cecRcProfileSourceSetupMenuHandled_default">true</bool> + <bool name="config_cecRcProfileSourceSetupMenuNotHandled_allowed">true</bool> + <bool name="config_cecRcProfileSourceSetupMenuNotHandled_default">false</bool> + + <bool name="config_cecRcProfileSourceContentsMenu_userConfigurable">true</bool> + <bool name="config_cecRcProfileSourceContentsMenuHandled_allowed">true</bool> + <bool name="config_cecRcProfileSourceContentsMenuHandled_default">false</bool> + <bool name="config_cecRcProfileSourceContentsMenuNotHandled_allowed">true</bool> + <bool name="config_cecRcProfileSourceContentsMenuNotHandled_default">true</bool> + + <bool name="config_cecRcProfileSourceTopMenu_userConfigurable">true</bool> + <bool name="config_cecRcProfileSourceTopMenuHandled_allowed">true</bool> + <bool name="config_cecRcProfileSourceTopMenuHandled_default">false</bool> + <bool name="config_cecRcProfileSourceTopMenuNotHandled_allowed">true</bool> + <bool name="config_cecRcProfileSourceTopMenuNotHandled_default">true</bool> + + <bool name="config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable">true</bool> + <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed">true</bool> + <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default">false</bool> + <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed">true</bool> + <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default">true</bool> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e0a728c233b2..40c80dbeeb73 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3087,6 +3087,7 @@ <public name="passwordsActivity"/> <public name="selectableAsDefault"/> <public name="isAccessibilityTool"/> + <public name="attributionTags"/> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4af561b947cd..387c065175be 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3691,6 +3691,8 @@ <string name="ext_media_checking_notification_title">Checking <xliff:g id="name" example="SD card">%s</xliff:g>\u2026</string> <!-- Notification body when external media is being checked [CHAR LIMIT=NONE] --> <string name="ext_media_checking_notification_message">Reviewing current content</string> + <!-- TV specific notification body when external media is being checked [CHAR LIMIT=75] --> + <string name="ext_media_checking_notification_message" product="tv">Analyzing media storage</string> <!-- Notification body when new external media is detected [CHAR LIMIT=30] --> <string name="ext_media_new_notification_title">New <xliff:g id="name" example="SD card">%s</xliff:g></string> @@ -3698,11 +3700,15 @@ <string name="ext_media_new_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string> <!-- Notification body when new external media is detected [CHAR LIMIT=NONE] --> <string name="ext_media_new_notification_message">Tap to set up</string> + <!-- TV specific notification body when new external media is detected [CHAR LIMIT=75] --> + <string name="ext_media_new_notification_message" product="tv">Select to set up</string> <!-- Automotive specific notification body when new external media is detected. [CHAR LIMIT=NONE] --> <string name="ext_media_new_notification_message" product="automotive">You may need to reformat the device. Tap to eject.</string> <!-- Notification body when external media is ready for use [CHAR LIMIT=NONE] --> <string name="ext_media_ready_notification_message">For transferring photos and media</string> + <!-- TV specific notification body when external media is ready for use [CHAR LIMIT=75] --> + <string name="ext_media_ready_notification_message" product="tv">Browse media files</string> <!-- Notification title when external media is unmountable (corrupt) [CHAR LIMIT=30] --> <string name="ext_media_unmountable_notification_title">Issue with <xliff:g id="name" example="SD card">%s</xliff:g></string> @@ -3721,8 +3727,8 @@ <string name="ext_media_unsupported_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string> <!-- Notification body when external media is unsupported [CHAR LIMIT=NONE] --> <string name="ext_media_unsupported_notification_message">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Tap to set up in a supported format.</string> - <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=NONE] --> - <string name="ext_media_unsupported_notification_message" product="tv">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Select to set up in a supported format.</string> + <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=75] --> + <string name="ext_media_unsupported_notification_message" product="tv">Select to set up <xliff:g id="name" example="SD card">%s</xliff:g> in a supported format.</string> <!-- Automotive specific notification body when external media is unsupported [CHAR LIMIT=NONE] --> <string name="ext_media_unsupported_notification_message" product="automotive">You may need to reformat the device</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f19405260fa8..543dc1564851 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4224,4 +4224,97 @@ <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" /> <java-symbol type="bool" name="config_enableOneHandedKeyguard" /> + + <!-- CEC Configuration --> + <java-symbol type="bool" name="config_cecHdmiCecEnabled_userConfigurable" /> + <java-symbol type="bool" name="config_cecHdmiCecControlEnabled_allowed" /> + <java-symbol type="bool" name="config_cecHdmiCecControlEnabled_default" /> + <java-symbol type="bool" name="config_cecHdmiCecControlDisabled_allowed" /> + <java-symbol type="bool" name="config_cecHdmiCecControlDisabled_default" /> + + <java-symbol type="bool" name="config_cecHdmiCecVersion_userConfigurable" /> + <java-symbol type="bool" name="config_cecHdmiCecVersion14b_allowed" /> + <java-symbol type="bool" name="config_cecHdmiCecVersion14b_default" /> + <java-symbol type="bool" name="config_cecHdmiCecVersion20_allowed" /> + <java-symbol type="bool" name="config_cecHdmiCecVersion20_default" /> + + <java-symbol type="bool" name="config_cecSendStandbyOnSleep_userConfigurable" /> + <java-symbol type="bool" name="config_cecPowerControlModeTv_allowed" /> + <java-symbol type="bool" name="config_cecPowerControlModeTv_default" /> + <java-symbol type="bool" name="config_cecPowerControlModeBroadcast_allowed" /> + <java-symbol type="bool" name="config_cecPowerControlModeBroadcast_default" /> + <java-symbol type="bool" name="config_cecPowerControlModeNone_allowed" /> + <java-symbol type="bool" name="config_cecPowerControlModeNone_default" /> + + <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLost_userConfigurable" /> + <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostNone_allowed" /> + <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostNone_default" /> + <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed" /> + <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default" /> + + <java-symbol type="bool" name="config_cecSystemAudioModeMuting_userConfigurable" /> + <java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_allowed" /> + <java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_default" /> + <java-symbol type="bool" name="config_cecSystemAudioModeMutingDisabled_allowed" /> + <java-symbol type="bool" name="config_cecSystemAudioModeMutingDisabled_default" /> + + <java-symbol type="bool" name="config_cecVolumeControlMode_userConfigurable" /> + <java-symbol type="bool" name="config_cecVolumeControlModeEnabled_allowed" /> + <java-symbol type="bool" name="config_cecVolumeControlModeEnabled_default" /> + <java-symbol type="bool" name="config_cecVolumeControlModeDisabled_allowed" /> + <java-symbol type="bool" name="config_cecVolumeControlModeDisabled_default" /> + + <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlay_userConfigurable" /> + <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayEnabled_allowed" /> + <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayEnabled_default" /> + <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayDisabled_allowed" /> + <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayDisabled_default" /> + + <java-symbol type="bool" name="config_cecTvSendStandbyOnSleep_userConfigurable" /> + <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepEnabled_allowed" /> + <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepEnabled_default" /> + <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_allowed" /> + <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_default" /> + + <java-symbol type="bool" name="config_cecRcProfileTv_userConfigurable" /> + <java-symbol type="bool" name="config_cecRcProfileTvNone_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileTvNone_default" /> + <java-symbol type="bool" name="config_cecRcProfileTvOne_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileTvOne_default" /> + <java-symbol type="bool" name="config_cecRcProfileTvTwo_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileTvTwo_default" /> + <java-symbol type="bool" name="config_cecRcProfileTvThree_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileTvThree_default" /> + <java-symbol type="bool" name="config_cecRcProfileTvFour_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileTvFour_default" /> + + <java-symbol type="bool" name="config_cecRcProfileSourceRootMenu_userConfigurable" /> + <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuHandled_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuHandled_default" /> + <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuNotHandled_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuNotHandled_default" /> + + <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenu_userConfigurable" /> + <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuHandled_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuHandled_default" /> + <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuNotHandled_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuNotHandled_default" /> + + <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenu_userConfigurable" /> + <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuHandled_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuHandled_default" /> + <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuNotHandled_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuNotHandled_default" /> + + <java-symbol type="bool" name="config_cecRcProfileSourceTopMenu_userConfigurable" /> + <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuHandled_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuHandled_default" /> + <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuNotHandled_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuNotHandled_default" /> + + <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable" /> + <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default" /> + <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed" /> + <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default" /> </resources> diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java index f6e02bc1f48a..28f9ccc10135 100644 --- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java +++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java @@ -41,7 +41,9 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import android.content.res.TypedArray; import android.text.Selection; @@ -356,4 +358,71 @@ public class SuggestionsPopupWindowTest { .perform(clearText()); } } + + @Test + public void testCursorVisibility() { + final TextView textView = getActivity().findViewById(R.id.textview); + final String text = "abc"; + + assertTrue(textView.isCursorVisible()); + + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(replaceText(text)); + final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(), + new String[]{"ABC"}, SuggestionSpan.FLAG_AUTO_CORRECTION); + setSuggestionSpan(suggestionSpan, text.indexOf('a'), text.indexOf('c') + 1); + showSuggestionsPopup(); + + assertSuggestionsPopupIsDisplayed(); + assertSuggestionsPopupContainsItem("ABC"); + assertFalse(textView.isCursorVisible()); + + // Delete an item. + clickSuggestionsPopupItem( + getActivity().getString(com.android.internal.R.string.delete)); + assertSuggestionsPopupIsNotDisplayed(); + assertTrue(textView.isCursorVisible()); + } + + @Test + public void testCursorVisibilityWhenImeConsumesInput() { + final TextView textView = getActivity().findViewById(R.id.textview); + final String text = "abc"; + + assertTrue(textView.isCursorVisible()); + + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(replaceText(text)); + setImeConsumesInputWithExpect(textView, true /* imeConsumesInput */, + false /* expectedCursorVisibility */); + final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(), + new String[]{"ABC"}, SuggestionSpan.FLAG_AUTO_CORRECTION); + setSuggestionSpan(suggestionSpan, text.indexOf('a'), text.indexOf('c') + 1); + showSuggestionsPopup(); + + assertSuggestionsPopupIsDisplayed(); + assertSuggestionsPopupContainsItem("ABC"); + assertFalse(textView.isCursorVisible()); + + // Delete an item. + clickSuggestionsPopupItem( + getActivity().getString(com.android.internal.R.string.delete)); + assertSuggestionsPopupIsNotDisplayed(); + assertFalse(textView.isCursorVisible()); + + // Set IME not consumes input, cursor should be back to visible. + setImeConsumesInputWithExpect(textView, false /* imeConsumesInput */, + true /* expectedCursorVisibility */); + } + + private void setImeConsumesInputWithExpect( + final TextView textView, boolean imeConsumesInput, boolean expectedCursorVisibility) { + textView.post(() -> textView.setImeConsumesInput(imeConsumesInput)); + getInstrumentation().waitForIdleSync(); + if (expectedCursorVisibility) { + assertTrue(textView.isCursorVisible()); + } else { + assertFalse(textView.isCursorVisible()); + } + } } diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java index 678f21fe1211..a036db224a54 100644 --- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java +++ b/core/tests/mockingcoretests/src/android/view/DisplayTests.java @@ -73,6 +73,11 @@ public class DisplayTests { private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT); private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH); + // Bounds of the device. + private static Rect sDeviceBoundsPortrait = new Rect(0, 0, LOGICAL_WIDTH, LOGICAL_HEIGHT); + private static Rect sDeviceBoundsLandscape = new Rect(0, 0, LOGICAL_HEIGHT, LOGICAL_WIDTH); + + private StaticMockitoSession mMockitoSession; private DisplayManagerGlobal mDisplayManagerGlobal; @@ -278,29 +283,57 @@ public class DisplayTests { } @Test - public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() { + public void testGetRealSize_resourcesPortraitSandboxed_matchesAppSandboxBounds() { // GIVEN display is not rotated. setDisplayInfoPortrait(mDisplayInfo); // GIVEN app is letterboxed. - setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(), - sAppBoundsPortrait); + setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsPortrait); final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, mApplicationContext.getResources()); // THEN real size matches app bounds. - verifyRealSizeMatchesApp(display, sAppBoundsPortrait); + verifyRealSizeMatchesBounds(display, sAppBoundsPortrait); } @Test - public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() { + public void testGetRealSize_resourcesPortraitSandboxed_matchesDisplayAreaSandboxBounds() { + // GIVEN display is not rotated. + setDisplayInfoPortrait(mDisplayInfo); + // GIVEN max bounds reflect DisplayArea size, which is the same size as the display. + setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsPortrait); + // GIVEN app bounds do not stretch to include the full DisplayArea. + mApplicationContext.getResources().getConfiguration().windowConfiguration + .setAppBounds(buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT - 10)); + final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, + mApplicationContext.getResources()); + // THEN real metrics matches max bounds for the DisplayArea. + verifyRealSizeMatchesBounds(display, sDeviceBoundsPortrait); + } + + @Test + public void testGetRealSize_resourcesLandscapeSandboxed_matchesAppSandboxBounds() { // GIVEN display is rotated. setDisplayInfoLandscape(mDisplayInfo); // GIVEN app is letterboxed. - setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(), - sAppBoundsLandscape); + setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsLandscape); final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, mApplicationContext.getResources()); // THEN real size matches app bounds. - verifyRealSizeMatchesApp(display, sAppBoundsLandscape); + verifyRealSizeMatchesBounds(display, sAppBoundsLandscape); + } + + @Test + public void testGetRealSize_resourcesLandscapeSandboxed_matchesDisplayAreaSandboxBounds() { + // GIVEN display is rotated. + setDisplayInfoLandscape(mDisplayInfo); + // GIVEN max bounds reflect DisplayArea size, which is the same size as the display. + setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsLandscape); + // GIVEN app bounds do not stretch to include the full DisplayArea. + mApplicationContext.getResources().getConfiguration().windowConfiguration + .setAppBounds(buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH - 10)); + final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, + mApplicationContext.getResources()); + // THEN real metrics matches max bounds for the DisplayArea. + verifyRealSizeMatchesBounds(display, sDeviceBoundsLandscape); } @Test @@ -396,29 +429,57 @@ public class DisplayTests { } @Test - public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() { + public void testGetRealMetrics_resourcesPortraitSandboxed_matchesAppSandboxBounds() { // GIVEN display is not rotated. setDisplayInfoPortrait(mDisplayInfo); // GIVEN app is letterboxed. - setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(), - sAppBoundsPortrait); + setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsPortrait); final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, mApplicationContext.getResources()); // THEN real metrics matches app bounds. - verifyRealMetricsMatchesApp(display, sAppBoundsPortrait); + verifyRealMetricsMatchesBounds(display, sAppBoundsPortrait); + } + + @Test + public void testGetRealMetrics_resourcesPortraitSandboxed_matchesDisplayAreaSandboxBounds() { + // GIVEN display is not rotated. + setDisplayInfoPortrait(mDisplayInfo); + // GIVEN max bounds reflect DisplayArea size, which is the same size as the display. + setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsPortrait); + // GIVEN app bounds do not stretch to include the full DisplayArea. + mApplicationContext.getResources().getConfiguration().windowConfiguration + .setAppBounds(buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT - 10)); + final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, + mApplicationContext.getResources()); + // THEN real metrics matches max bounds for the DisplayArea. + verifyRealMetricsMatchesBounds(display, sDeviceBoundsPortrait); } @Test - public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() { + public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesAppSandboxBounds() { // GIVEN display is rotated. setDisplayInfoLandscape(mDisplayInfo); // GIVEN app is letterboxed. - setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(), - sAppBoundsLandscape); + setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsLandscape); final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, mApplicationContext.getResources()); // THEN real metrics matches app bounds. - verifyRealMetricsMatchesApp(display, sAppBoundsLandscape); + verifyRealMetricsMatchesBounds(display, sAppBoundsLandscape); + } + + @Test + public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesDisplayAreaSandboxBounds() { + // GIVEN display is rotated. + setDisplayInfoLandscape(mDisplayInfo); + // GIVEN max bounds reflect DisplayArea size, which is the same size as the display. + setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsLandscape); + // GIVEN app bounds do not stretch to include the full DisplayArea. + mApplicationContext.getResources().getConfiguration().windowConfiguration + .setAppBounds(buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH - 10)); + final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo, + mApplicationContext.getResources()); + // THEN real metrics matches max bounds for the DisplayArea. + verifyRealMetricsMatchesBounds(display, sDeviceBoundsLandscape); } // Given rotated display dimensions, calculate the letterboxed app bounds. @@ -450,8 +511,8 @@ public class DisplayTests { * Set max bounds to be sandboxed to the app bounds, indicating the app is in * size compat mode or letterbox. */ - private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) { - resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds); + private static void setMaxBoundsSandboxed(Resources resources, Rect bounds) { + resources.getConfiguration().windowConfiguration.setMaxBounds(bounds); } /** @@ -492,17 +553,17 @@ public class DisplayTests { assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT); } - private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) { + private static void verifyRealSizeMatchesBounds(Display display, Rect bounds) { Point size = new Point(); display.getRealSize(size); - assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height())); + assertThat(size).isEqualTo(new Point(bounds.width(), bounds.height())); } - private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) { + private static void verifyRealMetricsMatchesBounds(Display display, Rect bounds) { DisplayMetrics metrics = new DisplayMetrics(); display.getRealMetrics(metrics); - assertThat(metrics.widthPixels).isEqualTo(appBounds.width()); - assertThat(metrics.heightPixels).isEqualTo(appBounds.height()); + assertThat(metrics.widthPixels).isEqualTo(bounds.width()); + assertThat(metrics.heightPixels).isEqualTo(bounds.height()); } private static FixedRotationAdjustments setOverrideFixedRotationAdjustments( diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index c49fe8563dab..a7b6636a15de 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -488,6 +488,8 @@ applications that come with the platform <permission name="android.permission.MANAGE_UI_TRANSLATION" /> <!-- Permission required for CTS test - ClipboardManagerTest --> <permission name="android.permission.SET_CLIP_SOURCE" /> + <!-- Permission required for CTS test - FontManagerTest --> + <permission name="android.permission.UPDATE_FONTS" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml index c0bc73dcbd47..d2b3cf6a4fe2 100644 --- a/libs/WindowManager/Shell/AndroidManifest.xml +++ b/libs/WindowManager/Shell/AndroidManifest.xml @@ -18,4 +18,5 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.wm.shell"> <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" /> + <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> </manifest> diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 24198659e15d..c2f591b9d7af 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -51,4 +51,10 @@ <!-- maximum animation duration for the icon when entering the starting window --> <integer name="max_starting_window_intro_icon_anim_duration">1000</integer> + + <!-- Animation duration when exit starting window: icon going away --> + <integer name="starting_window_icon_exit_anim_duration">166</integer> + + <!-- Animation duration when exit starting window: reveal app --> + <integer name="starting_window_app_reveal_anim_duration">333</integer> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 75bed3777a9d..3ced8d3ec6e7 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -188,4 +188,13 @@ <!-- The height of the brand image on staring surface. --> <dimen name="starting_surface_brand_image_height">80dp</dimen> + + <!-- The length of the shift of main window when exit starting window. --> + <dimen name="starting_surface_exit_animation_window_shift_length">20dp</dimen> + + <!-- The distance of the shift icon when normal exit starting window. --> + <dimen name="starting_surface_normal_exit_icon_distance">120dp</dimen> + + <!-- The distance of the shift icon when early exit starting window. --> + <dimen name="starting_surface_early_exit_icon_distance">32dp</dimen> </resources> 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 cb04bd7ce02b..fcb53cd890b7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -31,6 +31,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.TaskInfo; import android.content.Context; import android.content.LocusId; +import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.util.ArrayMap; @@ -307,9 +308,10 @@ public class ShellTaskOrganizer extends TaskOrganizer { } @Override - public void removeStartingWindow(int taskId) { + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { if (mStartingSurface != null) { - mStartingSurface.removeStartingWindow(taskId); + mStartingSurface.removeStartingWindow(taskId, leash, frame, playRevealAnimation); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java new file mode 100644 index 000000000000..5bc2afd11fe8 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java @@ -0,0 +1,328 @@ +/* + * 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.startingsurface; + +import static android.view.View.GONE; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.graphics.BlendMode; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.RadialGradient; +import android.graphics.Rect; +import android.graphics.Shader; +import android.util.Slog; +import android.view.SurfaceControl; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; +import android.view.animation.Transformation; +import android.view.animation.TranslateYAnimation; +import android.window.SplashScreenView; + +import com.android.wm.shell.common.TransactionPool; + +/** + * Default animation for exiting the splash screen window. + * @hide + */ +public class SplashScreenExitAnimation implements Animator.AnimatorListener { + private static final boolean DEBUG_EXIT_ANIMATION = false; + private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false; + private static final String TAG = StartingSurfaceDrawer.TAG; + + private static final Interpolator ICON_EXIT_INTERPOLATOR = new PathInterpolator(1f, 0f, 1f, 1f); + private static final Interpolator APP_EXIT_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f); + + private static final int EXTRA_REVEAL_DELAY = 133; + private final Matrix mTmpTransform = new Matrix(); + private final float[] mTmpFloat9 = new float[9]; + private SurfaceControl mFirstWindowSurface; + private final Rect mFirstWindowFrame = new Rect(); + private final SplashScreenView mSplashScreenView; + private final int mMainWindowShiftLength; + private final int mIconShiftLength; + private final int mAppDuration; + private final int mIconDuration; + private final TransactionPool mTransactionPool; + + private ValueAnimator mMainAnimator; + private Animation mShiftUpAnimation; + private AnimationSet mIconAnimationSet; + private Runnable mFinishCallback; + + SplashScreenExitAnimation(SplashScreenView view, SurfaceControl leash, Rect frame, + int appDuration, int iconDuration, int mainWindowShiftLength, int iconShiftLength, + TransactionPool pool, Runnable handleFinish) { + mSplashScreenView = view; + mFirstWindowSurface = leash; + if (frame != null) { + mFirstWindowFrame.set(frame); + } + mAppDuration = appDuration; + mIconDuration = iconDuration; + mMainWindowShiftLength = mainWindowShiftLength; + mIconShiftLength = iconShiftLength; + mFinishCallback = handleFinish; + mTransactionPool = pool; + } + + void prepareAnimations() { + prepareRevealAnimation(); + prepareShiftAnimation(); + } + + void startAnimations() { + if (mIconAnimationSet != null) { + mIconAnimationSet.start(); + } + if (mMainAnimator != null) { + mMainAnimator.start(); + } + if (mShiftUpAnimation != null) { + mShiftUpAnimation.start(); + } + } + + // reveal splash screen, shift up main window + private void prepareRevealAnimation() { + // splash screen + mMainAnimator = ValueAnimator.ofFloat(0f, 1f); + mMainAnimator.setDuration(mAppDuration); + mMainAnimator.setInterpolator(APP_EXIT_INTERPOLATOR); + mMainAnimator.addListener(this); + + final int startDelay = mIconDuration + EXTRA_REVEAL_DELAY; + final float transparentRatio = 0.95f; + final int globalHeight = mSplashScreenView.getHeight(); + final int verticalCircleCenter = 0; + final int finalVerticalLength = globalHeight - verticalCircleCenter; + final int halfWidth = mSplashScreenView.getWidth() / 2; + final int endRadius = (int) (0.5 + (1f / transparentRatio * (int) + Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth))); + final RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation( + mSplashScreenView, mMainAnimator); + radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter); + radialVanishAnimation.setRadius(0/* initRadius */, endRadius); + final int[] colors = {Color.TRANSPARENT, Color.TRANSPARENT, Color.WHITE}; + final float[] stops = {0f, transparentRatio, 1f}; + radialVanishAnimation.setRadialPaintParam(colors, stops); + radialVanishAnimation.setReady(); + mMainAnimator.setStartDelay(startDelay); + + if (mFirstWindowSurface != null) { + // shift up main window + View occludeHoleView = new View(mSplashScreenView.getContext()); + if (DEBUG_EXIT_ANIMATION_BLEND) { + occludeHoleView.setBackgroundColor(Color.BLUE); + } else { + occludeHoleView.setBackgroundColor(mSplashScreenView.getInitBackgroundColor()); + } + final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( + WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength); + mSplashScreenView.addView(occludeHoleView, params); + + mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength); + mShiftUpAnimation.setDuration(mAppDuration); + mShiftUpAnimation.setInterpolator(APP_EXIT_INTERPOLATOR); + mShiftUpAnimation.setStartOffset(startDelay); + + occludeHoleView.setAnimation(mShiftUpAnimation); + } + } + + // shift down icon and branding view + private void prepareShiftAnimation() { + final View iconView = mSplashScreenView.getIconView(); + if (iconView == null) { + return; + } + if (mIconShiftLength > 0) { + mIconAnimationSet = new AnimationSet(true /* shareInterpolator */); + if (DEBUG_EXIT_ANIMATION) { + Slog.v(TAG, "first exit animation, shift length: " + mIconShiftLength); + } + mIconAnimationSet.addAnimation(new TranslateYAnimation(0, mIconShiftLength)); + mIconAnimationSet.addAnimation(new AlphaAnimation(1, 0)); + mIconAnimationSet.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + + } + + @Override + public void onAnimationEnd(Animation animation) { + if (DEBUG_EXIT_ANIMATION) { + Slog.v(TAG, "first exit animation finished"); + } + iconView.post(() -> iconView.setVisibility(GONE)); + } + + @Override + public void onAnimationRepeat(Animation animation) { + // ignore + } + }); + mIconAnimationSet.setDuration(mIconDuration); + mIconAnimationSet.setInterpolator(ICON_EXIT_INTERPOLATOR); + iconView.setAnimation(mIconAnimationSet); + final View brandingView = mSplashScreenView.getBrandingView(); + if (brandingView != null) { + brandingView.setAnimation(mIconAnimationSet); + } + } + } + + private static class RadialVanishAnimation extends View { + private SplashScreenView mView; + private int mInitRadius; + private int mFinishRadius; + private boolean mReady; + + private final Point mCircleCenter = new Point(); + private final Matrix mVanishMatrix = new Matrix(); + private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + RadialVanishAnimation(SplashScreenView target, ValueAnimator animator) { + super(target.getContext()); + mView = target; + animator.addUpdateListener((animation) -> { + if (mVanishPaint.getShader() == null) { + return; + } + final float value = (float) animation.getAnimatedValue(); + final float scale = (mFinishRadius - mInitRadius) * value + mInitRadius; + mVanishMatrix.setScale(scale, scale); + mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y); + mVanishPaint.getShader().setLocalMatrix(mVanishMatrix); + mView.postInvalidate(); + }); + mView.addView(this); + } + + void setRadius(int initRadius, int finishRadius) { + if (DEBUG_EXIT_ANIMATION) { + Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius + + " final " + finishRadius); + } + mInitRadius = initRadius; + mFinishRadius = finishRadius; + } + + void setCircleCenter(int x, int y) { + if (DEBUG_EXIT_ANIMATION) { + Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y); + } + mCircleCenter.set(x, y); + } + + void setRadialPaintParam(int[] colors, float[] stops) { + // setup gradient shader + final RadialGradient rShader = + new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP); + mVanishPaint.setShader(rShader); + if (!DEBUG_EXIT_ANIMATION_BLEND) { + mVanishPaint.setBlendMode(BlendMode.MODULATE); + } + } + + void setReady() { + mReady = true; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (mReady) { + canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint); + } + } + } + + private final class ShiftUpAnimation extends TranslateYAnimation { + ShiftUpAnimation(float fromYDelta, float toYDelta) { + super(fromYDelta, toYDelta); + } + + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + super.applyTransformation(interpolatedTime, t); + + if (mFirstWindowSurface == null) { + return; + } + mTmpTransform.set(t.getMatrix()); + final SurfaceControl.Transaction tx = mTransactionPool.acquire(); + mTmpTransform.postTranslate(mFirstWindowFrame.left, + mFirstWindowFrame.top + mMainWindowShiftLength); + tx.setMatrix(mFirstWindowSurface, mTmpTransform, mTmpFloat9); + // TODO set the vsyncId to ensure the transaction doesn't get applied too early. + // Additionally, do you want to have this synchronized with your view animations? + // If so, you'll need to use SyncRtSurfaceTransactionApplier + tx.apply(); + mTransactionPool.release(tx); + } + } + + private void reset() { + if (DEBUG_EXIT_ANIMATION) { + Slog.v(TAG, "vanish animation finished"); + } + mSplashScreenView.post(() -> { + mSplashScreenView.setVisibility(GONE); + if (mFinishCallback != null) { + mFinishCallback.run(); + mFinishCallback = null; + } + }); + if (mFirstWindowSurface != null) { + final SurfaceControl.Transaction tx = mTransactionPool.acquire(); + tx.setWindowCrop(mFirstWindowSurface, null); + tx.apply(); + mFirstWindowSurface.release(); + mFirstWindowSurface = null; + } + } + + @Override + public void onAnimationStart(Animator animation) { + // ignore + } + + @Override + public void onAnimationEnd(Animator animation) { + reset(); + } + + @Override + public void onAnimationCancel(Animator animation) { + reset(); + } + + @Override + public void onAnimationRepeat(Animator animation) { + // ignore + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index 2973b5080ae6..3f9c2717731a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -31,12 +31,14 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.os.Build; import android.util.Slog; +import android.view.SurfaceControl; import android.window.SplashScreenView; import com.android.internal.R; import com.android.internal.graphics.palette.Palette; import com.android.internal.graphics.palette.Quantizer; import com.android.internal.graphics.palette.VariationalKMeansQuantizer; +import com.android.wm.shell.common.TransactionPool; import java.util.List; @@ -56,15 +58,25 @@ public class SplashscreenContentDrawer { // also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon. private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f); private final Context mContext; - private final int mMaxIconAnimationDuration; + private final int mMaxAnimatableIconDuration; private int mIconSize; private int mBrandingImageWidth; private int mBrandingImageHeight; - - SplashscreenContentDrawer(Context context, int maxIconAnimationDuration) { + private final int mAppRevealDuration; + private final int mIconExitDuration; + private int mMainWindowShiftLength; + private int mIconNormalExitDistance; + private int mIconEarlyExitDistance; + private final TransactionPool mTransactionPool; + + SplashscreenContentDrawer(Context context, int maxAnimatableIconDuration, + int iconExitAnimDuration, int appRevealAnimDuration, TransactionPool pool) { mContext = context; - mMaxIconAnimationDuration = maxIconAnimationDuration; + mMaxAnimatableIconDuration = maxAnimatableIconDuration; + mAppRevealDuration = appRevealAnimDuration; + mIconExitDuration = iconExitAnimDuration; + mTransactionPool = pool; } private void updateDensity() { @@ -74,6 +86,12 @@ public class SplashscreenContentDrawer { com.android.wm.shell.R.dimen.starting_surface_brand_image_width); mBrandingImageHeight = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.starting_surface_brand_image_height); + mMainWindowShiftLength = mContext.getResources().getDimensionPixelSize( + com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length); + mIconNormalExitDistance = mContext.getResources().getDimensionPixelSize( + com.android.wm.shell.R.dimen.starting_surface_normal_exit_icon_distance); + mIconEarlyExitDistance = mContext.getResources().getDimensionPixelSize( + com.android.wm.shell.R.dimen.starting_surface_early_exit_icon_distance); } private int getSystemBGColor() { @@ -119,7 +137,7 @@ public class SplashscreenContentDrawer { if (attrs.mReplaceIcon != null) { iconDrawable = attrs.mReplaceIcon; animationDuration = Math.max(0, - Math.min(attrs.mAnimationDuration, mMaxIconAnimationDuration)); + Math.min(attrs.mAnimationDuration, mMaxAnimatableIconDuration)); } else { iconDrawable = iconRes != 0 ? context.getDrawable(iconRes) : context.getPackageManager().getDefaultActivityIcon(); @@ -439,8 +457,8 @@ public class SplashscreenContentDrawer { } /** - * For ColorDrawable only. - * There will be only one color so don't spend too much resource for it. + * For ColorDrawable only. There will be only one color so don't spend too much resource for + * it. */ private static class SingleColorTester implements ColorTester { private final ColorDrawable mColorDrawable; @@ -472,9 +490,8 @@ public class SplashscreenContentDrawer { } /** - * For any other Drawable except ColorDrawable. - * This will use the Palette API to check the color information and use a quantizer to - * filter out transparent colors when needed. + * For any other Drawable except ColorDrawable. This will use the Palette API to check the + * color information and use a quantizer to filter out transparent colors when needed. */ private static class ComplexDrawableTester implements ColorTester { private static final int MAX_BITMAP_SIZE = 40; @@ -593,4 +610,17 @@ public class SplashscreenContentDrawer { } } } + + /** + * Create and play the default exit animation for splash screen view. + */ + void applyExitAnimation(SplashScreenView view, SurfaceControl leash, + Rect frame, boolean isEarlyExit, Runnable finishCallback) { + final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(view, leash, + frame, mAppRevealDuration, mIconExitDuration, mMainWindowShiftLength, + isEarlyExit ? mIconEarlyExitDistance : mIconNormalExitDistance, mTransactionPool, + finishCallback); + animation.prepareAnimations(); + animation.startAnimations(); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java index a594a9f31dde..f258286f2d17 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java @@ -16,7 +16,9 @@ package com.android.wm.shell.startingsurface; +import android.graphics.Rect; import android.os.IBinder; +import android.view.SurfaceControl; import android.window.StartingWindowInfo; import java.util.function.BiConsumer; @@ -31,7 +33,8 @@ public interface StartingSurface { /** * Called when the content of a task is ready to show, starting window can be removed. */ - void removeStartingWindow(int taskId); + void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation); /** * Called when the Task wants to copy the splash screen. * @param taskId diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index 2d1d65b87718..14fbaacb9613 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -29,14 +29,18 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; import android.hardware.display.DisplayManager; import android.os.IBinder; +import android.os.SystemClock; import android.util.Slog; import android.util.SparseArray; -import android.view.Choreographer; import android.view.Display; +import android.view.SurfaceControl; import android.view.View; -import android.view.Window; import android.view.WindowManager; import android.window.SplashScreenView; import android.window.SplashScreenView.SplashScreenViewParcelable; @@ -46,6 +50,7 @@ import android.window.TaskSnapshot; import com.android.internal.R; import com.android.internal.policy.PhoneWindow; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.TransactionPool; import java.util.function.Consumer; @@ -62,23 +67,23 @@ public class StartingSurfaceDrawer { private final DisplayManager mDisplayManager; private final ShellExecutor mSplashScreenExecutor; private final SplashscreenContentDrawer mSplashscreenContentDrawer; - protected Choreographer mChoreographer; // TODO(b/131727939) remove this when clearing ActivityRecord private static final int REMOVE_WHEN_TIMEOUT = 2000; - public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor) { + public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor, + TransactionPool pool) { mContext = context; mDisplayManager = mContext.getSystemService(DisplayManager.class); mSplashScreenExecutor = splashScreenExecutor; - final int maxIconAnimDuration = context.getResources().getInteger( + final int maxAnimatableIconDuration = context.getResources().getInteger( com.android.wm.shell.R.integer.max_starting_window_intro_icon_anim_duration); - mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, maxIconAnimDuration); - mSplashScreenExecutor.execute(this::initChoreographer); - } - - protected void initChoreographer() { - mChoreographer = Choreographer.getInstance(); + final int iconExitAnimDuration = context.getResources().getInteger( + com.android.wm.shell.R.integer.starting_window_icon_exit_anim_duration); + final int appRevealAnimDuration = context.getResources().getInteger( + com.android.wm.shell.R.integer.starting_window_app_reveal_anim_duration); + mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, + maxAnimatableIconDuration, iconExitAnimDuration, appRevealAnimDuration, pool); } private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>(); @@ -195,6 +200,7 @@ public class StartingSurfaceDrawer { } final PhoneWindow win = new PhoneWindow(context); + win.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); win.setIsStartingWindow(true); CharSequence label = context.getResources().getText(labelRes, null); @@ -211,7 +217,7 @@ public class StartingSurfaceDrawer { // the keyguard is being hidden. This is okay because starting windows never show // secret information. // TODO(b/113840485): Occluded may not only happen on default display - if (displayId == DEFAULT_DISPLAY) { + if (displayId == DEFAULT_DISPLAY && windowInfo.isKeyguardOccluded) { windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; } @@ -247,6 +253,7 @@ public class StartingSurfaceDrawer { // Setting as trusted overlay to let touches pass through. This is safe because this // window is controlled by the system. params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; + params.format = PixelFormat.RGBA_8888; final Resources res = context.getResources(); final boolean supportsScreen = res != null && (res.getCompatibilityInfo() != null @@ -257,98 +264,25 @@ public class StartingSurfaceDrawer { params.setTitle("Splash Screen " + activityInfo.packageName); - // TODO(b/173975965) If the target activity doesn't request FLAG_HARDWARE_ACCELERATED, we - // cannot replace the content view after first view was drawn, sounds like an issue. - new AddSplashScreenViewRunnable(taskInfo.taskId, win, context, appToken, params, iconRes, - splashscreenContentResId[0], enableHardAccelerated).run(); - } - - private class AddSplashScreenViewRunnable implements Runnable { - private final int mTaskId; - private final Window mWin; - private final IBinder mAppToken; - private final WindowManager.LayoutParams mLayoutParams; - private final Context mContext; - private final int mIconRes; - private final int mSplashscreenContentResId; - private final boolean mReplaceSplashScreenView; - private int mSequence; - - AddSplashScreenViewRunnable(int taskId, Window window, Context context, - IBinder appToken, WindowManager.LayoutParams params, int iconRes, - int splashscreenContentResId, boolean replaceSplashScreenView) { - mTaskId = taskId; - mWin = window; - mAppToken = appToken; - mContext = context; - mLayoutParams = params; - mIconRes = iconRes; - mSplashscreenContentResId = splashscreenContentResId; - mReplaceSplashScreenView = replaceSplashScreenView; - } - - private void createInitialView() { - View tempView = new View(mContext); - mWin.setContentView(tempView); - mSequence++; - final View view = mWin.getDecorView(); - final WindowManager wm = mContext.getSystemService(WindowManager.class); - if (postAddWindow(mTaskId, mAppToken, view, wm, mLayoutParams)) { - mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, this, null); - } - } - - private SplashScreenView replaceRealView() { - final SplashScreenView sView = - mSplashscreenContentDrawer.makeSplashScreenContentView(mContext, - mIconRes, mSplashscreenContentResId); - mWin.setContentView(sView); - sView.cacheRootWindow(mWin); - return sView; - } - - private SplashScreenView initiateOnce() { - final SplashScreenView sView = - mSplashscreenContentDrawer.makeSplashScreenContentView(mContext, mIconRes, - mSplashscreenContentResId); - final View view = mWin.getDecorView(); + // TODO(b/173975965) tracking performance + final int taskId = taskInfo.taskId; + SplashScreenView sView = null; + try { + sView = mSplashscreenContentDrawer.makeSplashScreenContentView(context, iconRes, + splashscreenContentResId[0]); + final View view = win.getDecorView(); final WindowManager wm = mContext.getSystemService(WindowManager.class); - if (postAddWindow(mTaskId, mAppToken, view, wm, mLayoutParams)) { - mWin.setContentView(sView); - sView.cacheRootWindow(mWin); - } - return sView; - } - - @Override - public void run() { - SplashScreenView view = null; - boolean setRecord = false; - try { - if (mReplaceSplashScreenView) { - // Tricky way to make animation start faster... create the real content after - // first window drawn. The first empty window won't been see because wm will - // still need to wait for transition ready. - if (mSequence == 0) { - createInitialView(); - } else if (mSequence == 1) { - setRecord = true; - view = replaceRealView(); - } - } else { - setRecord = true; - view = initiateOnce(); - } - } catch (RuntimeException e) { - // don't crash if something else bad happens, for example a - // failure loading resources because we are loading from an app - // on external storage that has been unmounted. - Slog.w(TAG, " failed creating starting window", e); - } finally { - if (setRecord) { - setSplashScreenRecord(mTaskId, view); - } + if (postAddWindow(taskId, appToken, view, wm, params)) { + win.setContentView(sView); + sView.cacheRootWindow(win); } + } catch (RuntimeException e) { + // don't crash if something else bad happens, for example a + // failure loading resources because we are loading from an app + // on external storage that has been unmounted. + Slog.w(TAG, " failed creating starting window", e); + } finally { + setSplashScreenRecord(taskId, sView); } } @@ -359,8 +293,10 @@ public class StartingSurfaceDrawer { TaskSnapshot snapshot) { final int taskId = startingWindowInfo.taskInfo.taskId; final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken, - snapshot, mSplashScreenExecutor, () -> removeWindowSynced(taskId)); - mSplashScreenExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT); + snapshot, mSplashScreenExecutor, + () -> removeWindowNoAnimate(taskId)); + mSplashScreenExecutor.executeDelayed(() -> removeWindowNoAnimate(taskId), + REMOVE_WHEN_TIMEOUT); final StartingWindowRecord tView = new StartingWindowRecord(null/* decorView */, surface); mStartingWindowRecords.put(taskId, tView); @@ -369,11 +305,12 @@ public class StartingSurfaceDrawer { /** * Called when the content of a task is ready to show, starting window can be removed. */ - public void removeStartingWindow(int taskId) { + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId); } - removeWindowSynced(taskId); + removeWindowSynced(taskId, leash, frame, playRevealAnimation); } /** @@ -383,13 +320,6 @@ public class StartingSurfaceDrawer { public void copySplashScreenView(int taskId) { final StartingWindowRecord preView = mStartingWindowRecords.get(taskId); SplashScreenViewParcelable parcelable; - if (preView != null) { - if (preView.isWaitForContent()) { - mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, - () -> copySplashScreenView(taskId), null); - return; - } - } if (preView != null && preView.mContentView != null && preView.mContentView.isCopyable()) { parcelable = new SplashScreenViewParcelable(preView.mContentView); @@ -413,12 +343,6 @@ public class StartingSurfaceDrawer { Slog.w(TAG, appToken + " already running, starting window not displayed. " + e.getMessage()); shouldSaveView = false; - } catch (RuntimeException e) { - // don't crash if something else bad happens, for example a - // failure loading resources because we are loading from an app - // on external storage that has been unmounted. - Slog.w(TAG, appToken + " failed creating starting window", e); - shouldSaveView = false; } finally { if (view != null && view.getParent() == null) { Slog.w(TAG, "view not successfully added to wm, removing view"); @@ -427,9 +351,9 @@ public class StartingSurfaceDrawer { } } if (shouldSaveView) { - removeWindowSynced(taskId); - mSplashScreenExecutor.executeDelayed(() -> removeWindowSynced(taskId), - REMOVE_WHEN_TIMEOUT); + removeWindowNoAnimate(taskId); + mSplashScreenExecutor.executeDelayed( + () -> removeWindowNoAnimate(taskId), REMOVE_WHEN_TIMEOUT); saveSplashScreenRecord(taskId, view); } return shouldSaveView; @@ -449,24 +373,30 @@ public class StartingSurfaceDrawer { } } - protected void removeWindowSynced(int taskId) { + private void removeWindowNoAnimate(int taskId) { + removeWindowSynced(taskId, null, null, false); + } + + protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { final StartingWindowRecord record = mStartingWindowRecords.get(taskId); if (record != null) { - if (record.isWaitForContent()) { - if (DEBUG_SPLASH_SCREEN) { - Slog.v(TAG, "splash screen window haven't been draw yet"); - } - mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, - () -> removeWindowSynced(taskId), null); - return; - } if (record.mDecorView != null) { if (DEBUG_SPLASH_SCREEN) { Slog.v(TAG, "Removing splash screen window for task: " + taskId); } - final WindowManager wm = record.mDecorView.getContext() - .getSystemService(WindowManager.class); - wm.removeView(record.mDecorView); + if (record.mContentView != null) { + final HandleExitFinish exitFinish = new HandleExitFinish(record.mDecorView); + if (leash != null || playRevealAnimation) { + mSplashscreenContentDrawer.applyExitAnimation(record.mContentView, + leash, frame, record.isEarlyExit(), exitFinish); + mSplashScreenExecutor.executeDelayed(exitFinish, REMOVE_WHEN_TIMEOUT); + } else { + // the SplashScreenView has been copied to client, skip default exit + // animation + exitFinish.run(); + } + } } if (record.mTaskSnapshotWindow != null) { if (DEBUG_TASK_SNAPSHOT) { @@ -478,6 +408,26 @@ public class StartingSurfaceDrawer { } } + private static class HandleExitFinish implements Runnable { + private View mDecorView; + + HandleExitFinish(View decorView) { + mDecorView = decorView; + } + + @Override + public void run() { + if (mDecorView == null) { + return; + } + final WindowManager wm = mDecorView.getContext().getSystemService(WindowManager.class); + if (wm != null) { + wm.removeView(mDecorView); + } + mDecorView = null; + } + } + private void getWindowResFromContext(Context ctx, Consumer<TypedArray> consumer) { final TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window); consumer.accept(a); @@ -488,10 +438,12 @@ public class StartingSurfaceDrawer { * Record the view or surface for a starting window. */ private static class StartingWindowRecord { + private static final long EARLY_START_MINIMUM_TIME_MS = 250; private final View mDecorView; private final TaskSnapshotWindow mTaskSnapshotWindow; private SplashScreenView mContentView; private boolean mSetSplashScreen; + private long mContentCreateTime; StartingWindowRecord(View decorView, TaskSnapshotWindow taskSnapshotWindow) { mDecorView = decorView; @@ -503,11 +455,12 @@ public class StartingSurfaceDrawer { return; } mContentView = splashScreenView; + mContentCreateTime = SystemClock.uptimeMillis(); mSetSplashScreen = true; } - private boolean isWaitForContent() { - return mDecorView != null && !mSetSplashScreen; + boolean isEarlyExit() { + return SystemClock.uptimeMillis() - mContentCreateTime < EARLY_START_MINIMUM_TIME_MS; } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index 5eb7071fbd63..a694e525a761 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -28,14 +28,17 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.Context; +import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import android.view.SurfaceControl; import android.window.StartingWindowInfo; import android.window.TaskOrganizer; import android.window.TaskSnapshot; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.TransactionPool; import java.util.function.BiConsumer; @@ -67,8 +70,16 @@ public class StartingWindowController { private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl(); private final ShellExecutor mSplashScreenExecutor; + // For Car Launcher public StartingWindowController(Context context, ShellExecutor splashScreenExecutor) { - mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor); + mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, + new TransactionPool()); + mSplashScreenExecutor = splashScreenExecutor; + } + + public StartingWindowController(Context context, ShellExecutor splashScreenExecutor, + TransactionPool pool) { + mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, pool); mSplashScreenExecutor = splashScreenExecutor; } @@ -112,7 +123,8 @@ public class StartingWindowController { + " allowTaskSnapshot " + allowTaskSnapshot + " activityCreated " + activityCreated); } - if (newTask || !processRunning || (taskSwitch && !activityCreated)) { + if ((newTask || !processRunning || (taskSwitch && !activityCreated)) + && windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) { return STARTING_WINDOW_TYPE_SPLASH_SCREEN; } if (taskSwitch && allowTaskSnapshot) { @@ -198,8 +210,9 @@ public class StartingWindowController { /** * Called when the content of a task is ready to show, starting window can be removed. */ - void removeStartingWindow(int taskId) { - mStartingSurfaceDrawer.removeStartingWindow(taskId); + void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { + mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation); } private class StartingSurfaceImpl implements StartingSurface { @@ -211,9 +224,11 @@ public class StartingWindowController { } @Override - public void removeStartingWindow(int taskId) { + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { mSplashScreenExecutor.execute(() -> - StartingWindowController.this.removeStartingWindow(taskId)); + StartingWindowController.this.removeStartingWindow(taskId, leash, frame, + playRevealAnimation)); } @Override diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml index f06d57c6c789..ad4ccc0288ad 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml @@ -11,6 +11,8 @@ <option name="force-skip-system-props" value="true" /> <!-- set WM tracing verbose level to all --> <option name="run-command" value="cmd window tracing level all" /> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame" /> <!-- restart launcher to activate TAPL --> <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" /> </target_preparer> diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java index a531ef58725d..207db9e80511 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package unittest.src.com.android.wm.shell.startingsurface; +package com.android.wm.shell.startingsurface; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -33,11 +33,12 @@ import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.testing.TestableContext; -import android.view.Choreographer; +import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; import android.view.WindowMetrics; @@ -49,7 +50,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.common.HandlerExecutor; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.startingsurface.StartingSurfaceDrawer; +import com.android.wm.shell.common.TransactionPool; import org.junit.Before; import org.junit.Test; @@ -68,7 +69,7 @@ public class StartingSurfaceDrawerTests { @Mock private WindowManager mMockWindowManager; @Mock - private static Choreographer sFakeChoreographer; + private TransactionPool mTransactionPool; TestStartingSurfaceDrawer mStartingSurfaceDrawer; @@ -76,13 +77,9 @@ public class StartingSurfaceDrawerTests { int mAddWindowForTask = 0; int mViewThemeResId; - TestStartingSurfaceDrawer(Context context, ShellExecutor executor) { - super(context, executor); - } - - @Override - protected void initChoreographer() { - mChoreographer = sFakeChoreographer; + TestStartingSurfaceDrawer(Context context, ShellExecutor animExecutor, + TransactionPool pool) { + super(context, animExecutor, pool); } @Override @@ -95,7 +92,8 @@ public class StartingSurfaceDrawerTests { } @Override - protected void removeWindowSynced(int taskId) { + protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { // listen for removeView if (mAddWindowForTask == taskId) { mAddWindowForTask = 0; @@ -123,7 +121,8 @@ public class StartingSurfaceDrawerTests { doNothing().when(mMockWindowManager).addView(any(), any()); mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context, - new HandlerExecutor(new Handler(Looper.getMainLooper())))); + new HandlerExecutor(new Handler(Looper.getMainLooper())), + mTransactionPool)); } @Test @@ -137,9 +136,9 @@ public class StartingSurfaceDrawerTests { verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any()); assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId); - mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId); + mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId, null, null, false); waitHandlerIdle(mainLoop); - verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId)); + verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId), any(), any(), eq(false)); assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java index 27e5f51d88b8..b908df20d3c0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package unittest.src.com.android.wm.shell.startingsurface; +package com.android.wm.shell.startingsurface; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -48,7 +48,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.wm.shell.TestShellExecutor; -import com.android.wm.shell.startingsurface.TaskSnapshotWindow; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt index a8f1a4d2a7f8..243e4ca4295a 100644 --- a/packages/Connectivity/framework/api/current.txt +++ b/packages/Connectivity/framework/api/current.txt @@ -143,6 +143,7 @@ package android.net { public static class ConnectivityManager.NetworkCallback { ctor public ConnectivityManager.NetworkCallback(); + ctor public ConnectivityManager.NetworkCallback(int); method public void onAvailable(@NonNull android.net.Network); method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean); method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities); @@ -150,6 +151,7 @@ package android.net { method public void onLosing(@NonNull android.net.Network, int); method public void onLost(@NonNull android.net.Network); method public void onUnavailable(); + field public static final int FLAG_INCLUDE_LOCATION_INFO = 1; // 0x1 } public static interface ConnectivityManager.OnNetworkActiveListener { @@ -293,6 +295,7 @@ package android.net { method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); method public int getOwnerUid(); method public int getSignalStrength(); + method @NonNull public java.util.Set<java.lang.Integer> getSubIds(); method @Nullable public android.net.TransportInfo getTransportInfo(); method public boolean hasCapability(int); method public boolean hasTransport(int); @@ -399,6 +402,11 @@ package android.net { method public android.net.NetworkRequest.Builder removeTransportType(int); method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String); method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier); + method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>); + } + + public class ParseException extends java.lang.RuntimeException { + field public String response; } public class ProxyInfo implements android.os.Parcelable { diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt index 5b64d5239cda..4b3336644ef9 100644 --- a/packages/Connectivity/framework/api/module-lib-current.txt +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -7,6 +7,7 @@ package android.net { public class ConnectivityManager { method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot(); + method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange(); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt index a732430e6a9c..a98f14ea9408 100644 --- a/packages/Connectivity/framework/api/system-current.txt +++ b/packages/Connectivity/framework/api/system-current.txt @@ -296,6 +296,7 @@ package android.net { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int); method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int); method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String); + method @NonNull public android.net.NetworkCapabilities.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>); method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo); } diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 3314af530e9b..7189be10d8f4 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -16,7 +16,6 @@ package android.net; import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; -import static android.net.IpSecManager.INVALID_RESOURCE_ID; import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST; import static android.net.NetworkRequest.Type.LISTEN; import static android.net.NetworkRequest.Type.REQUEST; @@ -44,6 +43,7 @@ import android.net.SocketKeepalive.Callback; import android.net.TetheringManager.StartTetheringCallback; import android.net.TetheringManager.TetheringEventCallback; import android.net.TetheringManager.TetheringRequest; +import android.net.wifi.WifiNetworkSuggestion; import android.os.Binder; import android.os.Build; import android.os.Build.VERSION_CODES; @@ -1315,7 +1315,7 @@ public class ConnectivityManager { } /** - * Returns an array of {@link android.net.NetworkCapabilities} objects, representing + * Returns an array of {@link NetworkCapabilities} objects, representing * the Networks that applications run by the given user will use by default. * @hide */ @@ -1395,11 +1395,19 @@ public class ConnectivityManager { } /** - * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This + * Get the {@link NetworkCapabilities} for the given {@link Network}. This * will return {@code null} if the network is unknown. * + * This will remove any location sensitive data in {@link TransportInfo} embedded in + * {@link NetworkCapabilities#getTransportInfo()}. Some transport info instances like + * {@link android.net.wifi.WifiInfo} contain location sensitive information. Retrieving + * this location sensitive information (subject to app's location permissions) will be + * noted by system. To include any location sensitive data in {@link TransportInfo}, + * use a {@link NetworkCallback} with + * {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} flag. + * * @param network The {@link Network} object identifying the network in question. - * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}. + * @return The {@link NetworkCapabilities} for the network, or {@code null}. */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @Nullable @@ -1997,7 +2005,7 @@ public class ConnectivityManager { dup = createInvalidFd(); } return new NattSocketKeepalive(mService, network, dup, - INVALID_RESOURCE_ID /* Unused */, source, destination, executor, callback); + -1 /* Unused */, source, destination, executor, callback); } /** @@ -3245,6 +3253,54 @@ public class ConnectivityManager { */ public static class NetworkCallback { /** + * No flags associated with this callback. + * @hide + */ + public static final int FLAG_NONE = 0; + /** + * Use this flag to include any location sensitive data in {@link NetworkCapabilities} sent + * via {@link #onCapabilitiesChanged(Network, NetworkCapabilities)}. + * <p> + * These include: + * <li> Some transport info instances (retrieved via + * {@link NetworkCapabilities#getTransportInfo()}) like {@link android.net.wifi.WifiInfo} + * contain location sensitive information. + * <li> OwnerUid (retrieved via {@link NetworkCapabilities#getOwnerUid()} is location + * sensitive for wifi suggestor apps (i.e using {@link WifiNetworkSuggestion}).</li> + * </p> + * <p> + * Note: + * <li> Retrieving this location sensitive information (subject to app's location + * permissions) will be noted by system. </li> + * <li> Without this flag any {@link NetworkCapabilities} provided via the callback does + * not include location sensitive info. + * </p> + */ + public static final int FLAG_INCLUDE_LOCATION_INFO = 1 << 0; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = "FLAG_", value = { + FLAG_NONE, + FLAG_INCLUDE_LOCATION_INFO + }) + public @interface Flag { } + + /** + * All the valid flags for error checking. + */ + private static final int VALID_FLAGS = FLAG_INCLUDE_LOCATION_INFO; + + public NetworkCallback() { + this(FLAG_NONE); + } + + public NetworkCallback(@Flag int flags) { + Preconditions.checkArgument((flags & VALID_FLAGS) == flags); + mFlags = flags; + } + + /** * Called when the framework connects to a new network to evaluate whether it satisfies this * request. If evaluation succeeds, this callback may be followed by an {@link #onAvailable} * callback. There is no guarantee that this new network will satisfy any requests, or that @@ -3381,7 +3437,7 @@ public class ConnectivityManager { * calling these methods while in a callback may return an outdated or even a null object. * * @param network The {@link Network} whose capabilities have changed. - * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this + * @param networkCapabilities The new {@link NetworkCapabilities} for this * network. */ public void onCapabilitiesChanged(@NonNull Network network, @@ -3450,6 +3506,7 @@ public class ConnectivityManager { public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {} private NetworkRequest networkRequest; + private final int mFlags; } /** @@ -3639,14 +3696,15 @@ public class ConnectivityManager { } Messenger messenger = new Messenger(handler); Binder binder = new Binder(); + final int callbackFlags = callback.mFlags; if (reqType == LISTEN) { request = mService.listenForNetwork( - need, messenger, binder, callingPackageName, + need, messenger, binder, callbackFlags, callingPackageName, getAttributionTag()); } else { request = mService.requestNetwork( need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType, - callingPackageName, getAttributionTag()); + callbackFlags, callingPackageName, getAttributionTag()); } if (request != null) { sCallbacks.put(request, callback); @@ -3693,7 +3751,7 @@ public class ConnectivityManager { } /** - * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}. + * Request a network to satisfy a set of {@link NetworkCapabilities}. * * <p>This method will attempt to find the best network that matches the passed * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the @@ -3777,7 +3835,7 @@ public class ConnectivityManager { } /** - * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}. + * Request a network to satisfy a set of {@link NetworkCapabilities}. * * This method behaves identically to {@link #requestNetwork(NetworkRequest, NetworkCallback)} * but runs all the callbacks on the passed Handler. @@ -3799,7 +3857,7 @@ public class ConnectivityManager { } /** - * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited + * Request a network to satisfy a set of {@link NetworkCapabilities}, limited * by a timeout. * * This function behaves identically to the non-timed-out version @@ -3834,7 +3892,7 @@ public class ConnectivityManager { } /** - * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited + * Request a network to satisfy a set of {@link NetworkCapabilities}, limited * by a timeout. * * This method behaves identically to @@ -3879,7 +3937,7 @@ public class ConnectivityManager { /** - * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}. + * Request a network to satisfy a set of {@link NetworkCapabilities}. * * This function behaves identically to the version that takes a NetworkCallback, but instead * of {@link NetworkCallback} a {@link PendingIntent} is used. This means @@ -4911,7 +4969,7 @@ public class ConnectivityManager { } /** - * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but + * Request a network to satisfy a set of {@link NetworkCapabilities}, but * does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can * be used to request that the system provide a network without causing the network to be * in the foreground. @@ -5053,4 +5111,21 @@ public class ConnectivityManager { throw e.rethrowFromSystemServer(); } } + + // The first network ID of IPSec tunnel interface. + private static final int TUN_INTF_NETID_START = 0xFC00; + // The network ID range of IPSec tunnel interface. + private static final int TUN_INTF_NETID_RANGE = 0x0400; + + /** + * Get the network ID range reserved for IPSec tunnel interfaces. + * + * @return A Range which indicates the network ID range of IPSec tunnel interface. + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @NonNull + public static Range<Integer> getIpSecNetIdRange() { + return new Range(TUN_INTF_NETID_START, TUN_INTF_NETID_START + TUN_INTF_NETID_RANGE - 1); + } } diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index cd49258d1c47..f9393e315b83 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -143,7 +143,7 @@ interface IConnectivityManager NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType, in Messenger messenger, int timeoutSec, in IBinder binder, int legacy, - String callingPackageName, String callingAttributionTag); + int callbackFlags, String callingPackageName, String callingAttributionTag); NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities, in PendingIntent operation, String callingPackageName, String callingAttributionTag); @@ -151,7 +151,7 @@ interface IConnectivityManager void releasePendingNetworkRequest(in PendingIntent operation); NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities, - in Messenger messenger, in IBinder binder, String callingPackageName, + in Messenger messenger, in IBinder binder, int callbackFlags, String callingPackageName, String callingAttributionTag); void pendingListenForNetwork(in NetworkCapabilities networkCapabilities, diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java index c82cd3b4f357..058f3c999dd7 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.net.ConnectivityManager.NetworkCallback; +import android.net.wifi.WifiNetworkSuggestion; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -131,6 +132,7 @@ public final class NetworkCapabilities implements Parcelable { mPrivateDnsBroken = false; mRequestorUid = Process.INVALID_UID; mRequestorPackageName = null; + mSubIds = new ArraySet<>(); } /** @@ -159,6 +161,7 @@ public final class NetworkCapabilities implements Parcelable { mPrivateDnsBroken = nc.mPrivateDnsBroken; mRequestorUid = nc.mRequestorUid; mRequestorPackageName = nc.mRequestorPackageName; + mSubIds = new ArraySet<>(nc.mSubIds); } /** @@ -1048,6 +1051,16 @@ public final class NetworkCapabilities implements Parcelable { * * Instances of NetworkCapabilities sent to apps without the appropriate permissions will have * this field cleared out. + * + * <p> + * This field will only be populated for VPN and wifi network suggestor apps (i.e using + * {@link WifiNetworkSuggestion}), and only for the network they own. + * In the case of wifi network suggestors apps, this field is also location sensitive, so the + * app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. If the + * app targets SDK version greater than or equal to {@link Build.VERSION_CODES#S}, then they + * also need to use {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their + * callback. The app will be blamed for location access if this field is included. + * </p> */ public int getOwnerUid() { return mOwnerUid; @@ -1655,6 +1668,7 @@ public final class NetworkCapabilities implements Parcelable { combineSSIDs(nc); combineRequestor(nc); combineAdministratorUids(nc); + combineSubIds(nc); } /** @@ -1674,8 +1688,9 @@ public final class NetworkCapabilities implements Parcelable { && satisfiedBySpecifier(nc) && (onlyImmutable || satisfiedBySignalStrength(nc)) && (onlyImmutable || satisfiedByUids(nc)) - && (onlyImmutable || satisfiedBySSID(nc))) - && (onlyImmutable || satisfiedByRequestor(nc)); + && (onlyImmutable || satisfiedBySSID(nc)) + && (onlyImmutable || satisfiedByRequestor(nc)) + && (onlyImmutable || satisfiedBySubIds(nc))); } /** @@ -1771,7 +1786,8 @@ public final class NetworkCapabilities implements Parcelable { && equalsOwnerUid(that) && equalsPrivateDnsBroken(that) && equalsRequestor(that) - && equalsAdministratorUids(that); + && equalsAdministratorUids(that) + && equalsSubIds(that); } @Override @@ -1793,7 +1809,8 @@ public final class NetworkCapabilities implements Parcelable { + Objects.hashCode(mPrivateDnsBroken) * 47 + Objects.hashCode(mRequestorUid) * 53 + Objects.hashCode(mRequestorPackageName) * 59 - + Arrays.hashCode(mAdministratorUids) * 61; + + Arrays.hashCode(mAdministratorUids) * 61 + + Objects.hashCode(mSubIds) * 67; } @Override @@ -1827,6 +1844,7 @@ public final class NetworkCapabilities implements Parcelable { dest.writeInt(mOwnerUid); dest.writeInt(mRequestorUid); dest.writeString(mRequestorPackageName); + dest.writeIntArray(CollectionUtils.toIntArray(mSubIds)); } public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR = @@ -1850,6 +1868,11 @@ public final class NetworkCapabilities implements Parcelable { netCap.mOwnerUid = in.readInt(); netCap.mRequestorUid = in.readInt(); netCap.mRequestorPackageName = in.readString(); + netCap.mSubIds = new ArraySet<>(); + final int[] subIdInts = Objects.requireNonNull(in.createIntArray()); + for (int i = 0; i < subIdInts.length; i++) { + netCap.mSubIds.add(subIdInts[i]); + } return netCap; } @Override @@ -1933,11 +1956,14 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" SSID: ").append(mSSID); } - if (mPrivateDnsBroken) { sb.append(" PrivateDnsBroken"); } + if (!mSubIds.isEmpty()) { + sb.append(" SubscriptionIds: ").append(mSubIds); + } + sb.append("]"); return sb.toString(); } @@ -2251,6 +2277,67 @@ public final class NetworkCapabilities implements Parcelable { } /** + * Set of the subscription IDs that identifies the network or request, empty if none. + */ + @NonNull + private ArraySet<Integer> mSubIds = new ArraySet<>(); + + /** + * Sets the subscription ID set that associated to this network or request. + * + * @hide + */ + @NonNull + public NetworkCapabilities setSubIds(@NonNull Set<Integer> subIds) { + mSubIds = new ArraySet(Objects.requireNonNull(subIds)); + return this; + } + + /** + * Gets the subscription ID set that associated to this network or request. + * @return + */ + @NonNull + public Set<Integer> getSubIds() { + return new ArraySet<>(mSubIds); + } + + /** + * Tests if the subscription ID set of this network is the same as that of the passed one. + */ + private boolean equalsSubIds(@NonNull NetworkCapabilities nc) { + return Objects.equals(mSubIds, nc.mSubIds); + } + + /** + * Check if the subscription ID set requirements of this object are matched by the passed one. + * If specified in the request, the passed one need to have at least one subId and at least + * one of them needs to be in the request set. + */ + private boolean satisfiedBySubIds(@NonNull NetworkCapabilities nc) { + if (mSubIds.isEmpty()) return true; + if (nc.mSubIds.isEmpty()) return false; + for (final Integer subId : nc.mSubIds) { + if (mSubIds.contains(subId)) return true; + } + return false; + } + + /** + * Combine subscription ID set of the capabilities. + * + * <p>This is only legal if the subscription Ids are equal. + * + * <p>If both subscription IDs are not equal, they belong to different subscription + * (or no subscription). In this case, it would not make sense to add them together. + */ + private void combineSubIds(@NonNull NetworkCapabilities nc) { + if (!Objects.equals(mSubIds, nc.mSubIds)) { + throw new IllegalStateException("Can't combine two subscription ID sets"); + } + } + + /** * Builder class for NetworkCapabilities. * * This class is mainly for for {@link NetworkAgent} instances to use. Many fields in @@ -2556,6 +2643,18 @@ public final class NetworkCapabilities implements Parcelable { } /** + * Set the subscription ID set. + * + * @param subIds a set that represent the subscription IDs. Empty if clean up. + * @return this builder. + */ + @NonNull + public Builder setSubIds(@NonNull final Set<Integer> subIds) { + mCaps.setSubIds(subIds); + return this; + } + + /** * Builds the instance of the capabilities. * * @return the built instance of NetworkCapabilities. diff --git a/packages/Connectivity/framework/src/android/net/NetworkInfo.java b/packages/Connectivity/framework/src/android/net/NetworkInfo.java index d752901e2eb0..bb2349459331 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkInfo.java +++ b/packages/Connectivity/framework/src/android/net/NetworkInfo.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; -import android.telephony.Annotation.NetworkType; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; @@ -164,7 +163,7 @@ public class NetworkInfo implements Parcelable { * @param typeName a human-readable string for the network type, or an empty string or null. * @param subtypeName a human-readable string for the subtype, or an empty string or null. */ - public NetworkInfo(int type, @NetworkType int subtype, + public NetworkInfo(int type, int subtype, @Nullable String typeName, @Nullable String subtypeName) { if (!ConnectivityManager.isNetworkTypeValid(type) && type != ConnectivityManager.TYPE_NONE) { diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index aa6975678adc..3fd95ee58df2 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -461,6 +461,21 @@ public class NetworkRequest implements Parcelable { } nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); } + + /** + * Sets the optional subscription ID set. + * <p> + * This specify the subscription IDs requirement. + * A network will satisfy this request only if it matches one of the subIds in this set. + * An empty set matches all networks, including those without a subId. + * + * @param subIds A {@code Set} that represents subscription IDs. + */ + @NonNull + public Builder setSubIds(@NonNull Set<Integer> subIds) { + mNetworkCapabilities.setSubIds(subIds); + return this; + } } // implement the Parcelable interface diff --git a/packages/Connectivity/framework/src/android/net/NetworkState.java b/packages/Connectivity/framework/src/android/net/NetworkState.java index d01026566ca0..9b69674728a8 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkState.java +++ b/packages/Connectivity/framework/src/android/net/NetworkState.java @@ -22,7 +22,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; -import android.util.Slog; +import android.util.Log; /** * Snapshot of network state. @@ -83,7 +83,7 @@ public class NetworkState implements Parcelable { if (VALIDATE_ROAMING_STATE && networkInfo != null && networkCapabilities != null) { if (networkInfo.isRoaming() == networkCapabilities .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) { - Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo + Log.wtf("NetworkState", "Roaming state disagreement between " + networkInfo + " and " + networkCapabilities); } } diff --git a/core/java/android/net/ParseException.java b/packages/Connectivity/framework/src/android/net/ParseException.java index bcfdd7ef09cc..bcfdd7ef09cc 100644 --- a/core/java/android/net/ParseException.java +++ b/packages/Connectivity/framework/src/android/net/ParseException.java diff --git a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java index d37c4691ddde..53d966937a70 100644 --- a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java +++ b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java @@ -92,7 +92,7 @@ public final class QosSocketInfo implements Parcelable { Objects.requireNonNull(socket, "socket cannot be null"); mNetwork = Objects.requireNonNull(network, "network cannot be null"); - mParcelFileDescriptor = ParcelFileDescriptor.dup(socket.getFileDescriptor$()); + mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(socket); mLocalSocketAddress = new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort()); } @@ -114,10 +114,10 @@ public final class QosSocketInfo implements Parcelable { try { return new InetSocketAddress(InetAddress.getByAddress(address), port); } catch (final UnknownHostException e) { - /* The catch block was purposely left empty. UnknownHostException will never be thrown + /* This can never happen. UnknownHostException will never be thrown since the address provided is numeric and non-null. */ + throw new RuntimeException("UnknownHostException on numeric address", e); } - return new InetSocketAddress(); } @Override diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index c1dca5df1b2f..16a946dc7bc6 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -138,9 +138,6 @@ public class DynamicSystemInstallationService extends Service private long mCurrentPartitionSize; private long mCurrentPartitionInstalledSize; - private boolean mJustCancelledByUser; - private boolean mKeepNotification; - // This is for testing only now private boolean mEnableWhenCompleted; @@ -174,11 +171,6 @@ public class DynamicSystemInstallationService extends Service if (cache != null) { cache.flush(); } - - if (!mKeepNotification) { - // Cancel the persistent notification. - mNM.cancel(NOTIFICATION_ID); - } } @Override @@ -231,9 +223,11 @@ public class DynamicSystemInstallationService extends Service return; } + boolean removeNotification = false; switch (result) { case RESULT_CANCELLED: postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null); + removeNotification = true; break; case RESULT_ERROR_IO: @@ -251,7 +245,7 @@ public class DynamicSystemInstallationService extends Service } // if it's not successful, reset the task and stop self. - resetTaskAndStop(); + resetTaskAndStop(removeNotification); } private void executeInstallCommand(Intent intent) { @@ -302,12 +296,12 @@ public class DynamicSystemInstallationService extends Service return; } - stopForeground(true); - mJustCancelledByUser = true; - if (mInstallTask.cancel(false)) { - // Will stopSelf() in onResult() + // onResult() would call resetTaskAndStop() upon task completion. Log.d(TAG, "Cancel request filed successfully"); + // Dismiss the notification as soon as possible as DynamicSystemManager.remove() may + // block. + stopForeground(STOP_FOREGROUND_REMOVE); } else { Log.e(TAG, "Trying to cancel installation while it's already completed."); } @@ -322,8 +316,7 @@ 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(); + resetTaskAndStop(true); return; } @@ -331,8 +324,8 @@ public class DynamicSystemInstallationService extends Service getString(R.string.toast_dynsystem_discarded), Toast.LENGTH_LONG).show(); - resetTaskAndStop(); postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null); + resetTaskAndStop(true); mDynSystem.remove(); } @@ -412,12 +405,13 @@ public class DynamicSystemInstallationService extends Service } private void resetTaskAndStop() { - mInstallTask = null; + resetTaskAndStop(/* removeNotification= */ false); + } - new Handler().postDelayed(() -> { - stopForeground(STOP_FOREGROUND_DETACH); - stopSelf(); - }, 50); + private void resetTaskAndStop(boolean removeNotification) { + mInstallTask = null; + stopForeground(removeNotification ? STOP_FOREGROUND_REMOVE : STOP_FOREGROUND_DETACH); + stopSelf(); } private void prepareNotification() { @@ -525,7 +519,7 @@ public class DynamicSystemInstallationService extends Service private void postStatus(int status, int cause, Throwable detail) { String statusString; String causeString; - mKeepNotification = false; + boolean notifyOnNotificationBar = true; switch (status) { case STATUS_NOT_STARTED: @@ -551,18 +545,16 @@ public class DynamicSystemInstallationService extends Service break; case CAUSE_INSTALL_CANCELLED: causeString = "INSTALL_CANCELLED"; + notifyOnNotificationBar = false; break; case CAUSE_ERROR_IO: causeString = "ERROR_IO"; - mKeepNotification = true; break; case CAUSE_ERROR_INVALID_URL: causeString = "ERROR_INVALID_URL"; - mKeepNotification = true; break; case CAUSE_ERROR_EXCEPTION: causeString = "ERROR_EXCEPTION"; - mKeepNotification = true; break; default: causeString = "CAUSE_NOT_SPECIFIED"; @@ -571,16 +563,6 @@ public class DynamicSystemInstallationService extends Service Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail); - boolean notifyOnNotificationBar = true; - - if (status == STATUS_NOT_STARTED - && cause == CAUSE_INSTALL_CANCELLED - && mJustCancelledByUser) { - // if task is cancelled by user, do not notify them - notifyOnNotificationBar = false; - mJustCancelledByUser = false; - } - if (notifyOnNotificationBar) { mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail)); } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index a28a1e32a2a5..b4194fd5bbf9 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -426,6 +426,9 @@ <!-- Permission required for CTS test - ClipboardManagerTest --> <uses-permission android:name="android.permission.SET_CLIP_SOURCE" /> + <!-- Permission required for CTS test - FontManagerTest --> + <uses-permission android:name="android.permission.UPDATE_FONTS" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java index ebb6e30d4b3b..e9e9b2421d4a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java @@ -289,15 +289,19 @@ public class Task { /** * Returns the visible width to height ratio. Returns 0f if snapshot data is not available. */ - public float getVisibleThumbnailRatio() { + public float getVisibleThumbnailRatio(boolean clipInsets) { if (lastSnapshotData.taskSize == null || lastSnapshotData.contentInsets == null) { return 0f; } - float availableWidth = lastSnapshotData.taskSize.x - (lastSnapshotData.contentInsets.left - + lastSnapshotData.contentInsets.right); - float availableHeight = lastSnapshotData.taskSize.y - (lastSnapshotData.contentInsets.top - + lastSnapshotData.contentInsets.bottom); + float availableWidth = lastSnapshotData.taskSize.x; + float availableHeight = lastSnapshotData.taskSize.y; + if (clipInsets) { + availableWidth -= + (lastSnapshotData.contentInsets.left + lastSnapshotData.contentInsets.right); + availableHeight -= + (lastSnapshotData.contentInsets.top + lastSnapshotData.contentInsets.bottom); + } return availableWidth / availableHeight; } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index a12326961b08..1b6c61237e29 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -480,8 +480,8 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static StartingWindowController provideStartingWindowController(Context context, - @ShellSplashscreenThread ShellExecutor executor) { - return new StartingWindowController(context, executor); + @ShellAnimationThread ShellExecutor executor, TransactionPool pool) { + return new StartingWindowController(context, executor, pool); } // diff --git a/services/core/Android.bp b/services/core/Android.bp index 0eb7dafe2612..b00689be3656 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -98,7 +98,6 @@ java_library_static { ":platform-compat-overrides", ":display-device-config", ":display-layout-config", - ":cec-config", ":device-state-config", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", @@ -119,7 +118,6 @@ java_library_static { ], required: [ - "cec_config.xml", "gps_debug.conf", "protolog.conf.json.gz", ], @@ -185,11 +183,6 @@ java_library_host { } prebuilt_etc { - name: "cec_config.xml", - src: "java/com/android/server/hdmi/cec_config.xml", -} - -prebuilt_etc { name: "gps_debug.conf", src: "java/com/android/server/location/gnss/gps_debug.conf", } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 72d32636f645..b4fcaeedd845 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -85,6 +85,7 @@ import android.net.ConnectionInfo; import android.net.ConnectivityDiagnosticsManager.ConnectivityReport; import android.net.ConnectivityDiagnosticsManager.DataStallReport; import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; import android.net.DataStallReportParcelable; import android.net.DnsResolverServiceManager; import android.net.ICaptivePortal; @@ -189,7 +190,6 @@ import android.util.SparseIntArray; import com.android.connectivity.aidl.INetworkAgent; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.AsyncChannel; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.LocationPermissionChecker; import com.android.internal.util.MessageUtils; @@ -345,8 +345,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private String mCurrentTcpBufferSizes; private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames( - new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class, - NetworkAgentInfo.class }); + new Class[] { ConnectivityService.class, NetworkAgent.class, NetworkAgentInfo.class }); private enum ReapUnvalidatedNetworks { // Tear down networks that have no chance (e.g. even if validated) of becoming @@ -1108,7 +1107,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRanker = new NetworkRanker(); final NetworkRequest defaultInternetRequest = createDefaultRequest(); mDefaultRequest = new NetworkRequestInfo( - defaultInternetRequest, null, new Binder(), + defaultInternetRequest, null, + new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO, null /* attributionTags */); mNetworkRequests.put(defaultInternetRequest, mDefaultRequest); mDefaultNetworkRequests.add(mDefaultRequest); @@ -1358,7 +1358,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (enable) { handleRegisterNetworkRequest(new NetworkRequestInfo( - networkRequest, null, new Binder(), + networkRequest, null, + new Binder(), + NetworkCallback.FLAG_INCLUDE_LOCATION_INFO, null /* attributionTags */)); } else { handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID, @@ -1719,8 +1721,8 @@ public class ConnectivityService extends IConnectivityManager.Stub result.put( nai.network, createWithLocationInfoSanitizedIfNecessaryWhenParceled( - nc, mDeps.getCallingUid(), callingPackageName, - callingAttributionTag)); + nc, false /* includeLocationSensitiveInfo */, + mDeps.getCallingUid(), callingPackageName, callingAttributionTag)); } } @@ -1733,7 +1735,9 @@ public class ConnectivityService extends IConnectivityManager.Stub result.put( network, createWithLocationInfoSanitizedIfNecessaryWhenParceled( - nc, mDeps.getCallingUid(), callingPackageName, + nc, + false /* includeLocationSensitiveInfo */, + mDeps.getCallingUid(), callingPackageName, callingAttributionTag)); } } @@ -1815,6 +1819,7 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceAccessPermission(); return createWithLocationInfoSanitizedIfNecessaryWhenParceled( getNetworkCapabilitiesInternal(network), + false /* includeLocationSensitiveInfo */, mDeps.getCallingUid(), callingPackageName, callingAttributionTag); } @@ -1848,8 +1853,8 @@ public class ConnectivityService extends IConnectivityManager.Stub @VisibleForTesting @Nullable NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled( - @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName, - @Nullable String callingAttributionTag) { + @Nullable NetworkCapabilities nc, boolean includeLocationSensitiveInfo, + int callerUid, @NonNull String callerPkgName, @Nullable String callingAttributionTag) { if (nc == null) { return null; } @@ -1857,7 +1862,9 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkCapabilities newNc; // Avoid doing location permission check if the transport info has no location sensitive // data. - if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) { + if (includeLocationSensitiveInfo + && nc.getTransportInfo() != null + && nc.getTransportInfo().hasLocationSensitiveFields()) { hasLocationPermission = hasLocationPermission(callerUid, callerPkgName, callingAttributionTag); newNc = new NetworkCapabilities(nc, hasLocationPermission); @@ -1874,6 +1881,16 @@ public class ConnectivityService extends IConnectivityManager.Stub // Owner UIDs already checked above. No need to re-check. return newNc; } + // If the caller does not want location sensitive data & target SDK >= S, then mask info. + // Else include the owner UID iff the caller has location permission to provide backwards + // compatibility for older apps. + if (!includeLocationSensitiveInfo + && isTargetSdkAtleast( + Build.VERSION_CODES.S, callerUid, callerPkgName)) { + newNc.setOwnerUid(INVALID_UID); + return newNc; + } + if (hasLocationPermission == null) { // Location permission not checked yet, check now for masking owner UID. hasLocationPermission = @@ -2892,22 +2909,6 @@ public class ConnectivityService extends IConnectivityManager.Stub super(looper); } - private boolean maybeHandleAsyncChannelMessage(Message msg) { - switch (msg.what) { - default: - return false; - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { - handleAsyncChannelHalfConnect(msg); - break; - } - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { - handleAsyncChannelDisconnected(msg); - break; - } - } - return true; - } - private void maybeHandleNetworkAgentMessage(Message msg) { final Pair<NetworkAgentInfo, Object> arg = (Pair<NetworkAgentInfo, Object>) msg.obj; final NetworkAgentInfo nai = arg.first; @@ -3199,8 +3200,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void handleMessage(Message msg) { - if (!maybeHandleAsyncChannelMessage(msg) - && !maybeHandleNetworkMonitorMessage(msg) + if (!maybeHandleNetworkMonitorMessage(msg) && !maybeHandleNetworkAgentInfoMessage(msg)) { maybeHandleNetworkAgentMessage(msg); } @@ -3464,21 +3464,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return false; } - private void handleAsyncChannelHalfConnect(Message msg) { - ensureRunningOnConnectivityServiceThread(); - if (mNetworkProviderInfos.containsKey(msg.replyTo)) { - if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - if (VDBG) log("NetworkFactory connected"); - // Finish setting up the full connection - NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo); - sendAllRequestsToProvider(npi); - } else { - loge("Error connecting NetworkFactory"); - mNetworkProviderInfos.remove(msg.obj); - } - } - } - private void handleNetworkAgentRegistered(Message msg) { final NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj; if (!mNetworkAgentInfos.contains(nai)) { @@ -3509,14 +3494,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - // This is a no-op if it's called with a message designating a provider that has - // already been destroyed, because its reference will not be found in the relevant - // maps. - private void handleAsyncChannelDisconnected(Message msg) { - NetworkProviderInfo npi = mNetworkProviderInfos.remove(msg.replyTo); - if (DBG && npi != null) log("unregisterNetworkFactory for " + npi.name); - } - // Destroys a network, remove references to it from the internal state managed by // ConnectivityService, free its interfaces and clean up. // Must be called on the Handler thread. @@ -5159,8 +5136,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private final IBinder.DeathRecipient mDeathRecipient; public final int providerId; - NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel, - int providerId, @NonNull IBinder.DeathRecipient deathRecipient) { + NetworkProviderInfo(String name, Messenger messenger, int providerId, + @NonNull IBinder.DeathRecipient deathRecipient) { this.name = name; this.messenger = messenger; this.providerId = providerId; @@ -5254,6 +5231,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private final IBinder mBinder; final int mPid; final int mUid; + final @NetworkCallback.Flag int mCallbackFlags; @Nullable final String mCallingAttributionTag; // In order to preserve the mapping of NetworkRequest-to-callback when apps register @@ -5301,17 +5279,26 @@ public class ConnectivityService extends IConnectivityManager.Stub mPid = getCallingPid(); mUid = mDeps.getCallingUid(); mNetworkRequestCounter.incrementCountOrThrow(mUid); + /** + * Location sensitive data not included in pending intent. Only included in + * {@link NetworkCallback}. + */ + mCallbackFlags = NetworkCallback.FLAG_NONE; mCallingAttributionTag = callingAttributionTag; } NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final Messenger m, - @Nullable final IBinder binder, @Nullable String callingAttributionTag) { - this(Collections.singletonList(r), r, m, binder, callingAttributionTag); + @Nullable final IBinder binder, + @NetworkCallback.Flag int callbackFlags, + @Nullable String callingAttributionTag) { + this(Collections.singletonList(r), r, m, binder, callbackFlags, callingAttributionTag); } NetworkRequestInfo(@NonNull final List<NetworkRequest> r, @NonNull final NetworkRequest requestForCallback, @Nullable final Messenger m, - @Nullable final IBinder binder, @Nullable String callingAttributionTag) { + @Nullable final IBinder binder, + @NetworkCallback.Flag int callbackFlags, + @Nullable String callingAttributionTag) { super(); ensureAllNetworkRequestsHaveType(r); mRequests = initializeRequests(r); @@ -5322,6 +5309,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mUid = mDeps.getCallingUid(); mPendingIntent = null; mNetworkRequestCounter.incrementCountOrThrow(mUid); + mCallbackFlags = callbackFlags; mCallingAttributionTag = callingAttributionTag; try { @@ -5363,6 +5351,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mUid = nri.mUid; mPendingIntent = nri.mPendingIntent; mNetworkRequestCounter.incrementCountOrThrow(mUid); + mCallbackFlags = nri.mCallbackFlags; mCallingAttributionTag = nri.mCallingAttributionTag; } @@ -5412,7 +5401,8 @@ public class ConnectivityService extends IConnectivityManager.Stub + " callback request Id: " + mNetworkRequestForCallback.requestId + " " + mRequests - + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent); + + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent) + + "callback flags: " + mCallbackFlags; } } @@ -5496,13 +5486,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) { - final UserHandle user = UserHandle.getUserHandleForUid(mDeps.getCallingUid()); + private boolean isTargetSdkAtleast(int version, int callingUid, + @NonNull String callingPackageName) { + final UserHandle user = UserHandle.getUserHandleForUid(callingUid); final PackageManager pm = mContext.createContextAsUser(user, 0 /* flags */).getPackageManager(); try { - final int callingVersion = pm.getApplicationInfo( - callingPackageName, 0 /* flags */).targetSdkVersion; + final int callingVersion = pm.getTargetSdkVersion(callingPackageName); if (callingVersion < version) return false; } catch (PackageManager.NameNotFoundException e) { } return true; @@ -5511,10 +5501,11 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder, - int legacyType, @NonNull String callingPackageName, + int legacyType, int callbackFlags, @NonNull String callingPackageName, @Nullable String callingAttributionTag) { if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) { - if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) { + if (isTargetSdkAtleast(Build.VERSION_CODES.M, mDeps.getCallingUid(), + callingPackageName)) { throw new SecurityException("Insufficient permissions to specify legacy type"); } } @@ -5576,7 +5567,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, nextNetworkRequestId(), reqType); final NetworkRequestInfo nri = getNriToRegister( - networkRequest, messenger, binder, callingAttributionTag); + networkRequest, messenger, binder, callbackFlags, callingAttributionTag); if (DBG) log("requestNetwork for " + nri); // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were @@ -5611,6 +5602,7 @@ public class ConnectivityService extends IConnectivityManager.Stub */ private NetworkRequestInfo getNriToRegister(@NonNull final NetworkRequest nr, @Nullable final Messenger msgr, @Nullable final IBinder binder, + @NetworkCallback.Flag int callbackFlags, @Nullable String callingAttributionTag) { final List<NetworkRequest> requests; if (NetworkRequest.Type.TRACK_DEFAULT == nr.type) { @@ -5619,7 +5611,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { requests = Collections.singletonList(nr); } - return new NetworkRequestInfo(requests, nr, msgr, binder, callingAttributionTag); + return new NetworkRequestInfo( + requests, nr, msgr, binder, callbackFlags, callingAttributionTag); } private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities, @@ -5745,8 +5738,9 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, IBinder binder, @NonNull String callingPackageName, - @Nullable String callingAttributionTag) { + Messenger messenger, IBinder binder, + @NetworkCallback.Flag int callbackFlags, + @NonNull String callingPackageName, @NonNull String callingAttributionTag) { final int callingUid = mDeps.getCallingUid(); if (!hasWifiNetworkListenPermission(networkCapabilities)) { enforceAccessPermission(); @@ -5767,7 +5761,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); NetworkRequestInfo nri = - new NetworkRequestInfo(networkRequest, messenger, binder, callingAttributionTag); + new NetworkRequestInfo(networkRequest, messenger, binder, callbackFlags, + callingAttributionTag); if (VDBG) log("listenForNetwork for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri)); @@ -5836,8 +5831,7 @@ public class ConnectivityService extends IConnectivityManager.Stub public int registerNetworkProvider(Messenger messenger, String name) { enforceNetworkFactoryOrSettingsPermission(); NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, - null /* asyncChannel */, nextNetworkProviderId(), - () -> unregisterNetworkProvider(messenger)); + nextNetworkProviderId(), () -> unregisterNetworkProvider(messenger)); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi)); return npi.providerId; } @@ -7101,6 +7095,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) { putParcelable(bundle, networkAgent.network); } + final boolean includeLocationSensitiveInfo = + (nri.mCallbackFlags & NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) != 0; switch (notificationType) { case ConnectivityManager.CALLBACK_AVAILABLE: { final NetworkCapabilities nc = @@ -7109,7 +7105,8 @@ public class ConnectivityService extends IConnectivityManager.Stub putParcelable( bundle, createWithLocationInfoSanitizedIfNecessaryWhenParceled( - nc, nri.mUid, nrForCallback.getRequestorPackageName(), + nc, includeLocationSensitiveInfo, nri.mUid, + nrForCallback.getRequestorPackageName(), nri.mCallingAttributionTag)); putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions( networkAgent.linkProperties, nri.mPid, nri.mUid)); @@ -7129,7 +7126,8 @@ public class ConnectivityService extends IConnectivityManager.Stub putParcelable( bundle, createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, nri.mUid, nrForCallback.getRequestorPackageName(), + netCap, includeLocationSensitiveInfo, nri.mUid, + nrForCallback.getRequestorPackageName(), nri.mCallingAttributionTag)); break; } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 81d4b9da63c8..4c3c6ef21fc5 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -56,6 +56,7 @@ import android.system.Os; import android.system.OsConstants; import android.text.TextUtils; import android.util.Log; +import android.util.Range; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -756,13 +757,9 @@ public class IpSecService extends IIpSecService.Stub { } } - // These values have been reserved in NetIdManager - @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00; - - public static final int TUN_INTF_NETID_RANGE = 0x0400; - private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray(); - private int mNextTunnelNetIdIndex = 0; + final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange(); + private int mNextTunnelNetId = mNetIdRange.getLower(); /** * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces @@ -775,11 +772,13 @@ public class IpSecService extends IIpSecService.Stub { */ @VisibleForTesting int reserveNetId() { + final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1; synchronized (mTunnelNetIds) { - for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) { - int index = mNextTunnelNetIdIndex; - int netId = index + TUN_INTF_NETID_START; - if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0; + for (int i = 0; i < range; i++) { + final int netId = mNextTunnelNetId; + if (++mNextTunnelNetId > mNetIdRange.getUpper()) { + mNextTunnelNetId = mNetIdRange.getLower(); + } if (!mTunnelNetIds.get(netId)) { mTunnelNetIds.put(netId, true); return netId; diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java index 097fb3ae47e3..61925c80a22b 100644 --- a/services/core/java/com/android/server/NetIdManager.java +++ b/services/core/java/com/android/server/NetIdManager.java @@ -17,6 +17,7 @@ package com.android.server; import android.annotation.NonNull; +import android.net.ConnectivityManager; import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; @@ -31,7 +32,7 @@ public class NetIdManager { // Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp public static final int MIN_NET_ID = 100; // some reserved marks // Top IDs reserved by IpSecService - public static final int MAX_NET_ID = 65535 - IpSecService.TUN_INTF_NETID_RANGE; + public static final int MAX_NET_ID = ConnectivityManager.getIpSecNetIdRange().getLower() - 1; @GuardedBy("mNetIdInUse") private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8ea6194a9535..06a1abb72607 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -44,11 +44,11 @@ import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.FactoryTest.FACTORY_TEST_OFF; -import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; +import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED; import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.os.Process.BLUETOOTH_UID; @@ -273,6 +273,9 @@ import android.os.TransactionTooLargeException; import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; +import android.os.incremental.IIncrementalService; +import android.os.incremental.IncrementalManager; +import android.os.incremental.IncrementalMetrics; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.provider.DeviceConfig; @@ -7697,18 +7700,32 @@ public class ActivityManagerService extends IActivityManager.Stub */ void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName, ApplicationErrorReport.CrashInfo crashInfo) { - boolean isPackageLoading = false; + boolean isIncremental = false; + float loadingProgress = 1; + long millisSinceOldestPendingRead = 0; // Notify package manager service to possibly update package state if (r != null && r.info != null && r.info.packageName != null) { + final String codePath = r.info.getCodePath(); mPackageManagerInt.notifyPackageCrashOrAnr(r.info.packageName); IncrementalStatesInfo incrementalStatesInfo = mPackageManagerInt.getIncrementalStatesInfo(r.info.packageName, r.uid, r.userId); - isPackageLoading = incrementalStatesInfo.isLoading(); - if (isPackageLoading) { - // Report in the main log that the package is still loading - Slog.e(TAG, "App crashed when package " + r.info.packageName + " is " - + ((int) (incrementalStatesInfo.getProgress() * 100)) + "% loaded."); + if (incrementalStatesInfo != null) { + loadingProgress = incrementalStatesInfo.getProgress(); + } + isIncremental = IncrementalManager.isIncrementalPath(codePath); + if (isIncremental) { + // Report in the main log about the incremental package + Slog.e(TAG, "App crashed on incremental package " + r.info.packageName + + " which is " + ((int) (loadingProgress * 100)) + "% loaded."); + final IBinder incrementalService = ServiceManager.getService( + Context.INCREMENTAL_SERVICE); + if (incrementalService != null) { + final IncrementalManager incrementalManager = new IncrementalManager( + IIncrementalService.Stub.asInterface(incrementalService)); + IncrementalMetrics metrics = incrementalManager.getMetrics(codePath); + millisSinceOldestPendingRead = metrics.getMillisSinceOldestPendingRead(); + } } } @@ -7737,7 +7754,7 @@ public class ActivityManagerService extends IActivityManager.Stub processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER : (r != null) ? r.getProcessClassEnum() : ServerProtoEnums.ERROR_SOURCE_UNKNOWN, - isPackageLoading + isIncremental, loadingProgress, millisSinceOldestPendingRead ); final int relaunchReason = r == null ? RELAUNCH_REASON_NONE diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 81c4c8605fb4..e79f09665153 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -1416,7 +1416,7 @@ public final class BroadcastQueue { } } if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID && - r.requiredPermissions != null && r.requiredPermissions.length > 0) { + r.requiredPermissions != null && r.requiredPermissions.length > 0) { for (int i = 0; i < r.requiredPermissions.length; i++) { String requiredPermission = r.requiredPermissions[i]; try { @@ -1424,7 +1424,7 @@ public final class BroadcastQueue { checkPermission(requiredPermission, info.activityInfo.applicationInfo.packageName, UserHandle - .getUserId(info.activityInfo.applicationInfo.uid)); + .getUserId(info.activityInfo.applicationInfo.uid)); } catch (RemoteException e) { perm = PackageManager.PERMISSION_DENIED; } @@ -1439,36 +1439,18 @@ public final class BroadcastQueue { break; } int appOp = AppOpsManager.permissionToOpCode(requiredPermission); - if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp - && mService.getAppOpsManager().noteOpNoThrow(appOp, - info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, - null /* default featureId */, - "Broadcast delivered to " + info.activityInfo.name) - != AppOpsManager.MODE_ALLOWED) { - Slog.w(TAG, "Appop Denial: receiving " - + r.intent + " to " - + component.flattenToShortString() - + " requires appop " + AppOpsManager.permissionToOp( - requiredPermission) - + " due to sender " + r.callerPackage - + " (uid " + r.callingUid + ")"); - skip = true; - break; + if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) { + if (!noteOpForManifestReceiver(appOp, r, info, component)) { + skip = true; + break; + } } } } - if (!skip && r.appOp != AppOpsManager.OP_NONE - && mService.getAppOpsManager().noteOpNoThrow(r.appOp, - info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, - null /* default featureId */, "Broadcast delivered to " + info.activityInfo.name) - != AppOpsManager.MODE_ALLOWED) { - Slog.w(TAG, "Appop Denial: receiving " - + r.intent + " to " - + component.flattenToShortString() - + " requires appop " + AppOpsManager.opToName(r.appOp) - + " due to sender " + r.callerPackage - + " (uid " + r.callingUid + ")"); - skip = true; + if (!skip && r.appOp != AppOpsManager.OP_NONE) { + if (!noteOpForManifestReceiver(r.appOp, r, info, component)) { + skip = true; + } } boolean isSingleton = false; try { @@ -1717,6 +1699,40 @@ public final class BroadcastQueue { mPendingBroadcastRecvIndex = recIdx; } + private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info, + ComponentName component) { + if (info.activityInfo.attributionTags == null) { + return noteOpForManifestReceiverInner(appOp, r, info, component, null); + } else { + // Attribution tags provided, noteOp each tag + for (String tag : info.activityInfo.attributionTags) { + if (!noteOpForManifestReceiverInner(appOp, r, info, component, tag)) { + return false; + } + } + return true; + } + } + + private boolean noteOpForManifestReceiverInner(int appOp, BroadcastRecord r, ResolveInfo info, + ComponentName component, String tag) { + if (mService.getAppOpsManager().noteOpNoThrow(appOp, + info.activityInfo.applicationInfo.uid, + info.activityInfo.packageName, + tag, + "Broadcast delivered to " + info.activityInfo.name) + != AppOpsManager.MODE_ALLOWED) { + Slog.w(TAG, "Appop Denial: receiving " + + r.intent + " to " + + component.flattenToShortString() + + " requires appop " + AppOpsManager.opToName(appOp) + + " due to sender " + r.callerPackage + + " (uid " + r.callingUid + ")"); + return false; + } + return true; + } + private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) { if (r == null || proc == null || !r.allowBackgroundActivityStarts) { return; diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java index d03a47afed8a..93f30cc8ac5e 100644 --- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java @@ -26,12 +26,18 @@ import android.app.AnrController; import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; import android.content.ComponentName; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IncrementalStatesInfo; import android.content.pm.PackageManagerInternal; +import android.os.IBinder; import android.os.Message; import android.os.Process; +import android.os.ServiceManager; import android.os.SystemClock; +import android.os.incremental.IIncrementalService; +import android.os.incremental.IncrementalManager; +import android.os.incremental.IncrementalMetrics; import android.provider.Settings; import android.util.EventLog; import android.util.Slog; @@ -294,14 +300,31 @@ class ProcessErrorStateRecord { } // Check if package is still being loaded - boolean isPackageLoading = false; + boolean isIncremental = false; + float loadingProgress = 1; + long millisSinceOldestPendingRead = 0; final PackageManagerInternal packageManagerInternal = mService.getPackageManagerInternal(); if (aInfo != null && aInfo.packageName != null) { IncrementalStatesInfo incrementalStatesInfo = packageManagerInternal.getIncrementalStatesInfo( aInfo.packageName, mApp.uid, mApp.userId); if (incrementalStatesInfo != null) { - isPackageLoading = incrementalStatesInfo.isLoading(); + loadingProgress = incrementalStatesInfo.getProgress(); + } + final String codePath = aInfo.getCodePath(); + isIncremental = IncrementalManager.isIncrementalPath(codePath); + if (isIncremental) { + // Report in the main log that the incremental package is still loading + Slog.e(TAG, "App crashed on incremental package " + aInfo.packageName + + " which is " + ((int) (loadingProgress * 100)) + "% loaded."); + final IBinder incrementalService = ServiceManager.getService( + Context.INCREMENTAL_SERVICE); + if (incrementalService != null) { + final IncrementalManager incrementalManager = new IncrementalManager( + IIncrementalService.Stub.asInterface(incrementalService)); + IncrementalMetrics metrics = incrementalManager.getMetrics(codePath); + millisSinceOldestPendingRead = metrics.getMillisSinceOldestPendingRead(); + } } } @@ -322,10 +345,8 @@ class ProcessErrorStateRecord { info.append("Parent: ").append(parentShortComponentName).append("\n"); } - if (isPackageLoading) { - // Report in the main log that the package is still loading - final float loadingProgress = packageManagerInternal.getIncrementalStatesInfo( - aInfo.packageName, mApp.uid, mApp.userId).getProgress(); + if (isIncremental) { + // Report in the main log about the incremental package info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n"); } @@ -412,7 +433,8 @@ class ProcessErrorStateRecord { ? FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND : FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND, mApp.getProcessClassEnum(), - (mApp.info != null) ? mApp.info.packageName : "", isPackageLoading); + (mApp.info != null) ? mApp.info.packageName : "", + isIncremental, loadingProgress, millisSinceOldestPendingRead); final ProcessRecord parentPr = parentProcess != null ? (ProcessRecord) parentProcess.mOwner : null; mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName, diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index e0d1375b4069..6712c5474921 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -950,34 +950,36 @@ public class ClipboardService extends SystemService { } clipboard.mNotifiedUids.put(uid, true); - // Retrieve the app label of the source of the clip data - CharSequence sourceAppLabel = null; - if (clipboard.mPrimaryClipPackage != null) { - try { - sourceAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser( - clipboard.mPrimaryClipPackage, 0, userId)); - } catch (PackageManager.NameNotFoundException e) { - // leave label as null + Binder.withCleanCallingIdentity(() -> { + // Retrieve the app label of the source of the clip data + CharSequence sourceAppLabel = null; + if (clipboard.mPrimaryClipPackage != null) { + try { + sourceAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser( + clipboard.mPrimaryClipPackage, 0, userId)); + } catch (PackageManager.NameNotFoundException e) { + // leave label as null + } } - } - try { - CharSequence callingAppLabel = mPm.getApplicationLabel( - mPm.getApplicationInfoAsUser(callingPackage, 0, userId)); - String message; - if (sourceAppLabel != null) { - message = getContext().getString( - R.string.pasted_from_app, callingAppLabel, sourceAppLabel); - } else { - message = getContext().getString(R.string.pasted_from_clipboard, callingAppLabel); + try { + CharSequence callingAppLabel = mPm.getApplicationLabel( + mPm.getApplicationInfoAsUser(callingPackage, 0, userId)); + String message; + if (sourceAppLabel != null) { + message = getContext().getString( + R.string.pasted_from_app, callingAppLabel, sourceAppLabel); + } else { + message = getContext().getString( + R.string.pasted_from_clipboard, callingAppLabel); + } + Slog.i(TAG, message); + Toast.makeText( + getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT) + .show(); + } catch (PackageManager.NameNotFoundException e) { + // do nothing } - Slog.i(TAG, message); - Binder.withCleanCallingIdentity(() -> - Toast.makeText(getContext(), UiThread.get().getLooper(), message, - Toast.LENGTH_SHORT) - .show()); - } catch (PackageManager.NameNotFoundException e) { - // do nothing - } + }); } } diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index 9411e33434d8..488677ac1b59 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -31,14 +31,17 @@ import static android.os.Process.SYSTEM_UID; import static com.android.net.module.util.CollectionUtils.toIntArray; import android.annotation.NonNull; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.PackageManagerInternal; import android.net.INetd; import android.net.UidRange; +import android.net.Uri; import android.os.Build; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -54,7 +57,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.net.module.util.CollectionUtils; -import com.android.server.LocalServices; import java.util.ArrayList; import java.util.HashMap; @@ -71,7 +73,7 @@ import java.util.Set; * * @hide */ -public class PermissionMonitor implements PackageManagerInternal.PackageListObserver { +public class PermissionMonitor { private static final String TAG = "PermissionMonitor"; private static final boolean DBG = true; protected static final Boolean SYSTEM = Boolean.TRUE; @@ -83,6 +85,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse private final SystemConfigManager mSystemConfigManager; private final INetd mNetd; private final Dependencies mDeps; + private final Context mContext; @GuardedBy("this") private final Set<UserHandle> mUsers = new HashSet<>(); @@ -102,6 +105,25 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse @GuardedBy("this") private final Set<Integer> mAllApps = new HashSet<>(); + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + final Uri packageData = intent.getData(); + final String packageName = + packageData != null ? packageData.getSchemeSpecificPart() : null; + + if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + onPackageAdded(packageName, uid); + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + onPackageRemoved(packageName, uid); + } else { + Log.wtf(TAG, "received unexpected intent: " + action); + } + } + }; + /** * Dependencies of PermissionMonitor, for injection in tests. */ @@ -127,6 +149,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse mSystemConfigManager = context.getSystemService(SystemConfigManager.class); mNetd = netd; mDeps = deps; + mContext = context; } // Intended to be called only once at startup, after the system is ready. Installs a broadcast @@ -134,12 +157,14 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse public synchronized void startMonitoring() { log("Monitoring"); - PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); - if (pmi != null) { - pmi.getPackageList(this); - } else { - loge("failed to get the PackageManagerInternal service"); - } + final IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + intentFilter.addDataScheme("package"); + mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */).registerReceiver( + mIntentReceiver, intentFilter, null /* broadcastPermission */, + null /* scheduler */); + List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS | MATCH_ANY_USER); if (apps == null) { @@ -347,9 +372,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse * * @hide */ - @Override public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) { - sendPackagePermissionsForUid(uid, getPermissionForUid(uid)); + // TODO: Netd is using appId for checking traffic permission. Correct the methods that are + // using appId instead of uid actually + sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid)); // If multiple packages share a UID (cf: android:sharedUserId) and ask for different // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is). @@ -384,9 +410,10 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse * * @hide */ - @Override public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) { - sendPackagePermissionsForUid(uid, getPermissionForUid(uid)); + // TODO: Netd is using appId for checking traffic permission. Correct the methods that are + // using appId instead of uid actually + sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid)); // If the newly-removed package falls within some VPN's uid range, update Netd with it. // This needs to happen before the mApps update below, since removeBypassingUids() depends @@ -432,19 +459,6 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse } } - /** - * Called when a package is changed. - * - * @param packageName The name of the changed package. - * @param uid The uid of the changed package. - * - * @hide - */ - @Override - public synchronized void onPackageChanged(@NonNull final String packageName, final int uid) { - sendPackagePermissionsForUid(uid, getPermissionForUid(uid)); - } - private static int getNetdPermissionMask(String[] requestedPermissions, int[] requestedPermissionsFlags) { int permissions = 0; diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 027b9afba392..0e714969a69b 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -791,12 +791,13 @@ public final class ContentService extends IContentService.Stub { public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) { enforceCrossUserPermission(userId, "no permission to read sync settings for user " + userId); + final int callingUid = Binder.getCallingUid(); // This makes it so that future permission checks will be in the context of this // process rather than the caller's process. We will restore this before returning. final long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); - return syncManager.getSyncAdapterTypes(userId); + return syncManager.getSyncAdapterTypes(callingUid, userId); } finally { restoreCallingIdentity(identityToken); } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index df870125e253..ac7e01ed4d72 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -89,6 +89,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.provider.Settings; +import android.text.TextUtils; import android.text.format.TimeMigrationUtils; import android.util.EventLog; import android.util.Log; @@ -1257,16 +1258,19 @@ public class SyncManager { syncExemptionFlag, callingUid, callingPid, callingPackage); } - public SyncAdapterType[] getSyncAdapterTypes(int userId) { + public SyncAdapterType[] getSyncAdapterTypes(int callingUid, int userId) { final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos; serviceInfos = mSyncAdapters.getAllServices(userId); - SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()]; - int i = 0; + final List<SyncAdapterType> types = new ArrayList<>(serviceInfos.size()); for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) { - types[i] = serviceInfo.type; - ++i; + final String packageName = serviceInfo.type.getPackageName(); + if (!TextUtils.isEmpty(packageName) && mPackageManagerInternal.filterAppAccess( + packageName, callingUid, userId)) { + continue; + } + types.add(serviceInfo.type); } - return types; + return types.toArray(new SyncAdapterType[] {}); } public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) { diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index c4663a122321..2b7d2074a7e0 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -67,6 +67,8 @@ public final class FontManagerService extends IFontManager.Stub { @Override public FontConfig getFontConfig() { + getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS, + "UPDATE_FONTS permission required."); return getSystemFontConfig(); } diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java index c23e2e691b55..947ee24f8e02 100644 --- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java @@ -97,11 +97,16 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { @Override public boolean start() { - // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0. - // The message is re-sent at the end of the action for devices that don't support 2.0. - sendSetStreamPath(); - int targetPowerStatus = localDevice().mService.getHdmiCecNetwork() - .getCecDeviceInfo(getTargetAddress()).getDevicePowerStatus(); + // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0. + // The message is re-sent at the end of the action for devices that don't support 2.0. + sendSetStreamPath(); + int targetPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN; + HdmiDeviceInfo targetDevice = localDevice().mService.getHdmiCecNetwork().getCecDeviceInfo( + getTargetAddress()); + if (targetDevice != null) { + targetPowerStatus = targetDevice.getDevicePowerStatus(); + } + if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) { queryDevicePowerStatus(); } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index 624af30854dc..6fbb26c4a5ee 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -35,33 +35,19 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings.Global; import android.util.ArrayMap; -import android.util.Slog; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ConcurrentUtils; -import com.android.server.hdmi.cec.config.CecSettings; -import com.android.server.hdmi.cec.config.Setting; -import com.android.server.hdmi.cec.config.Value; -import com.android.server.hdmi.cec.config.XmlParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; -import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map.Entry; -import java.util.Set; import java.util.concurrent.Executor; -import javax.xml.datatype.DatatypeConfigurationException; - /** * The {@link HdmiCecConfig} class is used for getting information about * available HDMI CEC settings. @@ -74,6 +60,10 @@ public class HdmiCecConfig { private static final String SHARED_PREFS_DIR = "shared_prefs"; private static final String SHARED_PREFS_NAME = "cec_config.xml"; + private static final int STORAGE_SYSPROPS = 0; + private static final int STORAGE_GLOBAL_SETTINGS = 1; + private static final int STORAGE_SHARED_PREFS = 2; + @IntDef({ STORAGE_SYSPROPS, STORAGE_GLOBAL_SETTINGS, @@ -81,10 +71,6 @@ public class HdmiCecConfig { }) private @interface Storage {} - private static final int STORAGE_SYSPROPS = 0; - private static final int STORAGE_GLOBAL_SETTINGS = 1; - private static final int STORAGE_SHARED_PREFS = 2; - private static final String VALUE_TYPE_STRING = "string"; private static final String VALUE_TYPE_INT = "int"; @@ -96,8 +82,6 @@ public class HdmiCecConfig { @NonNull private final Context mContext; @NonNull private final StorageAdapter mStorageAdapter; - @Nullable private final CecSettings mSystemConfig; - @Nullable private final CecSettings mVendorOverride; private final Object mLock = new Object(); @@ -107,6 +91,18 @@ public class HdmiCecConfig { private SettingsObserver mSettingsObserver; + private LinkedHashMap<String, Setting> mSettings = new LinkedHashMap<>(); + + /** + * Exception thrown when the CEC Configuration setup verification fails. + * This usually means a settings lacks default value or storage/storage key. + */ + public static class VerificationException extends RuntimeException { + public VerificationException(String message) { + super(message); + } + } + /** * Listener used to get notifications when value of a setting changes. */ @@ -202,91 +198,297 @@ public class HdmiCecConfig { } } - @VisibleForTesting - HdmiCecConfig(@NonNull Context context, - @NonNull StorageAdapter storageAdapter, - @Nullable CecSettings systemConfig, - @Nullable CecSettings vendorOverride) { - mContext = context; - mStorageAdapter = storageAdapter; - mSystemConfig = systemConfig; - mVendorOverride = vendorOverride; - if (mSystemConfig == null) { - Slog.i(TAG, "CEC system configuration XML missing."); + private class Value { + private final String mStringValue; + private final Integer mIntValue; + + Value(@NonNull String value) { + mStringValue = value; + mIntValue = null; } - if (mVendorOverride == null) { - Slog.i(TAG, "CEC OEM configuration override XML missing."); + + Value(@NonNull Integer value) { + mStringValue = null; + mIntValue = value; } - } - HdmiCecConfig(@NonNull Context context) { - this(context, new StorageAdapter(context), - readSettingsFromFile(Environment.buildPath(Environment.getRootDirectory(), - ETC_DIR, CONFIG_FILE)), - readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(), - ETC_DIR, CONFIG_FILE))); + String getStringValue() { + return mStringValue; + } + + Integer getIntValue() { + return mIntValue; + } } - @Nullable - private static CecSettings readSettingsFromFile(@NonNull File file) { - if (!file.exists()) { - return null; + private class Setting { + @NonNull private final Context mContext; + @NonNull private final @CecSettingName String mName; + private final boolean mUserConfigurable; + + private Value mDefaultValue = null; + private List<Value> mAllowedValues = new ArrayList<>(); + + Setting(@NonNull Context context, + @NonNull @CecSettingName String name, + int userConfResId) { + mContext = context; + mName = name; + mUserConfigurable = mContext.getResources().getBoolean(userConfResId); } - if (!file.isFile()) { - Slog.e(TAG, "CEC configuration is not a file: " + file + ", skipping."); - return null; + + public @CecSettingName String getName() { + return mName; } - try (InputStream in = new BufferedInputStream(new FileInputStream(file))) { - return XmlParser.read(in); - } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { - Slog.e(TAG, "Encountered an error while reading/parsing CEC config file: " + file, e); + + public @ValueType String getValueType() { + return getDefaultValue().getStringValue() != null + ? VALUE_TYPE_STRING + : VALUE_TYPE_INT; } - return null; - } - @NonNull - @VisibleForTesting - static HdmiCecConfig createFromStrings(@NonNull Context context, - @NonNull StorageAdapter storageAdapter, - @Nullable String productConfigXml, - @Nullable String vendorOverrideXml) { - CecSettings productConfig = null; - CecSettings vendorOverride = null; - try { - if (productConfigXml != null) { - productConfig = XmlParser.read( - new ByteArrayInputStream(productConfigXml.getBytes())); - } - if (vendorOverrideXml != null) { - vendorOverride = XmlParser.read( - new ByteArrayInputStream(vendorOverrideXml.getBytes())); + public Value getDefaultValue() { + if (mDefaultValue == null) { + throw new VerificationException("Invalid CEC setup for '" + + this.getName() + "' setting. " + + "Setting has no default value."); } - } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { - Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e); + return mDefaultValue; } - return new HdmiCecConfig(context, storageAdapter, productConfig, vendorOverride); - } - @Nullable - private Setting getSetting(@NonNull String name) { - if (mSystemConfig == null) { - return null; - } - if (mVendorOverride != null) { - // First read from the vendor override. - for (Setting setting : mVendorOverride.getSetting()) { - if (setting.getName().equals(name)) { - return setting; + public boolean getUserConfigurable() { + return mUserConfigurable; + } + + private void registerValue(@NonNull Value value, + int allowedResId, int defaultResId) { + if (mContext.getResources().getBoolean(allowedResId)) { + mAllowedValues.add(value); + if (mContext.getResources().getBoolean(defaultResId)) { + if (mDefaultValue != null) { + throw new VerificationException("Invalid CEC setup for '" + + this.getName() + "' setting. " + + "Setting already has a default value."); + } + mDefaultValue = value; } } } - // If not found, try the system config. - for (Setting setting : mSystemConfig.getSetting()) { - if (setting.getName().equals(name)) { - return setting; - } + + public void registerValue(@NonNull String value, int allowedResId, + int defaultResId) { + registerValue(new Value(value), allowedResId, defaultResId); } - return null; + + public void registerValue(int value, int allowedResId, + int defaultResId) { + registerValue(new Value(value), allowedResId, defaultResId); + } + + + public List<Value> getAllowedValues() { + return mAllowedValues; + } + } + + @VisibleForTesting + HdmiCecConfig(@NonNull Context context, + @NonNull StorageAdapter storageAdapter) { + mContext = context; + mStorageAdapter = storageAdapter; + + Setting hdmiCecEnabled = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + R.bool.config_cecHdmiCecEnabled_userConfigurable); + hdmiCecEnabled.registerValue(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED, + R.bool.config_cecHdmiCecControlEnabled_allowed, + R.bool.config_cecHdmiCecControlEnabled_default); + hdmiCecEnabled.registerValue(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED, + R.bool.config_cecHdmiCecControlDisabled_allowed, + R.bool.config_cecHdmiCecControlDisabled_default); + + Setting hdmiCecVersion = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + R.bool.config_cecHdmiCecVersion_userConfigurable); + hdmiCecVersion.registerValue(HdmiControlManager.HDMI_CEC_VERSION_1_4_B, + R.bool.config_cecHdmiCecVersion14b_allowed, + R.bool.config_cecHdmiCecVersion14b_default); + hdmiCecVersion.registerValue(HdmiControlManager.HDMI_CEC_VERSION_2_0, + R.bool.config_cecHdmiCecVersion20_allowed, + R.bool.config_cecHdmiCecVersion20_default); + + Setting powerControlMode = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + R.bool.config_cecSendStandbyOnSleep_userConfigurable); + powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV, + R.bool.config_cecPowerControlModeTv_allowed, + R.bool.config_cecPowerControlModeTv_default); + powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST, + R.bool.config_cecPowerControlModeBroadcast_allowed, + R.bool.config_cecPowerControlModeBroadcast_default); + powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_NONE, + R.bool.config_cecPowerControlModeNone_allowed, + R.bool.config_cecPowerControlModeNone_default); + + Setting powerStateChangeOnActiveSourceLost = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, + R.bool.config_cecPowerStateChangeOnActiveSourceLost_userConfigurable); + powerStateChangeOnActiveSourceLost.registerValue( + HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE, + R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_allowed, + R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_default); + powerStateChangeOnActiveSourceLost.registerValue( + HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW, + R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed, + R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default); + + Setting systemAudioModeMuting = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, + R.bool.config_cecSystemAudioModeMuting_userConfigurable); + systemAudioModeMuting.registerValue(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_ENABLED, + R.bool.config_cecSystemAudioModeMutingEnabled_allowed, + R.bool.config_cecSystemAudioModeMutingEnabled_default); + systemAudioModeMuting.registerValue(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED, + R.bool.config_cecSystemAudioModeMutingDisabled_allowed, + R.bool.config_cecSystemAudioModeMutingDisabled_default); + + Setting volumeControlMode = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + R.bool.config_cecVolumeControlMode_userConfigurable); + volumeControlMode.registerValue(HdmiControlManager.VOLUME_CONTROL_ENABLED, + R.bool.config_cecVolumeControlModeEnabled_allowed, + R.bool.config_cecVolumeControlModeEnabled_default); + volumeControlMode.registerValue(HdmiControlManager.VOLUME_CONTROL_DISABLED, + R.bool.config_cecVolumeControlModeDisabled_allowed, + R.bool.config_cecVolumeControlModeDisabled_default); + + Setting tvWakeOnOneTouchPlay = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, + R.bool.config_cecTvWakeOnOneTouchPlay_userConfigurable); + tvWakeOnOneTouchPlay.registerValue(HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED, + R.bool.config_cecTvWakeOnOneTouchPlayEnabled_allowed, + R.bool.config_cecTvWakeOnOneTouchPlayEnabled_default); + tvWakeOnOneTouchPlay.registerValue(HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED, + R.bool.config_cecTvWakeOnOneTouchPlayDisabled_allowed, + R.bool.config_cecTvWakeOnOneTouchPlayDisabled_default); + + Setting tvSendStandbyOnSleep = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, + R.bool.config_cecTvSendStandbyOnSleep_userConfigurable); + tvSendStandbyOnSleep.registerValue(HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED, + R.bool.config_cecTvSendStandbyOnSleepEnabled_allowed, + R.bool.config_cecTvSendStandbyOnSleepEnabled_default); + tvSendStandbyOnSleep.registerValue(HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_DISABLED, + R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed, + R.bool.config_cecTvSendStandbyOnSleepDisabled_default); + + Setting rcProfileTv = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV, + R.bool.config_cecRcProfileTv_userConfigurable); + rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_NONE, + R.bool.config_cecRcProfileTvNone_allowed, + R.bool.config_cecRcProfileTvNone_default); + rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_ONE, + R.bool.config_cecRcProfileTvOne_allowed, + R.bool.config_cecRcProfileTvOne_default); + rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_TWO, + R.bool.config_cecRcProfileTvTwo_allowed, + R.bool.config_cecRcProfileTvTwo_default); + rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_THREE, + R.bool.config_cecRcProfileTvThree_allowed, + R.bool.config_cecRcProfileTvThree_default); + rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_FOUR, + R.bool.config_cecRcProfileTvFour_allowed, + R.bool.config_cecRcProfileTvFour_default); + + Setting rcProfileSourceRootMenu = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, + R.bool.config_cecRcProfileSourceRootMenu_userConfigurable); + rcProfileSourceRootMenu.registerValue( + HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_HANDLED, + R.bool.config_cecRcProfileSourceRootMenuHandled_allowed, + R.bool.config_cecRcProfileSourceRootMenuHandled_default); + rcProfileSourceRootMenu.registerValue( + HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_NOT_HANDLED, + R.bool.config_cecRcProfileSourceRootMenuNotHandled_allowed, + R.bool.config_cecRcProfileSourceRootMenuNotHandled_default); + + Setting rcProfileSourceSetupMenu = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, + R.bool.config_cecRcProfileSourceSetupMenu_userConfigurable); + rcProfileSourceSetupMenu.registerValue( + HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_HANDLED, + R.bool.config_cecRcProfileSourceSetupMenuHandled_allowed, + R.bool.config_cecRcProfileSourceSetupMenuHandled_default); + rcProfileSourceSetupMenu.registerValue( + HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_NOT_HANDLED, + R.bool.config_cecRcProfileSourceSetupMenuNotHandled_allowed, + R.bool.config_cecRcProfileSourceSetupMenuNotHandled_default); + + Setting rcProfileSourceContentsMenu = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU, + R.bool.config_cecRcProfileSourceContentsMenu_userConfigurable); + rcProfileSourceContentsMenu.registerValue( + HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_HANDLED, + R.bool.config_cecRcProfileSourceContentsMenuHandled_allowed, + R.bool.config_cecRcProfileSourceContentsMenuHandled_default); + rcProfileSourceContentsMenu.registerValue( + HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_NOT_HANDLED, + R.bool.config_cecRcProfileSourceContentsMenuNotHandled_allowed, + R.bool.config_cecRcProfileSourceContentsMenuNotHandled_default); + + Setting rcProfileSourceTopMenu = registerSetting( + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU, + R.bool.config_cecRcProfileSourceTopMenu_userConfigurable); + rcProfileSourceTopMenu.registerValue( + HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_HANDLED, + R.bool.config_cecRcProfileSourceTopMenuHandled_allowed, + R.bool.config_cecRcProfileSourceTopMenuHandled_default); + rcProfileSourceTopMenu.registerValue( + HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_NOT_HANDLED, + R.bool.config_cecRcProfileSourceTopMenuNotHandled_allowed, + R.bool.config_cecRcProfileSourceTopMenuNotHandled_default); + + Setting rcProfileSourceMediaContextSensitiveMenu = registerSetting( + HdmiControlManager + .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU, + R.bool.config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable); + rcProfileSourceMediaContextSensitiveMenu.registerValue( + HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_HANDLED, + R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed, + R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default); + rcProfileSourceMediaContextSensitiveMenu.registerValue( + HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_NOT_HANDLED, + R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed, + R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default); + + verifySettings(); + } + + HdmiCecConfig(@NonNull Context context) { + this(context, new StorageAdapter(context)); + } + + private Setting registerSetting(@NonNull @CecSettingName String name, + int userConfResId) { + Setting setting = new Setting(mContext, name, userConfResId); + mSettings.put(name, setting); + return setting; + } + + private void verifySettings() { + for (Setting setting: mSettings.values()) { + // This will throw an exception when a setting + // doesn't have a default value assigned. + setting.getDefaultValue(); + getStorage(setting); + getStorageKey(setting); + } + } + + @Nullable + private Setting getSetting(@NonNull String name) { + return mSettings.containsKey(name) ? mSettings.get(name) : null; } @Storage @@ -322,7 +524,7 @@ public class HdmiCecConfig { .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU: return STORAGE_SHARED_PREFS; default: - throw new RuntimeException("Invalid CEC setting '" + setting.getName() + throw new VerificationException("Invalid CEC setting '" + setting.getName() + "' storage."); } } @@ -359,7 +561,7 @@ public class HdmiCecConfig { .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU: return setting.getName(); default: - throw new RuntimeException("Invalid CEC setting '" + setting.getName() + throw new VerificationException("Invalid CEC setting '" + setting.getName() + "' storage key."); } } @@ -396,10 +598,6 @@ public class HdmiCecConfig { } } - private int getIntValue(@NonNull Value value) { - return Integer.decode(value.getIntValue()); - } - private void notifyGlobalSettingChanged(String setting) { switch (setting) { case Global.HDMI_CONTROL_ENABLED: @@ -533,41 +731,20 @@ public class HdmiCecConfig { * Returns a list of all settings based on the XML metadata. */ public @CecSettingName List<String> getAllSettings() { - if (mSystemConfig == null) { - return new ArrayList<String>(); - } - List<String> allSettings = new ArrayList<String>(); - for (Setting setting : mSystemConfig.getSetting()) { - allSettings.add(setting.getName()); - } - return allSettings; + return new ArrayList<>(mSettings.keySet()); } /** * Returns a list of user-modifiable settings based on the XML metadata. */ public @CecSettingName List<String> getUserSettings() { - if (mSystemConfig == null) { - return new ArrayList<String>(); - } - Set<String> userSettings = new HashSet<String>(); - // First read from the system config. - for (Setting setting : mSystemConfig.getSetting()) { + List<String> settings = new ArrayList<>(); + for (Setting setting: mSettings.values()) { if (setting.getUserConfigurable()) { - userSettings.add(setting.getName()); - } - } - if (mVendorOverride != null) { - // Next either add or remove based on the vendor override. - for (Setting setting : mVendorOverride.getSetting()) { - if (setting.getUserConfigurable()) { - userSettings.add(setting.getName()); - } else { - userSettings.remove(setting.getName()); - } + settings.add(setting.getName()); } } - return new ArrayList(userSettings); + return settings; } /** @@ -607,7 +784,7 @@ public class HdmiCecConfig { + "' is not a string-type setting."); } List<String> allowedValues = new ArrayList<String>(); - for (Value allowedValue : setting.getAllowedValues().getValue()) { + for (Value allowedValue : setting.getAllowedValues()) { allowedValues.add(allowedValue.getStringValue()); } return allowedValues; @@ -626,8 +803,8 @@ public class HdmiCecConfig { + "' is not a string-type setting."); } List<Integer> allowedValues = new ArrayList<Integer>(); - for (Value allowedValue : setting.getAllowedValues().getValue()) { - allowedValues.add(getIntValue(allowedValue)); + for (Value allowedValue : setting.getAllowedValues()) { + allowedValues.add(allowedValue.getIntValue()); } return allowedValues; } @@ -659,7 +836,7 @@ public class HdmiCecConfig { throw new IllegalArgumentException("Setting '" + name + "' is not a string-type setting."); } - return getIntValue(getSetting(name).getDefaultValue()); + return getSetting(name).getDefaultValue().getIntValue(); } /** @@ -691,7 +868,7 @@ public class HdmiCecConfig { + "' is not a int-type setting."); } HdmiLogger.debug("Getting CEC setting value '" + name + "'."); - String defaultValue = Integer.toString(getIntValue(setting.getDefaultValue())); + String defaultValue = Integer.toString(setting.getDefaultValue().getIntValue()); String value = retrieveValue(setting, defaultValue); return Integer.parseInt(value); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index d8914b389191..bdc4e66cf7f9 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -500,7 +500,8 @@ abstract class HdmiCecLocalDevice { HdmiDeviceInfo cecDeviceInfo = mService.getHdmiCecNetwork().getCecDeviceInfo(address); // If no non-default display name is available for the device, request the devices OSD name. - if (cecDeviceInfo.getDisplayName().equals(HdmiUtils.getDefaultDeviceName(address))) { + if (cecDeviceInfo != null && cecDeviceInfo.getDisplayName().equals( + HdmiUtils.getDefaultDeviceName(address))) { mService.sendCecCommand( HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address)); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 7235a921254d..90d64339eac0 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -30,6 +30,7 @@ import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANAL import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; +import android.annotation.Nullable; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; @@ -1147,6 +1148,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { && getAvrDeviceInfo() != null; } + @Nullable @ServiceThreadOnly HdmiDeviceInfo getAvrDeviceInfo() { assertRunOnServiceThread(); @@ -1157,6 +1159,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return getSafeAvrDeviceInfo() != null; } + @Nullable HdmiDeviceInfo getSafeAvrDeviceInfo() { return mService.getHdmiCecNetwork().getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java index b748ae026cfc..7ceaa959212e 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java @@ -185,6 +185,12 @@ public class HdmiCecNetwork { mLocalDevices.clear(); } + /** + * Get the device info of a local device or a device in the CEC network by a device id. + * @param id id of the device to get + * @return the device with the given id, or {@code null} + */ + @Nullable public HdmiDeviceInfo getDeviceInfo(int id) { return mDeviceInfos.get(id); } @@ -717,6 +723,7 @@ public class HdmiCecNetwork { * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}. * Returns null if no logical address matched */ + @Nullable HdmiDeviceInfo getSafeCecDeviceInfo(int logicalAddress) { for (HdmiDeviceInfo info : mSafeAllDeviceInfos) { if (info.isCecDevice() && info.getLogicalAddress() == logicalAddress) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 115cafedca93..e6e2f9631d45 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -988,6 +988,7 @@ public class HdmiControlService extends SystemService { return mCecController.getVendorId(); } + @Nullable @ServiceThreadOnly HdmiDeviceInfo getDeviceInfo(int logicalAddress) { assertRunOnServiceThread(); diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java index 73ecbe0432b7..9d2db94cac8e 100644 --- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java +++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java @@ -16,6 +16,7 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback; import android.hardware.hdmi.IHdmiControlCallback; import android.util.Slog; @@ -62,8 +63,7 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { Slog.e(TAG, "Wrong arguments"); return null; } - return new OneTouchPlayAction(source, targetAddress, - callback); + return new OneTouchPlayAction(source, targetAddress, callback); } private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress, @@ -71,8 +71,8 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { this(localDevice, targetAddress, callback, localDevice.getDeviceInfo().getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0 - && localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo( - targetAddress).getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0); + && getTargetCecVersion(localDevice, targetAddress) + >= HdmiControlManager.HDMI_CEC_VERSION_2_0); } @VisibleForTesting @@ -88,9 +88,9 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { // Because only source device can create this action, it's safe to cast. mSource = source(); sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress)); - boolean targetOnBefore = localDevice().mService.getHdmiCecNetwork() - .getCecDeviceInfo(mTargetAddress).getDevicePowerStatus() - == HdmiControlManager.POWER_STATUS_ON; + + boolean targetOnBefore = getTargetDevicePowerStatus(mSource, mTargetAddress, + HdmiControlManager.POWER_STATUS_UNKNOWN) == HdmiControlManager.POWER_STATUS_ON; broadcastActiveSource(); // If the device is not an audio system itself, request the connected audio system to // turn on. @@ -98,8 +98,8 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { sendCommand(HdmiCecMessageBuilder.buildSystemAudioModeRequest(getSourceAddress(), Constants.ADDR_AUDIO_SYSTEM, getSourcePath(), true)); } - int targetPowerStatus = localDevice().mService.getHdmiCecNetwork() - .getCecDeviceInfo(mTargetAddress).getDevicePowerStatus(); + int targetPowerStatus = getTargetDevicePowerStatus(mSource, mTargetAddress, + HdmiControlManager.POWER_STATUS_UNKNOWN); if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) { queryDevicePowerStatus(); } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) { @@ -179,4 +179,23 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); } + private static int getTargetCecVersion(HdmiCecLocalDevice localDevice, + int targetLogicalAddress) { + HdmiDeviceInfo targetDevice = localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo( + targetLogicalAddress); + if (targetDevice != null) { + return targetDevice.getCecVersion(); + } + return HdmiControlManager.HDMI_CEC_VERSION_1_4_B; + } + + private static int getTargetDevicePowerStatus(HdmiCecLocalDevice localDevice, + int targetLogicalAddress, int defaultPowerStatus) { + HdmiDeviceInfo targetDevice = localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo( + targetLogicalAddress); + if (targetDevice != null) { + return targetDevice.getDevicePowerStatus(); + } + return defaultPowerStatus; + } } diff --git a/services/core/java/com/android/server/hdmi/cec_config.xml b/services/core/java/com/android/server/hdmi/cec_config.xml deleted file mode 100644 index 191e725181ca..000000000000 --- a/services/core/java/com/android/server/hdmi/cec_config.xml +++ /dev/null @@ -1,133 +0,0 @@ -<?xml version='1.0' encoding='utf-8' standalone='yes' ?> -<cec-settings> - <setting name="hdmi_cec_enabled" - value-type="int" - user-configurable="true"> - <allowed-values> - <value int-value="0" /> - <value int-value="1" /> - </allowed-values> - <default-value int-value="1" /> - </setting> - <setting name="hdmi_cec_version" - value-type="int" - user-configurable="true"> - <allowed-values> - <value int-value="0x05" /> - <value int-value="0x06" /> - </allowed-values> - <default-value int-value="0x05" /> - </setting> - <setting name="send_standby_on_sleep" - value-type="string" - user-configurable="true"> - <allowed-values> - <value string-value="to_tv" /> - <value string-value="broadcast" /> - <value string-value="none" /> - </allowed-values> - <default-value string-value="to_tv" /> - </setting> - <setting name="power_state_change_on_active_source_lost" - value-type="string" - user-configurable="true"> - <allowed-values> - <value string-value="none" /> - <value string-value="standby_now" /> - </allowed-values> - <default-value string-value="none" /> - </setting> - <setting name="system_audio_mode_muting" - value-type="int" - user-configurable="true"> - <allowed-values> - <value int-value="0" /> - <value int-value="1" /> - </allowed-values> - <default-value int-value="1" /> - </setting> - <setting name="volume_control_enabled" - value-type="int" - user-configurable="true"> - <allowed-values> - <value int-value="0" /> - <value int-value="1" /> - </allowed-values> - <default-value int-value="1" /> - </setting> - <setting name="tv_wake_on_one_touch_play" - value-type="int" - user-configurable="true"> - <allowed-values> - <value int-value="0" /> - <value int-value="1" /> - </allowed-values> - <default-value int-value="1" /> - </setting> - <setting name="tv_send_standby_on_sleep" - value-type="int" - user-configurable="true"> - <allowed-values> - <value int-value="0" /> - <value int-value="1" /> - </allowed-values> - <default-value int-value="1" /> - </setting> - <setting name="rc_profile_tv" - value-type="int" - user-configurable="false"> - <allowed-values> - <value int-value="0x0" /> - <value int-value="0x2" /> - <value int-value="0x6" /> - <value int-value="0xA" /> - <value int-value="0xE" /> - </allowed-values> - <default-value int-value="0x0" /> - </setting> - <setting name="rc_profile_source_handles_root_menu" - value-type="int" - user-configurable="false"> - <allowed-values> - <value int-value="0" /> - <value int-value="1" /> - </allowed-values> - <default-value int-value="1" /> - </setting> - <setting name="rc_profile_source_handles_setup_menu" - value-type="int" - user-configurable="false"> - <allowed-values> - <value int-value="0" /> - <value int-value="1" /> - </allowed-values> - <default-value int-value="1" /> - </setting> - <setting name="rc_profile_source_handles_contents_menu" - value-type="int" - user-configurable="false"> - <allowed-values> - <value int-value="0" /> - <value int-value="1" /> - </allowed-values> - <default-value int-value="0" /> - </setting> - <setting name="rc_profile_source_handles_top_menu" - value-type="int" - user-configurable="false"> - <allowed-values> - <value int-value="0" /> - <value int-value="1" /> - </allowed-values> - <default-value int-value="0" /> - </setting> - <setting name="rc_profile_source_handles_media_context_sensitive_menu" - value-type="int" - user-configurable="false"> - <allowed-values> - <value int-value="0" /> - <value int-value="1" /> - </allowed-values> - <default-value int-value="0" /> - </setting> -</cec-settings> diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 7dc9a0b2a364..cbe6e69cbef3 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -304,7 +304,7 @@ public class InputManagerService extends IInputManager.Stub int displayId, InputApplicationHandle application); private static native void nativeSetFocusedDisplay(long ptr, int displayId); private static native boolean nativeTransferTouchFocus(long ptr, - IBinder fromChannelToken, IBinder toChannelToken); + IBinder fromChannelToken, IBinder toChannelToken, boolean isDragDrop); private static native void nativeSetPointerSpeed(long ptr, int speed); private static native void nativeSetShowTouches(long ptr, boolean enabled); private static native void nativeSetInteractive(long ptr, boolean interactive); @@ -1727,12 +1727,14 @@ public class InputManagerService extends IInputManager.Stub * @param fromChannel The channel of a window that currently has touch focus. * @param toChannel The channel of the window that should receive touch focus in * place of the first. + * @param isDragDrop True if transfer touch focus for drag and drop. * @return True if the transfer was successful. False if the window with the * specified channel did not actually have touch focus at the time of the request. */ public boolean transferTouchFocus(@NonNull InputChannel fromChannel, - @NonNull InputChannel toChannel) { - return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken()); + @NonNull InputChannel toChannel, boolean isDragDrop) { + return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken(), + isDragDrop); } /** @@ -1752,7 +1754,8 @@ public class InputManagerService extends IInputManager.Stub @NonNull IBinder toChannelToken) { Objects.nonNull(fromChannelToken); Objects.nonNull(toChannelToken); - return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken); + return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken, + false /* isDragDrop */); } @Override // Binder call diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index a89cb5554825..672ed3d00344 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -5838,7 +5838,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @ShellCommandResult private int handleShellCommandTraceInputMethod(@NonNull ShellCommand shellCommand) { - int result = ImeTracing.getInstance().onShellCommand(shellCommand); + final String cmd = shellCommand.getNextArgRequired(); + final PrintWriter pw = shellCommand.getOutPrintWriter(); + switch (cmd) { + case "start": + ImeTracing.getInstance().getInstance().startTrace(pw); + break; + case "stop": + ImeTracing.getInstance().stopTrace(pw); + break; + default: + pw.println("Unknown command: " + cmd); + pw.println("Input method trace options:"); + pw.println(" start: Start tracing"); + pw.println(" stop: Stop tracing"); + return ShellCommandResult.FAILURE; + } boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled(); ArrayMap<IBinder, ClientState> clients; synchronized (mMethodMap) { @@ -5854,7 +5869,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } - return result; + return ShellCommandResult.SUCCESS; } /** diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 047e3b362b7a..ffea6a743a42 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -345,6 +345,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { */ private final Object mLock = new Object(); + /** List of {@link ScreenOnListener}s which do not belong to the default display. */ + private final SparseArray<ScreenOnListener> mScreenOnListeners = new SparseArray<>(); + Context mContext; IWindowManager mWindowManager; WindowManagerFuncs mWindowManagerFuncs; @@ -434,8 +437,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { volatile boolean mBeganFromNonInteractive; volatile boolean mEndCallKeyHandled; volatile boolean mCameraGestureTriggeredDuringGoingToSleep; - volatile boolean mGoingToSleep; - volatile boolean mRequestedOrGoingToSleep; + + /** + * {@code true} if the device is entering a low-power state; {@code false otherwise}. + * + * <p>This differs from {@link #mRequestedOrSleepingDefaultDisplay} which tracks the power state + * of the {@link #mDefaultDisplay default display} versus the power state of the entire device. + */ + volatile boolean mDeviceGoingToSleep; + + /** + * {@code true} if the {@link #mDefaultDisplay default display} is entering or was requested to + * enter a low-power state; {@code false otherwise}. + * + * <p>This differs from {@link #mDeviceGoingToSleep} which tracks the power state of the entire + * device versus the power state of the {@link #mDefaultDisplay default display}. + */ + // TODO(b/178103325): Track sleep/requested sleep for every display. + volatile boolean mRequestedOrSleepingDefaultDisplay; + volatile boolean mRecentsVisible; volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true; volatile boolean mPictureInPictureVisible; @@ -917,13 +937,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { case SHORT_PRESS_POWER_NOTHING: break; case SHORT_PRESS_POWER_GO_TO_SLEEP: - goToSleepFromPowerButton(eventTime, 0); + sleepDefaultDisplayFromPowerButton(eventTime, 0); break; case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP: - goToSleepFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); + sleepDefaultDisplayFromPowerButton(eventTime, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); break; case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME: - if (goToSleepFromPowerButton(eventTime, + if (sleepDefaultDisplayFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) { launchHomeFromHotKey(DEFAULT_DISPLAY); } @@ -951,11 +972,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** - * Sends the device to sleep as a result of a power button press. + * Sends the default display to sleep as a result of a power button press. * - * @return True if the was device was sent to sleep, false if sleep was suppressed. + * @return {@code true} if the device was sent to sleep, {@code false} if the device did not + * sleep. */ - private boolean goToSleepFromPowerButton(long eventTime, int flags) { + private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) { // Before we actually go to sleep, we check the last wakeup reason. // If the device very recently woke up from a gesture (like user lifting their device) // then ignore the sleep instruction. This is because users have developed @@ -975,12 +997,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags); + sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags); return true; } - private void goToSleep(long eventTime, int reason, int flags) { - mRequestedOrGoingToSleep = true; + private void sleepDefaultDisplay(long eventTime, int reason, int flags) { + mRequestedOrSleepingDefaultDisplay = true; mPowerManager.goToSleep(eventTime, reason, flags); } @@ -1017,7 +1039,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Global.THEATER_MODE_ON, 1); if (mGoToSleepOnButtonPressTheaterMode && interactive) { - goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); + sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, + 0); } } break; @@ -1126,7 +1149,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case SHORT_PRESS_SLEEP_GO_TO_SLEEP: case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME: Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)"); - goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0); + sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0); break; } } @@ -3511,7 +3534,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if ((mEndcallBehavior & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { - goToSleep(event.getEventTime(), + sleepDefaultDisplay(event.getEventTime(), PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); isWakeKey = false; } @@ -3538,10 +3561,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Any activity on the power button stops the accessibility shortcut result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately + final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState()); + final boolean interactiveAndOn = interactive && isDefaultDisplayOn; if (down) { - interceptPowerKeyDown(event, interactive); + interceptPowerKeyDown(event, interactiveAndOn); } else { - interceptPowerKeyUp(event, interactive, canceled); + interceptPowerKeyUp(event, interactiveAndOn, canceled); } break; } @@ -3746,7 +3771,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final MutableBoolean outLaunched = new MutableBoolean(false); final boolean gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive, outLaunched); - if (outLaunched.value && mRequestedOrGoingToSleep) { + if (outLaunched.value && mRequestedOrSleepingDefaultDisplay) { mCameraGestureTriggeredDuringGoingToSleep = true; } return gesturedServiceIntercepted; @@ -4088,8 +4113,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { pmSleepReason)) + ")"); } - mGoingToSleep = true; - mRequestedOrGoingToSleep = true; + mDeviceGoingToSleep = true; + mRequestedOrSleepingDefaultDisplay = true; if (mKeyguardDelegate != null) { mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason); @@ -4108,8 +4133,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000); - mGoingToSleep = false; - mRequestedOrGoingToSleep = false; + mDeviceGoingToSleep = false; + mRequestedOrSleepingDefaultDisplay = false; mDefaultDisplayPolicy.setAwake(false); // We must get this work done here because the power manager will drop @@ -4253,21 +4278,25 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Called on the DisplayManager's DisplayPowerController thread. @Override public void screenTurnedOff(int displayId) { - if (displayId != DEFAULT_DISPLAY) { - return; - } - - if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off..."); + if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off..."); - updateScreenOffSleepToken(true); - mDefaultDisplayPolicy.screenTurnedOff(); - synchronized (mLock) { - if (mKeyguardDelegate != null) { - mKeyguardDelegate.onScreenTurnedOff(); + if (displayId == DEFAULT_DISPLAY) { + updateScreenOffSleepToken(true); + mRequestedOrSleepingDefaultDisplay = false; + mDefaultDisplayPolicy.screenTurnedOff(); + synchronized (mLock) { + if (mKeyguardDelegate != null) { + mKeyguardDelegate.onScreenTurnedOff(); + } + } + mDefaultDisplayRotation.updateOrientationListener(); + reportScreenStateToVrManager(false); + if (mCameraGestureTriggeredDuringGoingToSleep) { + wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromPowerKey, + PowerManager.WAKE_REASON_CAMERA_LAUNCH, + "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK"); } } - mDefaultDisplayRotation.updateOrientationListener(); - reportScreenStateToVrManager(false); } private long getKeyguardDrawnTimeout() { @@ -4280,27 +4309,28 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Called on the DisplayManager's DisplayPowerController thread. @Override public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) { - if (displayId != DEFAULT_DISPLAY) { - return; - } - - if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on..."); + if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turning on..."); - Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */); - updateScreenOffSleepToken(false); - mDefaultDisplayPolicy.screenTurnedOn(screenOnListener); + if (displayId == DEFAULT_DISPLAY) { + Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", + 0 /* cookie */); + updateScreenOffSleepToken(false); + mDefaultDisplayPolicy.screenTurnedOn(screenOnListener); - synchronized (mLock) { - if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) { - mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT); - mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, - getKeyguardDrawnTimeout()); - mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback); - } else { - if (DEBUG_WAKEUP) Slog.d(TAG, - "null mKeyguardDelegate: setting mKeyguardDrawComplete."); - mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE); + synchronized (mLock) { + if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) { + mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT); + mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, + getKeyguardDrawnTimeout()); + mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback); + } else { + if (DEBUG_WAKEUP) Slog.d(TAG, + "null mKeyguardDelegate: setting mKeyguardDrawComplete."); + mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE); + } } + } else { + mScreenOnListeners.put(displayId, screenOnListener); } } @@ -4321,11 +4351,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) { + mWindowManagerFuncs.screenTurningOff(displayId, screenOffListener); if (displayId != DEFAULT_DISPLAY) { return; } - mWindowManagerFuncs.screenTurningOff(screenOffListener); + mRequestedOrSleepingDefaultDisplay = true; synchronized (mLock) { if (mKeyguardDelegate != null) { mKeyguardDelegate.onScreenTurningOff(); @@ -4380,6 +4411,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { listener.onScreenOn(); } + for (int i = mScreenOnListeners.size() - 1; i >= 0; i--) { + final ScreenOnListener screenOnListener = mScreenOnListeners.valueAt(i); + if (screenOnListener != null) { + screenOnListener.onScreenOn(); + } + } + mScreenOnListeners.clear(); + if (enableScreen) { try { mWindowManager.enableScreenIfNeeded(); @@ -4410,7 +4449,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public boolean okToAnimate() { - return mDefaultDisplayPolicy.isAwake() && !mGoingToSleep; + return mDefaultDisplayPolicy.isAwake() && !mDeviceGoingToSleep; } /** {@inheritDoc} */ @@ -4777,7 +4816,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWindowManagerFuncs.lockDeviceNow(); break; case LID_BEHAVIOR_SLEEP: - goToSleep(SystemClock.uptimeMillis(), + sleepDefaultDisplay(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); break; diff --git a/services/core/java/com/android/server/policy/SplashScreenSurface.java b/services/core/java/com/android/server/policy/SplashScreenSurface.java index b9202c334fec..72933a0ad309 100644 --- a/services/core/java/com/android/server/policy/SplashScreenSurface.java +++ b/services/core/java/com/android/server/policy/SplashScreenSurface.java @@ -45,7 +45,7 @@ class SplashScreenSurface implements StartingSurface { } @Override - public void remove() { + public void remove(boolean animate) { if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + mAppToken + ": " + this + " Callers=" + Debug.getCallers(4)); diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index b5a9acacec83..0735977be72a 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -238,8 +238,9 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { /** * Removes the starting window surface. Do not hold the window manager lock when calling * this method! + * @param animate Whether need to play the default exit animation for starting window. */ - void remove(); + void remove(boolean animate); } /** @@ -303,9 +304,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { /** * Notifies the window manager that screen is being turned off. * + * @param displayId the ID of the display which is turning off * @param listener callback to call when display can be turned off */ - void screenTurningOff(ScreenOffListener listener); + void screenTurningOff(int displayId, ScreenOffListener listener); /** * Convert the lid state to a human readable format. diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index aeeabe21460c..db3d7ad0c398 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -137,6 +137,7 @@ import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE; import static com.android.server.wm.ActivityRecordProto.DEFER_HIDING_CLIENT; import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT; import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK; +import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE; import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING; import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START; import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN; @@ -2114,7 +2115,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } if (abort) { - surface.remove(); + surface.remove(false /* prepareAnimation */); } } else { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Surface returned was null: %s", @@ -2128,7 +2129,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, TaskSnapshot snapshot) { - if (newTask || !processRunning || (taskSwitch && !activityCreated)) { + if ((newTask || !processRunning || (taskSwitch && !activityCreated)) + && !isActivityTypeHome()) { return STARTING_WINDOW_TYPE_SPLASH_SCREEN; } else if (taskSwitch && allowTaskSnapshot) { if (isSnapshotCompatible(snapshot)) { @@ -2179,7 +2181,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A + ActivityRecord.this + " state " + mTransferringSplashScreenState); if (isTransferringSplashScreen()) { mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH; - // TODO show default exit splash screen animation removeStartingWindow(); } } @@ -2196,6 +2197,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } private boolean transferSplashScreenIfNeeded() { + if (!mWmService.mStartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) { + return false; + } if (!mHandleExitSplashScreen || mStartingSurface == null || mStartingWindow == null || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH) { return false; @@ -2265,10 +2269,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // no matter what, remove the starting window. mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH; - removeStartingWindow(); + removeStartingWindowAnimation(false /* prepareAnimation */); } void removeStartingWindow() { + removeStartingWindowAnimation(true /* prepareAnimation */); + } + + void removeStartingWindowAnimation(boolean prepareAnimation) { if (transferSplashScreenIfNeeded()) { return; } @@ -2313,7 +2321,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mWmService.mAnimationHandler.post(() -> { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface); try { - surface.remove(); + surface.remove(prepareAnimation); } catch (Exception e) { Slog.w(TAG_WM, "Exception when removing starting window", e); } @@ -6190,7 +6198,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Remove orphaned starting window. if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this); mStartingWindowState = STARTING_WINDOW_REMOVED; - removeStartingWindow(); + removeStartingWindowAnimation(false /* prepareAnimation */); } if (isState(INITIALIZING) && !shouldBeVisible( true /* behindFullscreenActivity */, true /* ignoringKeyguard */)) { @@ -8255,6 +8263,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A proto.write(PROC_ID, app.getPid()); } proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled()); + proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode()); } @Override diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 32152ec85493..01f0359fa548 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1410,9 +1410,9 @@ public class DisplayPolicy { boolean localClient) { final InsetsState state = mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs); - final boolean inSizeCompatMode = WindowState.inSizeCompatMode(attrs, windowToken); - outInsetsState.set(state, inSizeCompatMode || localClient); - if (inSizeCompatMode) { + final boolean hasCompatScale = WindowState.hasCompatScale(attrs, windowToken); + outInsetsState.set(state, hasCompatScale || localClient); + if (hasCompatScale) { final float compatScale = windowToken != null ? windowToken.getSizeCompatScale() : mDisplayContent.mCompatibleScreenScale; diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index 627af9149fe5..1120a074aa8c 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -182,8 +182,6 @@ class DragDropController { if (SHOW_LIGHT_TRANSACTIONS) { Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag"); } - - mDragState.notifyLocationLocked(touchX, touchY); } finally { if (surface != null) { surface.release(); diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 0b3c065e0e73..08d5e800a808 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -515,50 +515,6 @@ class DragState { mTransaction.setPosition(mSurfaceControl, x - mThumbOffsetX, y - mThumbOffsetY).apply(); ProtoLog.i(WM_SHOW_TRANSACTIONS, "DRAG %s: pos=(%d,%d)", mSurfaceControl, (int) (x - mThumbOffsetX), (int) (y - mThumbOffsetY)); - - notifyLocationLocked(x, y); - } - - void notifyLocationLocked(float x, float y) { - // Tell the affected window - WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y); - if (touchedWin != null && !isWindowNotified(touchedWin)) { - // The drag point is over a window which was not notified about a drag start. - // Pretend it's over empty space. - touchedWin = null; - } - - try { - final int myPid = Process.myPid(); - - // have we dragged over a new window? - if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) { - if (DEBUG_DRAG) { - Slog.d(TAG_WM, "sending DRAG_EXITED to " + mTargetWindow); - } - // force DRAG_EXITED_EVENT if appropriate - DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED, - 0, 0, 0, 0, null, null, null, null, null, false); - mTargetWindow.mClient.dispatchDragEvent(evt); - if (myPid != mTargetWindow.mSession.mPid) { - evt.recycle(); - } - } - if (touchedWin != null) { - if (false && DEBUG_DRAG) { - Slog.d(TAG_WM, "sending DRAG_LOCATION to " + touchedWin); - } - DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION, - x, y, mThumbOffsetX, mThumbOffsetY, null, null, null, null, null, false); - touchedWin.mClient.dispatchDragEvent(evt); - if (myPid != touchedWin.mSession.mPid) { - evt.recycle(); - } - } - } catch (RemoteException e) { - Slog.w(TAG_WM, "can't send drag notification to windows"); - } - mTargetWindow = touchedWin; } /** diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 45c4233b40aa..35e54912b33e 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -30,7 +30,6 @@ import static com.android.server.wm.InsetsSourceProviderProto.CONTROLLABLE; import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET; import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL; import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET; -import static com.android.server.wm.InsetsSourceProviderProto.FINISH_SEAMLESS_ROTATE_FRAME_NUMBER; import static com.android.server.wm.InsetsSourceProviderProto.FRAME; import static com.android.server.wm.InsetsSourceProviderProto.IME_OVERRIDDEN_FRAME; import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING; @@ -59,6 +58,7 @@ import com.android.server.wm.SurfaceAnimator.AnimationType; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; import java.io.PrintWriter; +import java.util.function.Consumer; /** * Controller for a specific inset source on the server. It's called provider as it provides the @@ -84,6 +84,16 @@ class InsetsSourceProvider { private final Rect mImeOverrideFrame = new Rect(); private boolean mIsLeashReadyForDispatching; + private final Consumer<Transaction> mSetLeashPositionConsumer = t -> { + if (mControl != null) { + final SurfaceControl leash = mControl.getLeash(); + if (leash != null) { + final Point position = mControl.getSurfacePosition(); + t.setPosition(leash, position.x, position.y); + } + } + }; + /** The visibility override from the current controlling window. */ private boolean mClientVisible; @@ -149,7 +159,6 @@ class InsetsSourceProvider { // TODO: Ideally, we should wait for the animation to finish so previous window can // animate-out as new one animates-in. mWin.cancelAnimation(); - mWin.mPendingPositionChanged = null; mWin.mProvidedInsetsSources.remove(mSource.getType()); } ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win); @@ -248,31 +257,16 @@ class InsetsSourceProvider { if (mControl != null) { final Point position = getWindowFrameSurfacePosition(); if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) { - if (!mWin.getWindowFrames().didFrameSizeChange()) { - updateLeashPosition(-1 /* frameNumber */); - } else if (mWin.mInRelayout) { - updateLeashPosition(mWin.getFrameNumber()); + if (mWin.getWindowFrames().didFrameSizeChange()) { + mWin.applyWithNextDraw(mSetLeashPositionConsumer); } else { - mWin.mPendingPositionChanged = this; + mSetLeashPositionConsumer.accept(mWin.getPendingTransaction()); } mStateController.notifyControlChanged(mControlTarget); } } } - void updateLeashPosition(long frameNumber) { - if (mControl == null) { - return; - } - final SurfaceControl leash = mControl.getLeash(); - if (leash != null) { - final Transaction t = mDisplayContent.getPendingTransaction(); - final Point position = mControl.getSurfacePosition(); - t.setPosition(leash, position.x, position.y); - deferTransactionUntil(t, leash, frameNumber); - } - } - private Point getWindowFrameSurfacePosition() { final Rect frame = mWin.getFrame(); final Point position = new Point(); @@ -280,14 +274,6 @@ class InsetsSourceProvider { return position; } - private void deferTransactionUntil(Transaction t, SurfaceControl leash, long frameNumber) { - if (frameNumber >= 0) { - final SurfaceControl barrier = mWin.getClientViewRootSurface(); - t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber); - t.deferTransactionUntil(leash, barrier, frameNumber); - } - } - /** * @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget) */ diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java index ef4a40f4837c..6c4613526e88 100644 --- a/services/core/java/com/android/server/wm/StartingSurfaceController.java +++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java @@ -40,9 +40,8 @@ public class StartingSurfaceController { private static final String TAG = TAG_WITH_CLASS_NAME ? StartingSurfaceController.class.getSimpleName() : TAG_WM; /** Set to {@code true} to enable shell starting surface drawer. */ - private static final boolean DEBUG_ENABLE_SHELL_DRAWER = - SystemProperties.getBoolean("persist.debug.shell_starting_surface", false); - + static final boolean DEBUG_ENABLE_SHELL_DRAWER = + SystemProperties.getBoolean("persist.debug.shell_starting_surface", true); private final WindowManagerService mService; public StartingSurfaceController(WindowManagerService wm) { @@ -139,8 +138,9 @@ public class StartingSurfaceController { } @Override - public void remove() { - mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask); + public void remove(boolean animate) { + mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask, + animate); } } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 04a254c2aabf..a4b4726fe070 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4135,6 +4135,8 @@ class Task extends WindowContainer<WindowContainer> { final StartingWindowInfo info = new StartingWindowInfo(); info.taskInfo = getTaskInfo(); + info.isKeyguardOccluded = + mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY); final ActivityRecord topActivity = getTopMostActivity(); if (topActivity != null) { info.startingWindowTypeParameter = diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index fc6db61bdbcd..385dc79567fa 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -32,6 +32,7 @@ import android.app.WindowConfiguration; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ParceledListSlice; +import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; @@ -131,10 +132,28 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { }); } - void removeStartingWindow(Task task) { + void removeStartingWindow(Task task, boolean prepareAnimation) { mDeferTaskOrgCallbacksConsumer.accept(() -> { + SurfaceControl firstWindowLeash = null; + Rect mainFrame = null; + // TODO enable shift up animation once we fix flicker test +// final boolean playShiftUpAnimation = !task.inMultiWindowMode(); +// if (prepareAnimation && playShiftUpAnimation) { +// final ActivityRecord topActivity = task.topActivityWithStartingWindow(); +// if (topActivity != null) { +// final WindowState mainWindow = +// topActivity.findMainWindow(false/* includeStartingApp */); +// if (mainWindow != null) { + // TODO create proper leash instead of the copied SC +// firstWindowLeash = new SurfaceControl(mainWindow.getSurfaceControl(), +// "TaskOrganizerController.removeStartingWindow"); +// mainFrame = mainWindow.getRelativeFrame(); +// } +// } +// } try { - mTaskOrganizer.removeStartingWindow(task.mTaskId); + mTaskOrganizer.removeStartingWindow(task.mTaskId, firstWindowLeash, mainFrame, + prepareAnimation); } catch (RemoteException e) { Slog.e(TAG, "Exception sending onStartTaskFinished callback", e); } @@ -249,8 +268,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mOrganizer.addStartingWindow(t, appToken, launchTheme); } - void removeStartingWindow(Task t) { - mOrganizer.removeStartingWindow(t); + void removeStartingWindow(Task t, boolean prepareAnimation) { + mOrganizer.removeStartingWindow(t, prepareAnimation); } void copySplashScreenView(Task t) { @@ -495,14 +514,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return true; } - void removeStartingWindow(Task task) { + void removeStartingWindow(Task task, boolean prepareAnimation) { final Task rootTask = task.getRootTask(); if (rootTask == null || rootTask.mTaskOrganizer == null) { return; } final TaskOrganizerState state = mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder()); - state.removeStartingWindow(task); + state.removeStartingWindow(task, prepareAnimation); } boolean copySplashScreenView(Task task) { diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java index 9d35c25fc546..525420e045b5 100644 --- a/services/core/java/com/android/server/wm/TaskPositioningController.java +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -188,7 +188,8 @@ class TaskPositioningController { transferFocusFromWin = displayContent.mCurrentFocus; } if (!mInputManager.transferTouchFocus( - transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel)) { + transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel, + false /* isDragDrop */)) { Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus"); cleanUpTaskPositioner(); return false; diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index b810de99ee10..8915eba3d509 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -37,6 +37,7 @@ import android.os.Handler; import android.util.ArraySet; import android.util.Pair; import android.util.Slog; +import android.view.Display; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.ThreadedRenderer; @@ -624,7 +625,7 @@ class TaskSnapshotController { /** * Called when screen is being turned off. */ - void screenTurningOff(ScreenOffListener listener) { + void screenTurningOff(int displayId, ScreenOffListener listener) { if (shouldDisableSnapshots()) { listener.onScreenOff(); return; @@ -635,7 +636,7 @@ class TaskSnapshotController { try { synchronized (mService.mGlobalLock) { mTmpTasks.clear(); - mService.mRoot.forAllTasks(task -> { + mService.mRoot.getDisplayContent(displayId).forAllTasks(task -> { // Since RecentsAnimation will handle task snapshot while switching apps // with the best capture timing (e.g. IME window capture), No need // additional task capture while task is controlled by RecentsAnimation. @@ -645,7 +646,7 @@ class TaskSnapshotController { }); // Allow taking snapshot of home when turning screen off to reduce the delay of // waking from secure lock to home. - final boolean allowSnapshotHome = + final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY && mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId); snapshotTasks(mTmpTasks, allowSnapshotHome); } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 07610ab6d546..79a6bd7dcd2c 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -281,13 +281,14 @@ class TaskSnapshotSurface implements StartingSurface { } @Override - public void remove() { + public void remove(boolean animate) { synchronized (mService.mGlobalLock) { final long now = SystemClock.uptimeMillis(); if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS // Show the latest content as soon as possible for unlocking to home. && mActivityType != ACTIVITY_TYPE_HOME) { - mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS); + mHandler.postAtTime(() -> remove(false /* prepareAnimation */), + mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS); ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Defer removing snapshot surface in %dms", (now - mShownTime)); @@ -517,7 +518,7 @@ class TaskSnapshotSurface implements StartingSurface { // The orientation of the screen is changing. We better remove the snapshot ASAP as // we are going to wait on the new window in any case to unfreeze the screen, and // the starting window is not needed anymore. - sHandler.post(mOuter::remove); + sHandler.post(() -> mOuter.remove(false /* prepareAnimation */)); } if (reportDraw) { sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget(); diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java index 9245f8c3efe5..ffd6d21c1026 100644 --- a/services/core/java/com/android/server/wm/WindowFrames.java +++ b/services/core/java/com/android/server/wm/WindowFrames.java @@ -113,7 +113,7 @@ public class WindowFrames { } /** - * @return true if the width or height has changed since last reported to the client. + * @return true if the width or height has changed since last updating resizing window. */ boolean didFrameSizeChange() { return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height()); @@ -135,6 +135,13 @@ public class WindowFrames { } /** + * @return true if the width or height has changed since last reported to the client. + */ + boolean isFrameSizeChangeReported() { + return mFrameSizeChanged || didFrameSizeChange(); + } + + /** * Resets the size changed flags so they're all set to false again. This should be called * after the frames are reported to client. */ diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 7450782364f4..53ebfb2c6e0e 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -221,7 +221,8 @@ public abstract class WindowManagerInternal { DragState state, Display display, InputManagerService service, InputChannel source) { state.register(display); - return service.transferTouchFocus(source, state.getInputChannel()); + return service.transferTouchFocus(source, state.getInputChannel(), + true /* isDragDrop */); } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0a75924b0266..3007f0fed2a0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2234,11 +2234,6 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayContent dc = win.getDisplayContent(); - if (win.mPendingPositionChanged != null) { - win.mPendingPositionChanged.updateLeashPosition(frameNumber); - win.mPendingPositionChanged = null; - } - if (mUseBLASTSync && win.useBLASTSync() && viewVisibility != View.GONE) { win.prepareDrawHandlers(); result |= RELAYOUT_RES_BLAST_SYNC; @@ -2985,8 +2980,8 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void screenTurningOff(ScreenOffListener listener) { - mTaskSnapshotController.screenTurningOff(listener); + public void screenTurningOff(int displayId, ScreenOffListener listener) { + mTaskSnapshotController.screenTurningOff(displayId, listener); } @Override diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 4313ea4039fc..7ebc1cc6d5c1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -168,8 +168,8 @@ import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_ import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION; import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS; import static com.android.server.wm.WindowStateProto.GLOBAL_SCALE; +import static com.android.server.wm.WindowStateProto.HAS_COMPAT_SCALE; import static com.android.server.wm.WindowStateProto.HAS_SURFACE; -import static com.android.server.wm.WindowStateProto.IN_SIZE_COMPAT_MODE; import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN; import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY; import static com.android.server.wm.WindowStateProto.IS_VISIBLE; @@ -726,8 +726,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private InsetsState mFrozenInsetsState; - @Nullable InsetsSourceProvider mPendingPositionChanged; - private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; private KeyInterceptionInfo mKeyInterceptionInfo; @@ -774,6 +772,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP updateSurfacePosition(t); }; + private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> { + if (mSurfaceControl != null && mSurfaceControl.isValid()) { + t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); + } + }; + /** * @see #setSurfaceTranslationY(int) */ @@ -1089,18 +1093,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * scaling override set. * @see CompatModePackages#getCompatScale * @see android.content.res.CompatibilityInfo#supportsScreen - * @see ActivityRecord#inSizeCompatMode() + * @see ActivityRecord#hasSizeCompatBounds() */ - boolean inSizeCompatMode() { - return mOverrideScale != 1f || inSizeCompatMode(mAttrs, mActivityRecord); + boolean hasCompatScale() { + return mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord); } /** * @return {@code true} if the application runs in size compatibility mode. * @see android.content.res.CompatibilityInfo#supportsScreen - * @see ActivityRecord#inSizeCompatMode() + * @see ActivityRecord#hasSizeCompatBounds() */ - static boolean inSizeCompatMode(WindowManager.LayoutParams attrs, WindowToken windowToken) { + static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken windowToken) { return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0 || (windowToken != null && windowToken.hasSizeCompatBounds() // Exclude starting window because it is not displayed by the application. @@ -1305,7 +1309,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP windowFrames.offsetFrames(-layoutXDiff, -layoutYDiff); windowFrames.mCompatFrame.set(windowFrames.mFrame); - if (inSizeCompatMode()) { + if (hasCompatScale()) { // Also the scaled frame that we report to the app needs to be // adjusted to be in its coordinate space. windowFrames.mCompatFrame.scale(mInvGlobalScale); @@ -1577,7 +1581,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ InsetsState getCompatInsetsState() { InsetsState state = getInsetsState(); - if (inSizeCompatMode()) { + if (hasCompatScale()) { state = new InsetsState(state, true); state.scale(mInvGlobalScale); } @@ -1715,7 +1719,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } void prelayout() { - if (inSizeCompatMode()) { + if (hasCompatScale()) { if (mOverrideScale != 1f) { mGlobalScale = mToken.hasSizeCompatBounds() ? mToken.getSizeCompatScale() * mOverrideScale @@ -2129,6 +2133,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP : getTask().getWindowConfiguration().hasMovementAnimations(); if (mToken.okToAnimate() && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0 + && !mWindowFrames.didFrameSizeChange() + && !surfaceInsetsChanging() && !isDragResizing() && hasMovementAnimation && !mWinAnimator.mLastHidden @@ -3629,7 +3635,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP void fillClientWindowFrames(ClientWindowFrames outFrames) { outFrames.frame.set(mWindowFrames.mCompatFrame); outFrames.displayFrame.set(mWindowFrames.mDisplayFrame); - if (mInvGlobalScale != 1.0f && inSizeCompatMode()) { + if (mInvGlobalScale != 1.0f && hasCompatScale()) { outFrames.displayFrame.scale(mInvGlobalScale); } @@ -4029,7 +4035,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null); proto.write(FINISHED_SEAMLESS_ROTATION_FRAME, mFinishSeamlessRotateFrameNumber); proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate); - proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode()); + proto.write(HAS_COMPAT_SCALE, hasCompatScale()); proto.write(GLOBAL_SCALE, mGlobalScale); proto.end(token); } @@ -4131,7 +4137,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.println(prefix + "mHasSurface=" + mHasSurface + " isReadyForDisplay()=" + isReadyForDisplay() + " mWindowRemovalAllowed=" + mWindowRemovalAllowed); - if (inSizeCompatMode()) { + if (hasCompatScale()) { pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB)); } if (dumpAll) { @@ -4254,18 +4260,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float x, y; int w,h; - final boolean inSizeCompatMode = inSizeCompatMode(); + final boolean hasCompatScale = hasCompatScale(); if ((mAttrs.flags & FLAG_SCALED) != 0) { if (mAttrs.width < 0) { w = pw; - } else if (inSizeCompatMode) { + } else if (hasCompatScale) { w = (int)(mAttrs.width * mGlobalScale + .5f); } else { w = mAttrs.width; } if (mAttrs.height < 0) { h = ph; - } else if (inSizeCompatMode) { + } else if (hasCompatScale) { h = (int)(mAttrs.height * mGlobalScale + .5f); } else { h = mAttrs.height; @@ -4273,21 +4279,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } else { if (mAttrs.width == MATCH_PARENT) { w = pw; - } else if (inSizeCompatMode) { + } else if (hasCompatScale) { w = (int)(mRequestedWidth * mGlobalScale + .5f); } else { w = mRequestedWidth; } if (mAttrs.height == MATCH_PARENT) { h = ph; - } else if (inSizeCompatMode) { + } else if (hasCompatScale) { h = (int)(mRequestedHeight * mGlobalScale + .5f); } else { h = mRequestedHeight; } } - if (inSizeCompatMode) { + if (hasCompatScale) { x = mAttrs.x * mGlobalScale; y = mAttrs.y * mGlobalScale; } else { @@ -4315,7 +4321,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // We need to make sure we update the CompatFrame as it is used for // cropping decisions, etc, on systems where we lack a decor layer. windowFrames.mCompatFrame.set(windowFrames.mFrame); - if (inSizeCompatMode) { + if (hasCompatScale) { // See comparable block in computeFrameLw. windowFrames.mCompatFrame.scale(mInvGlobalScale); } @@ -4433,7 +4439,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float translateToWindowX(float x) { float winX = x - mWindowFrames.mFrame.left; - if (inSizeCompatMode()) { + if (hasCompatScale()) { winX *= mGlobalScale; } return winX; @@ -4441,7 +4447,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float translateToWindowY(float y) { float winY = y - mWindowFrames.mFrame.top; - if (inSizeCompatMode()) { + if (hasCompatScale()) { winY *= mGlobalScale; } return winY; @@ -5318,13 +5324,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // prior to the rotation. if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null && !mLastSurfacePosition.equals(mSurfacePosition)) { - t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y); + final boolean frameSizeChanged = mWindowFrames.isFrameSizeChangeReported(); + final boolean surfaceInsetsChanged = surfaceInsetsChanging(); + final boolean surfaceSizeChanged = frameSizeChanged || surfaceInsetsChanged; mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y); - if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) { + if (surfaceInsetsChanged) { mLastSurfaceInsets.set(mAttrs.surfaceInsets); - t.deferTransactionUntil(mSurfaceControl, - mWinAnimator.mSurfaceController.mSurfaceControl, - getFrameNumber()); + } + if (surfaceSizeChanged) { + applyWithNextDraw(mSetSurfacePositionConsumer); + } else { + mSetSurfacePositionConsumer.accept(t); } } } @@ -5372,7 +5382,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * scaled, the insets also need to be scaled for surface position in global coordinate. */ private void transformSurfaceInsetsPosition(Point outPos, Rect surfaceInsets) { - if (!inSizeCompatMode()) { + if (!hasCompatScale()) { outPos.x = surfaceInsets.left; outPos.y = surfaceInsets.top; return; diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 10705af9ac38..be06d0395499 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -1783,8 +1783,9 @@ static void nativeSetSystemUiLightsOut(JNIEnv* /* env */, jclass /* clazz */, jl im->setSystemUiLightsOut(lightsOut); } -static jboolean nativeTransferTouchFocus(JNIEnv* env, - jclass /* clazz */, jlong ptr, jobject fromChannelTokenObj, jobject toChannelTokenObj) { +static jboolean nativeTransferTouchFocus(JNIEnv* env, jclass /* clazz */, jlong ptr, + jobject fromChannelTokenObj, jobject toChannelTokenObj, + jboolean isDragDrop) { if (fromChannelTokenObj == nullptr || toChannelTokenObj == nullptr) { return JNI_FALSE; } @@ -1793,8 +1794,8 @@ static jboolean nativeTransferTouchFocus(JNIEnv* env, sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj); NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - if (im->getInputManager()->getDispatcher()->transferTouchFocus( - fromChannelToken, toChannelToken)) { + if (im->getInputManager()->getDispatcher()->transferTouchFocus(fromChannelToken, toChannelToken, + isDragDrop)) { return JNI_TRUE; } else { return JNI_FALSE; @@ -2267,7 +2268,7 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*)nativeRequestPointerCapture}, {"nativeSetInputDispatchMode", "(JZZ)V", (void*)nativeSetInputDispatchMode}, {"nativeSetSystemUiLightsOut", "(JZ)V", (void*)nativeSetSystemUiLightsOut}, - {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z", + {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;Z)Z", (void*)nativeTransferTouchFocus}, {"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed}, {"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches}, diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp index 6a8f6d419786..d43cf3f59170 100644 --- a/services/core/xsd/Android.bp +++ b/services/core/xsd/Android.bp @@ -45,13 +45,6 @@ xsd_config { } xsd_config { - name: "cec-config", - srcs: ["cec-config/cec-config.xsd"], - api_dir: "cec-config/schema", - package_name: "com.android.server.hdmi.cec.config", -} - -xsd_config { name: "device-state-config", srcs: ["device-state-config/device-state-config.xsd"], api_dir: "device-state-config/schema", diff --git a/services/core/xsd/cec-config/cec-config.xsd b/services/core/xsd/cec-config/cec-config.xsd deleted file mode 100644 index b59c93cce332..000000000000 --- a/services/core/xsd/cec-config/cec-config.xsd +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xs:schema version="2.0" - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element name="cec-settings"> - <xs:complexType> - <xs:sequence> - <xs:element name="setting" type="setting" minOccurs="0" maxOccurs="unbounded"/> - </xs:sequence> - </xs:complexType> - </xs:element> - <xs:complexType name="setting"> - <xs:attribute name="name" type="xs:string"/> - <xs:attribute name="value-type" type="xs:string"/> - <xs:attribute name="user-configurable" type="xs:boolean"/> - <xs:element name="allowed-values" type="value-list" minOccurs="1" maxOccurs="1"/> - <xs:element name="default-value" type="value" minOccurs="1" maxOccurs="1"/> - </xs:complexType> - <xs:complexType name="value-list"> - <xs:sequence> - <xs:element name="value" type="value" minOccurs="1" maxOccurs="unbounded"/> - </xs:sequence> - </xs:complexType> - <xs:complexType name="value"> - <xs:attribute name="string-value" type="xs:string"/> - <xs:attribute name="int-value" type="xs:string"/> - </xs:complexType> -</xs:schema> diff --git a/services/core/xsd/cec-config/schema/current.txt b/services/core/xsd/cec-config/schema/current.txt deleted file mode 100644 index 75872d4fb8a7..000000000000 --- a/services/core/xsd/cec-config/schema/current.txt +++ /dev/null @@ -1,44 +0,0 @@ -// Signature format: 2.0 -package com.android.server.hdmi.cec.config { - - public class CecSettings { - ctor public CecSettings(); - method public java.util.List<com.android.server.hdmi.cec.config.Setting> getSetting(); - } - - public class Setting { - ctor public Setting(); - method public com.android.server.hdmi.cec.config.ValueList getAllowedValues(); - method public com.android.server.hdmi.cec.config.Value getDefaultValue(); - method public String getName(); - method public boolean getUserConfigurable(); - method public String getValueType(); - method public void setAllowedValues(com.android.server.hdmi.cec.config.ValueList); - method public void setDefaultValue(com.android.server.hdmi.cec.config.Value); - method public void setName(String); - method public void setUserConfigurable(boolean); - method public void setValueType(String); - } - - public class Value { - ctor public Value(); - method public String getIntValue(); - method public String getStringValue(); - method public void setIntValue(String); - method public void setStringValue(String); - } - - public class ValueList { - ctor public ValueList(); - method public java.util.List<com.android.server.hdmi.cec.config.Value> getValue(); - } - - public class XmlParser { - ctor public XmlParser(); - method public static com.android.server.hdmi.cec.config.CecSettings read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - } - -} - diff --git a/services/core/xsd/cec-config/schema/last_current.txt b/services/core/xsd/cec-config/schema/last_current.txt deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/services/core/xsd/cec-config/schema/last_current.txt +++ /dev/null diff --git a/services/core/xsd/cec-config/schema/last_removed.txt b/services/core/xsd/cec-config/schema/last_removed.txt deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/services/core/xsd/cec-config/schema/last_removed.txt +++ /dev/null diff --git a/services/core/xsd/cec-config/schema/removed.txt b/services/core/xsd/cec-config/schema/removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/services/core/xsd/cec-config/schema/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1590ef1c4cb9..6e63454385c5 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2969,7 +2969,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private class DpmsUpgradeDataProvider implements PolicyUpgraderDataProvider { @Override - public boolean isUserDeviceOwner(int userId, ComponentName who) { + public boolean isDeviceOwner(int userId, ComponentName who) { return mOwners.isDeviceOwnerUserId(userId) && mOwners.getDeviceOwnerComponent().equals(who); } @@ -2999,14 +2999,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return component -> findAdmin(component, userId, /* throwForMissingPermission= */ false); } + + @Override + public int[] getUsersForUpgrade() { + List<UserInfo> allUsers = mUserManager.getUsers(); + return allUsers.stream().mapToInt(u -> u.id).toArray(); + } } private void performPolicyVersionUpgrade() { - List<UserInfo> allUsers = mUserManager.getUsers(); PolicyVersionUpgrader upgrader = new PolicyVersionUpgrader( new DpmsUpgradeDataProvider()); - upgrader.upgradePolicy(allUsers.stream().mapToInt(u -> u.id).toArray(), DPMS_VERSION); + upgrader.upgradePolicy(DPMS_VERSION); } private void revertTransferOwnershipIfNecessaryLocked() { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java index c20d0f5f5f5f..19a7659f4d60 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java @@ -33,7 +33,7 @@ public interface PolicyUpgraderDataProvider { * Returns true if the provided {@code userId} is a device owner. May affect some policy * defaults. */ - boolean isUserDeviceOwner(int userId, ComponentName who); + boolean isDeviceOwner(int userId, ComponentName who); /** * Returns true if the storage manager indicates file-based encryption is enabled. @@ -60,4 +60,9 @@ public interface PolicyUpgraderDataProvider { * user. */ Function<ComponentName, DeviceAdminInfo> getAdminInfoSupplier(int userId); + + /** + * Returns the users to upgrade. + */ + int[] getUsersForUpgrade(); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java index 2ab4b66a6831..6bc7ba6499ac 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java @@ -62,7 +62,7 @@ public class PolicyVersionUpgrader { * managed profile user IDs. * @param dpmsVersion The version to upgrade to. */ - public void upgradePolicy(int[] allUsers, int dpmsVersion) { + public void upgradePolicy(int dpmsVersion) { int oldVersion = readVersion(); if (oldVersion >= dpmsVersion) { Slog.i(LOG_TAG, String.format("Current version %d, latest version %d, not upgrading.", @@ -70,6 +70,8 @@ public class PolicyVersionUpgrader { return; } + final int[] allUsers = mProvider.getUsersForUpgrade(); + //NOTE: The current version is provided in case the XML file format changes in a // non-backwards-compatible way, so that DeviceAdminData could load it with // old tags, for example. @@ -94,7 +96,7 @@ public class PolicyVersionUpgrader { continue; } for (ActiveAdmin admin : userData.mAdminList) { - if (mProvider.isUserDeviceOwner(userId, admin.info.getComponent())) { + if (mProvider.isDeviceOwner(userId, admin.info.getComponent())) { Slog.i(LOG_TAG, String.format( "Marking Device Owner in user %d for permission grant ", userId)); admin.mAdminCanGrantSensorsPermissions = true; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java index 2fe47d3ff184..2fe2f40f34be 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java @@ -65,9 +65,10 @@ public class PolicyVersionUpgraderTest { Map<Integer, ComponentName> mUserToComponent = new HashMap<>(); Map<ComponentName, DeviceAdminInfo> mComponentToDeviceAdminInfo = new HashMap<>(); File mDataDir; + int[] mUsers; @Override - public boolean isUserDeviceOwner(int userId, ComponentName who) { + public boolean isDeviceOwner(int userId, ComponentName who) { return userId == mDeviceOwnerUserId && mDeviceOwnerComponent.equals(who); } @@ -105,6 +106,11 @@ public class PolicyVersionUpgraderTest { public Function<ComponentName, DeviceAdminInfo> getAdminInfoSupplier(int userId) { return componentName -> mComponentToDeviceAdminInfo.get(componentName); } + + @Override + public int[] getUsersForUpgrade() { + return mUsers; + } } private final Context mRealTestContext = InstrumentationRegistry.getTargetContext(); @@ -126,16 +132,17 @@ public class PolicyVersionUpgraderTest { ActivityInfo activityInfo = createActivityInfo(mFakeAdmin); DeviceAdminInfo dai = createDeviceAdminInfo(activityInfo); mProvider.mComponentToDeviceAdminInfo.put(mFakeAdmin, dai); + mProvider.mUsers = new int[] {0}; } @Test public void testSameVersionDoesNothing() throws IOException { - int[] users = new int[] {0}; writeVersionToXml(DevicePolicyManagerService.DPMS_VERSION); - preparePoliciesFile(users[0]); - String oldContents = readPoliciesFile(0); + final int userId = mProvider.mUsers[0]; + preparePoliciesFile(userId); + String oldContents = readPoliciesFile(userId); - mUpgrader.upgradePolicy(users, DevicePolicyManagerService.DPMS_VERSION); + mUpgrader.upgradePolicy(DevicePolicyManagerService.DPMS_VERSION); String newContents = readPoliciesFile(0); assertThat(newContents).isEqualTo(oldContents); @@ -144,18 +151,18 @@ public class PolicyVersionUpgraderTest { @Test public void testUpgrade0To1RemovesPasswordMetrics() throws IOException, XmlPullParserException { final String activePasswordTag = "active-password"; - int[] users = new int[] {0, 10}; + mProvider.mUsers = new int[] {0, 10}; writeVersionToXml(0); - for (int userId : users) { + for (int userId : mProvider.mUsers) { preparePoliciesFile(userId); } // Validate test set-up. assertThat(isTagPresent(readPoliciesFileToStream(0), activePasswordTag)).isTrue(); - mUpgrader.upgradePolicy(users, 1); + mUpgrader.upgradePolicy(1); assertThat(readVersionFromXml()).isGreaterThan(1); - for (int user: users) { + for (int user: mProvider.mUsers) { assertThat(isTagPresent(readPoliciesFileToStream(user), activePasswordTag)).isFalse(); } } @@ -163,21 +170,22 @@ public class PolicyVersionUpgraderTest { @Test public void testUpgrade1To2MarksDoForPermissionControl() throws IOException, XmlPullParserException { - int[] users = new int[] {0, 10}; + final int ownerUser = 10; + mProvider.mUsers = new int[] {0, ownerUser}; writeVersionToXml(1); - for (int userId : users) { + for (int userId : mProvider.mUsers) { preparePoliciesFile(userId); } - mProvider.mDeviceOwnerUserId = 10; + mProvider.mDeviceOwnerUserId = ownerUser; mProvider.mDeviceOwnerComponent = mFakeAdmin; - mProvider.mUserToComponent.put(10, mFakeAdmin); + mProvider.mUserToComponent.put(ownerUser, mFakeAdmin); - mUpgrader.upgradePolicy(users, 2); + mUpgrader.upgradePolicy(2); assertThat(readVersionFromXml()).isEqualTo(2); - assertThat(getBooleanValueTag(readPoliciesFileToStream(users[0]), + assertThat(getBooleanValueTag(readPoliciesFileToStream(mProvider.mUsers[0]), PERMISSIONS_TAG)).isFalse(); - assertThat(getBooleanValueTag(readPoliciesFileToStream(users[1]), + assertThat(getBooleanValueTag(readPoliciesFileToStream(ownerUser), PERMISSIONS_TAG)).isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java index 137bd88b1489..375704ee31bf 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java @@ -16,183 +16,206 @@ package com.android.server.hdmi; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + import android.annotation.NonNull; import android.content.Context; -import android.util.Slog; - -import com.android.server.hdmi.cec.config.CecSettings; -import com.android.server.hdmi.cec.config.XmlParser; - -import org.xmlpull.v1.XmlPullParserException; +import android.content.ContextWrapper; +import android.content.res.Resources; -import java.io.ByteArrayInputStream; -import java.io.IOException; - -import javax.xml.datatype.DatatypeConfigurationException; +import com.android.internal.R; /** - * Fake class which loads default system configuration with user-configurable + * Fake class which stubs default system configuration with user-configurable * settings (useful for testing). */ final class FakeHdmiCecConfig extends HdmiCecConfig { private static final String TAG = "FakeHdmiCecConfig"; - private static final String SYSTEM_CONFIG_XML = - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + " <setting name=\"power_state_change_on_active_source_lost\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"none\" />" - + " <value string-value=\"standby_now\" />" - + " </allowed-values>" - + " <default-value string-value=\"none\" />" - + " </setting>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"hdmi_cec_version\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0x05\" />" - + " <value int-value=\"0x06\" />" - + " </allowed-values>" - + " <default-value int-value=\"0x05\" />" - + " </setting>" - + " <setting name=\"system_audio_mode_muting\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"volume_control_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"tv_wake_on_one_touch_play\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"tv_send_standby_on_sleep\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"rc_profile_tv\"" - + " value-type=\"int\"" - + " user-configurable=\"false\">" - + " <allowed-values>" - + " <value int-value=\"0x0\" />" - + " <value int-value=\"0x2\" />" - + " <value int-value=\"0x6\" />" - + " <value int-value=\"0xA\" />" - + " <value int-value=\"0xE\" />" - + " </allowed-values>" - + " <default-value int-value=\"0x0\" />" - + " </setting>" - + " <setting name=\"rc_profile_source_handles_root_menu\"" - + " value-type=\"int\"" - + " user-configurable=\"false\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"rc_profile_source_handles_setup_menu\"" - + " value-type=\"int\"" - + " user-configurable=\"false\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"rc_profile_source_handles_contents_menu\"" - + " value-type=\"int\"" - + " user-configurable=\"false\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"0\" />" - + " </setting>" - + " <setting name=\"rc_profile_source_handles_top_menu\"" - + " value-type=\"int\"" - + " user-configurable=\"false\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"0\" />" - + " </setting>" - + " <setting name=\"rc_profile_source_handles_media_context_sensitive_menu\"" - + " value-type=\"int\"" - + " user-configurable=\"false\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"0\" />" - + " </setting>" - + "</cec-settings>"; + public static Context buildContext(Context context) { + Context contextSpy = spy(new ContextWrapper(context)); + doReturn(buildResources(context)).when(contextSpy).getResources(); + return contextSpy; + } - FakeHdmiCecConfig(@NonNull Context context) { - super(context, new StorageAdapter(context), parseFromString(SYSTEM_CONFIG_XML), null); + private static Resources buildResources(Context context) { + Resources resources = spy(context.getResources()); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecHdmiCecEnabled_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecHdmiCecControlEnabled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecHdmiCecControlEnabled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecHdmiCecControlDisabled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecHdmiCecControlDisabled_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecHdmiCecVersion_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecHdmiCecVersion14b_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecHdmiCecVersion14b_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecHdmiCecVersion20_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecHdmiCecVersion20_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecSendStandbyOnSleep_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecPowerControlModeTv_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecPowerControlModeTv_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecPowerControlModeBroadcast_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecPowerControlModeBroadcast_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecPowerControlModeNone_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecPowerControlModeNone_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecPowerStateChangeOnActiveSourceLost_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecSystemAudioModeMuting_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecSystemAudioModeMutingEnabled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecSystemAudioModeMutingEnabled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecSystemAudioModeMutingDisabled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecSystemAudioModeMutingDisabled_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecVolumeControlMode_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecVolumeControlModeEnabled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecVolumeControlModeEnabled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecVolumeControlModeDisabled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecVolumeControlModeDisabled_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecTvWakeOnOneTouchPlay_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecTvWakeOnOneTouchPlayEnabled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecTvWakeOnOneTouchPlayEnabled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecTvWakeOnOneTouchPlayDisabled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecTvWakeOnOneTouchPlayDisabled_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecTvSendStandbyOnSleep_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecTvSendStandbyOnSleepEnabled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecTvSendStandbyOnSleepEnabled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecTvSendStandbyOnSleepDisabled_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileTv_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileTvNone_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileTvNone_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileTvOne_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecRcProfileTvOne_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileTvTwo_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecRcProfileTvTwo_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileTvThree_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecRcProfileTvThree_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileTvFour_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecRcProfileTvFour_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceRootMenu_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceRootMenuHandled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceRootMenuHandled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceRootMenuNotHandled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceRootMenuNotHandled_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceSetupMenu_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceSetupMenuHandled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceSetupMenuHandled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceSetupMenuNotHandled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceSetupMenuNotHandled_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceContentsMenu_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceContentsMenuHandled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceContentsMenuHandled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceContentsMenuNotHandled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceContentsMenuNotHandled_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceTopMenu_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceTopMenuHandled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceTopMenuHandled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceTopMenuNotHandled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceTopMenuNotHandled_default); + + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed); + doReturn(false).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed); + doReturn(true).when(resources).getBoolean( + R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default); + + return resources; } - private static CecSettings parseFromString(@NonNull String configXml) { - CecSettings config = null; - try { - config = XmlParser.read( - new ByteArrayInputStream(configXml.getBytes())); - } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { - Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e); - } - return config; + FakeHdmiCecConfig(@NonNull Context context) { + super(buildContext(context), new StorageAdapter(context)); } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java index 798cf85957c0..c834510ba24c 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -27,6 +28,7 @@ import static org.testng.Assert.assertThrows; import android.annotation.NonNull; import android.content.Context; +import android.content.res.Resources; import android.hardware.hdmi.HdmiControlManager; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; @@ -35,6 +37,8 @@ import android.provider.Settings.Global; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.internal.R; + import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -61,265 +65,118 @@ public final class HdmiCecConfigTest { @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter; @Mock private HdmiCecConfig.SettingChangeListener mSettingChangeListener; + private void setBooleanResource(int resId, boolean value) { + Resources resources = mContext.getResources(); + doReturn(value).when(resources).getBoolean(resId); + } + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mContext = InstrumentationRegistry.getTargetContext(); - } - - @Test - public void getAllCecSettings_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThat(hdmiCecConfig.getAllSettings()).isEmpty(); - } - - @Test - public void getAllCecSettings_Empty() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); - assertThat(hdmiCecConfig.getAllSettings()).isEmpty(); + mContext = FakeHdmiCecConfig.buildContext(InstrumentationRegistry.getTargetContext()); } @Test public void getAllCecSettings_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"false\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getAllSettings()) .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, - HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); - } - - @Test - public void getUserCecSettings_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThat(hdmiCecConfig.getUserSettings()).isEmpty(); - } - - @Test - public void getUserCecSettings_Empty() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); - assertThat(hdmiCecConfig.getUserSettings()).isEmpty(); - } - - @Test - public void getUserCecSettings_OnlyMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, + HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU, + HdmiControlManager + .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU); + } + + @Test + public void getUserCecSettings_BasicSanity() { + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getUserSettings()) .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, - HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, + HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU, + HdmiControlManager + .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU); } @Test public void getUserCecSettings_WithOverride() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"false\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>"); + setBooleanResource(R.bool.config_cecHdmiCecEnabled_userConfigurable, false); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getUserSettings()) - .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); - } - - @Test - public void isStringValueType_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThrows(IllegalArgumentException.class, - () -> hdmiCecConfig.isStringValueType("foo")); + .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, + HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, + HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, + HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE, + HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, + HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU, + HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU, + HdmiControlManager + .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU); } @Test public void isStringValueType_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.isStringValueType("foo")); } @Test public void isStringValueType_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertTrue(hdmiCecConfig.isStringValueType( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)); } @Test - public void isIntValueType_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThrows(IllegalArgumentException.class, - () -> hdmiCecConfig.isIntValueType("foo")); - } - - @Test public void isIntValueType_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.isIntValueType("foo")); } @Test public void isIntValueType_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertTrue(hdmiCecConfig.isIntValueType( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)); } @Test - public void getAllowedStringValues_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThrows(IllegalArgumentException.class, - () -> hdmiCecConfig.getAllowedStringValues("foo")); - } - - @Test public void getAllowedStringValues_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getAllowedStringValues("foo")); } @Test public void getAllowedStringValues_InvalidValueType() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getAllowedStringValues( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)); @@ -327,21 +184,7 @@ public final class HdmiCecConfigTest { @Test public void getAllowedStringValues_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getAllowedStringValues( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)) .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV, @@ -350,41 +193,25 @@ public final class HdmiCecConfigTest { } @Test - public void getAllowedIntValues_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThrows(IllegalArgumentException.class, - () -> hdmiCecConfig.getAllowedIntValues("foo")); + public void getAllowedStringValues_WithOverride() { + setBooleanResource(R.bool.config_cecPowerControlModeNone_allowed, false); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); + assertThat(hdmiCecConfig.getAllowedStringValues( + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)) + .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV, + HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); } @Test public void getAllowedIntValues_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getAllowedIntValues("foo")); } @Test public void getAllowedIntValues_InvalidValueType() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getAllowedIntValues( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)); @@ -392,20 +219,7 @@ public final class HdmiCecConfigTest { @Test public void getAllowedIntValues_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getAllowedIntValues( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)) .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED, @@ -413,62 +227,24 @@ public final class HdmiCecConfigTest { } @Test - public void getAllowedIntValues_HexValues() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0x00\" />" - + " <value int-value=\"0x01\" />" - + " </allowed-values>" - + " <default-value int-value=\"0x01\" />" - + " </setting>" - + "</cec-settings>", null); + public void getAllowedIntValues_WithOverride() { + setBooleanResource(R.bool.config_cecHdmiCecControlDisabled_allowed, false); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getAllowedIntValues( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)) - .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED, - HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); - } - - @Test - public void getDefaultStringValue_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThrows(IllegalArgumentException.class, - () -> hdmiCecConfig.getDefaultStringValue("foo")); + .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); } @Test public void getDefaultStringValue_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getDefaultStringValue("foo")); } @Test public void getDefaultStringValue_InvalidValueType() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getDefaultStringValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)); @@ -476,62 +252,46 @@ public final class HdmiCecConfigTest { @Test public void getDefaultStringValue_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getDefaultStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)) .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV); } @Test - public void getDefaultIntValue_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThrows(IllegalArgumentException.class, - () -> hdmiCecConfig.getDefaultIntValue("foo")); + public void getDefaultStringValue_WithOverride() { + setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false); + setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); + assertThat(hdmiCecConfig.getDefaultStringValue( + HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)) + .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); + } + + @Test + public void getDefaultStringValue_MultipleDefaults() { + setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true); + assertThrows(RuntimeException.class, + () -> new HdmiCecConfig(mContext, mStorageAdapter)); + } + + @Test + public void getDefaultStringValue_NoDefault() { + setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false); + assertThrows(RuntimeException.class, + () -> new HdmiCecConfig(mContext, mStorageAdapter)); } @Test public void getDefaultIntValue_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getDefaultIntValue("foo")); } @Test public void getDefaultIntValue_InvalidValueType() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getDefaultIntValue( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)); @@ -539,81 +299,32 @@ public final class HdmiCecConfigTest { @Test public void getDefaultIntValue_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getDefaultIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)) .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); } @Test - public void getDefaultIntValue_HexValue() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0x00\" />" - + " <value int-value=\"0x01\" />" - + " </allowed-values>" - + " <default-value int-value=\"0x01\" />" - + " </setting>" - + "</cec-settings>", null); + public void getDefaultIntValue_WithOverride() { + setBooleanResource(R.bool.config_cecHdmiCecControlEnabled_default, false); + setBooleanResource(R.bool.config_cecHdmiCecControlDisabled_default, true); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getDefaultIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)) - .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); - } - - @Test - public void getStringValue_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThrows(IllegalArgumentException.class, - () -> hdmiCecConfig.getStringValue("foo")); + .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); } @Test public void getStringValue_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getStringValue("foo")); } @Test public void getStringValue_InvalidType() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getStringValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)); @@ -625,21 +336,7 @@ public final class HdmiCecConfigTest { Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, HdmiControlManager.POWER_CONTROL_MODE_TV)) .thenReturn(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)) .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); @@ -652,61 +349,22 @@ public final class HdmiCecConfigTest { HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE)) .thenReturn( HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW); - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"power_state_change_on_active_source_lost\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"none\" />" - + " <value string-value=\"standby_now\" />" - + " </allowed-values>" - + " <default-value string-value=\"none\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST)) .isEqualTo(HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW); } @Test - public void getIntValue_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThrows(IllegalArgumentException.class, - () -> hdmiCecConfig.getIntValue("foo")); - } - - @Test public void getIntValue_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getIntValue("foo")); } @Test public void getIntValue_InvalidType() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.getIntValue( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE)); @@ -718,45 +376,7 @@ public final class HdmiCecConfigTest { Global.HDMI_CONTROL_ENABLED, Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED))) .thenReturn(Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED)); - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); - assertThat(hdmiCecConfig.getIntValue( - HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)) - .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); - } - - @Test - public void getIntValue_GlobalSetting_HexValue() { - when(mStorageAdapter.retrieveGlobalSetting( - Global.HDMI_CONTROL_ENABLED, - Integer.toHexString(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED))) - .thenReturn(Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED)); - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0x0\" />" - + " <value int-value=\"0x1\" />" - + " </allowed-values>" - + " <default-value int-value=\"0x1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED)) .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); @@ -768,61 +388,23 @@ public final class HdmiCecConfigTest { HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_ENABLED))) .thenReturn(Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED)); - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"system_audio_mode_muting\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThat(hdmiCecConfig.getIntValue( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING)) .isEqualTo(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); } @Test - public void setStringValue_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThrows(IllegalArgumentException.class, - () -> hdmiCecConfig.setStringValue("foo", "bar")); - } - - @Test public void setStringValue_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.setStringValue("foo", "bar")); } @Test public void setStringValue_NotConfigurable() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"false\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + setBooleanResource(R.bool.config_cecSendStandbyOnSleep_userConfigurable, false); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.setStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, @@ -831,21 +413,7 @@ public final class HdmiCecConfigTest { @Test public void setStringValue_InvalidValue() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.setStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, @@ -854,21 +422,7 @@ public final class HdmiCecConfigTest { @Test public void setStringValue_GlobalSetting_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"send_standby_on_sleep\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"to_tv\" />" - + " <value string-value=\"broadcast\" />" - + " <value string-value=\"none\" />" - + " </allowed-values>" - + " <default-value string-value=\"to_tv\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); hdmiCecConfig.setStringValue(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE, HdmiControlManager.POWER_CONTROL_MODE_BROADCAST); verify(mStorageAdapter).storeGlobalSetting( @@ -878,20 +432,7 @@ public final class HdmiCecConfigTest { @Test public void setStringValue_SharedPref_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"power_state_change_on_active_source_lost\"" - + " value-type=\"string\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value string-value=\"none\" />" - + " <value string-value=\"standby_now\" />" - + " </allowed-values>" - + " <default-value string-value=\"none\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); hdmiCecConfig.setStringValue( HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST, HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW); @@ -901,40 +442,16 @@ public final class HdmiCecConfigTest { } @Test - public void setIntValue_NoMasterXml() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, null, null); - assertThrows(IllegalArgumentException.class, - () -> hdmiCecConfig.setIntValue("foo", 0)); - } - - @Test public void setIntValue_InvalidSetting() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.setIntValue("foo", 0)); } @Test public void setIntValue_NotConfigurable() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"false\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + setBooleanResource(R.bool.config_cecHdmiCecEnabled_userConfigurable, false); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, @@ -943,20 +460,7 @@ public final class HdmiCecConfigTest { @Test public void setIntValue_InvalidValue() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); assertThrows(IllegalArgumentException.class, () -> hdmiCecConfig.setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, @@ -965,43 +469,7 @@ public final class HdmiCecConfigTest { @Test public void setIntValue_GlobalSetting_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); - hdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, - HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); - verify(mStorageAdapter).storeGlobalSetting( - Global.HDMI_CONTROL_ENABLED, - Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED)); - } - - @Test - public void setIntValue_GlobalSetting_HexValue() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0x0\" />" - + " <value int-value=\"0x1\" />" - + " </allowed-values>" - + " <default-value int-value=\"0x1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); hdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); verify(mStorageAdapter).storeGlobalSetting( @@ -1011,20 +479,7 @@ public final class HdmiCecConfigTest { @Test public void setIntValue_SharedPref_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"system_audio_mode_muting\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); hdmiCecConfig.setIntValue( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); @@ -1035,20 +490,7 @@ public final class HdmiCecConfigTest { @Test public void registerChangeListener_SharedPref_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"system_audio_mode_muting\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); hdmiCecConfig.registerChangeListener( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, mSettingChangeListener); @@ -1061,20 +503,7 @@ public final class HdmiCecConfigTest { @Test public void removeChangeListener_SharedPref_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"system_audio_mode_muting\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); hdmiCecConfig.registerChangeListener( HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, mSettingChangeListener); @@ -1100,20 +529,7 @@ public final class HdmiCecConfigTest { String originalValue = Global.getString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED); try { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); + HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter); hdmiCecConfig.registerGlobalSettingsObserver(mTestLooper.getLooper()); HdmiCecConfig.SettingChangeListener latchUpdateListener = new HdmiCecConfig.SettingChangeListener() { diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java index 907cf3eb1f70..c61635cbd4b6 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java @@ -136,7 +136,6 @@ public class OneTouchPlayActionTest { mPhysicalAddress = 0x2000; mNativeWrapper.setPhysicalAddress(mPhysicalAddress); mTestLooper.dispatchAll(); - mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV); } private OneTouchPlayAction createOneTouchPlayAction(HdmiCecLocalDevicePlayback device, @@ -147,7 +146,47 @@ public class OneTouchPlayActionTest { } @Test + public void succeedWithUnknownTvDevice() { + HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback( + mHdmiControlService); + playbackDevice.init(); + mLocalDevices.add(playbackDevice); + mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + + TestActionTimer actionTimer = new TestActionTimer(); + TestCallback callback = new TestCallback(); + OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback, + false); + playbackDevice.addAndStartAction(action); + mTestLooper.dispatchAll(); + + HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource( + playbackDevice.mAddress, mPhysicalAddress); + HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress, + ADDR_TV); + HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder + .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV); + + assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn); + assertThat(mNativeWrapper.getResultMessages()).contains(activeSource); + assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus); + mNativeWrapper.clearResultMessages(); + assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS); + HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage( + ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON); + action.processCommand(reportPowerStatusOn); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn); + assertThat(mNativeWrapper.getResultMessages()).contains(activeSource); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus); + assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); + } + + @Test public void succeedAfterGettingPowerStatusOn_Cec14b() { + mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV); HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback( mHdmiControlService); playbackDevice.init(); @@ -187,6 +226,7 @@ public class OneTouchPlayActionTest { @Test public void succeedAfterGettingTransientPowerStatus_Cec14b() { + mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV); HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback( mHdmiControlService); playbackDevice.init(); @@ -236,6 +276,7 @@ public class OneTouchPlayActionTest { @Test public void timeOut_Cec14b() { + mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV); HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback( mHdmiControlService); playbackDevice.init(); @@ -276,6 +317,7 @@ public class OneTouchPlayActionTest { @Test public void succeedIfPowerStatusOn_Cec20() { + mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV); HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback( mHdmiControlService); playbackDevice.init(); @@ -307,6 +349,7 @@ public class OneTouchPlayActionTest { @Test public void succeedIfPowerStatusUnknown_Cec20() { + mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV); HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback( mHdmiControlService); playbackDevice.init(); @@ -348,6 +391,7 @@ public class OneTouchPlayActionTest { @Test public void succeedIfPowerStatusStandby_Cec20() { + mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV); HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback( mHdmiControlService); playbackDevice.init(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index d8e7582633de..c19f3489898d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -159,6 +159,11 @@ public class ActivityRecordTests extends WindowTestsBase { setBooted(mAtm); } + private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() { + return new TestStartingWindowOrganizer(mAtm, + mSystemServicesTestRule.getPowerManagerWrapper()); + } + @Test public void testStackCleanupOnClearingTask() { final ActivityRecord activity = createActivityWith2LevelTask(); @@ -2294,6 +2299,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testCreateRemoveStartingWindow() { + registerTestStartingWindowOrganizer(); final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, @@ -2307,6 +2313,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testAddRemoveRace() { + registerTestStartingWindowOrganizer(); // There was once a race condition between adding and removing starting windows final ActivityRecord appToken = new ActivityBuilder(mAtm).setCreateTask(true).build(); for (int i = 0; i < 1000; i++) { @@ -2321,6 +2328,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testTransferStartingWindow() { + registerTestStartingWindowOrganizer(); final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity1.addStartingWindow(mPackageName, @@ -2337,9 +2345,10 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testTransferStartingWindowWhileCreating() { + final TestStartingWindowOrganizer organizer = registerTestStartingWindowOrganizer(); final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); - ((TestWindowManagerPolicy) activity1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen( + organizer.setRunnableWhenAddingSplashScreen( () -> { // Surprise, ...! Transfer window in the middle of the creation flow. activity2.addStartingWindow(mPackageName, @@ -2357,6 +2366,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testTransferStartingWindowCanAnimate() { + registerTestStartingWindowOrganizer(); final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity1.addStartingWindow(mPackageName, @@ -2380,6 +2390,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testTransferStartingWindowFromFinishingActivity() { + registerTestStartingWindowOrganizer(); final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final Task task = activity.getTask(); activity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */, @@ -2423,6 +2434,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testTransferStartingWindowSetFixedRotation() { + registerTestStartingWindowOrganizer(); final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); final Task task = activity.getTask(); final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); @@ -2454,6 +2466,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testTryTransferStartingWindowFromHiddenAboveToken() { + registerTestStartingWindowOrganizer(); // Add two tasks on top of each other. final ActivityRecord activityTop = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityRecord activityBottom = new ActivityBuilder(mAtm).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 8703c3103607..7f9e7da99579 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -154,7 +154,7 @@ public class DragDropControllerTests extends WindowTestsBase { mWindow = createDropTargetWindow("Drag test window", 0); doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0); when(mWm.mInputManager.transferTouchFocus(any(InputChannel.class), - any(InputChannel.class))).thenReturn(true); + any(InputChannel.class), any(boolean.class))).thenReturn(true); mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow); } @@ -370,7 +370,7 @@ public class DragDropControllerTests extends WindowTestsBase { .build(); assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(), - new InputChannel())); + new InputChannel(), true /* isDragDrop */)); mToken = mTarget.performDrag(0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, data); assertNotNull(mToken); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java index 75226b7e66f7..8bc4cedf6fce 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -34,7 +34,6 @@ import static org.junit.Assert.assertTrue; import android.platform.test.annotations.Presubmit; import android.view.InputChannel; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -63,7 +62,8 @@ public class TaskPositioningControllerTests extends WindowTestsBase { when(mWm.mInputManager.transferTouchFocus( any(InputChannel.class), - any(InputChannel.class))).thenReturn(true); + any(InputChannel.class), + any(boolean.class))).thenReturn(true); mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window"); mWindow.getTask().setResizeMode(RESIZE_MODE_RESIZEABLE); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index 86d8eee878fd..7822a8514a13 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -119,7 +119,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { mRunnableWhenAddingSplashScreen.run(); mRunnableWhenAddingSplashScreen = null; } - return () -> { + return (a) -> { synchronized (wm.mGlobalLock) { activity.removeChild(window); activity.mStartingWindow = null; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 2c2c09a5750a..01c503e01326 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -547,7 +547,8 @@ public class WindowOrganizerTests extends WindowTestsBase { } @Override - public void removeStartingWindow(int taskId) { } + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { } @Override public void copySplashScreenView(int taskId) { } @@ -614,7 +615,8 @@ public class WindowOrganizerTests extends WindowTestsBase { } @Override - public void removeStartingWindow(int taskId) { } + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { } @Override public void copySplashScreenView(int taskId) { } @Override @@ -688,7 +690,8 @@ public class WindowOrganizerTests extends WindowTestsBase { } @Override - public void removeStartingWindow(int taskId) { } + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { } @Override public void copySplashScreenView(int taskId) { } @Override @@ -832,7 +835,8 @@ public class WindowOrganizerTests extends WindowTestsBase { @Override public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { } @Override - public void removeStartingWindow(int taskId) { } + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { } @Override public void copySplashScreenView(int taskId) { } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 827ff6c18a68..4a7784cf1b36 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -36,6 +36,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_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_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -50,6 +51,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; @@ -67,6 +69,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Build; import android.os.Bundle; @@ -74,6 +77,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.service.voice.IVoiceInteractionSession; +import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; import android.view.IDisplayWindowInsetsController; @@ -100,6 +104,7 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.HashMap; /** Common base class for window manager unit test classes. */ class WindowTestsBase extends SystemServiceTestsBase { @@ -1123,6 +1128,88 @@ class WindowTestsBase extends SystemServiceTestsBase { } } + static class TestStartingWindowOrganizer extends ITaskOrganizer.Stub { + private final ActivityTaskManagerService mAtm; + private final WindowManagerService mWMService; + private final WindowState.PowerManagerWrapper mPowerManagerWrapper; + + private Runnable mRunnableWhenAddingSplashScreen; + private final SparseArray<IBinder> mTaskAppMap = new SparseArray<>(); + private final HashMap<IBinder, WindowState> mAppWindowMap = new HashMap<>(); + + TestStartingWindowOrganizer(ActivityTaskManagerService service, + WindowState.PowerManagerWrapper powerManagerWrapper) { + mAtm = service; + mWMService = mAtm.mWindowManager; + mPowerManagerWrapper = powerManagerWrapper; + if (DEBUG_ENABLE_SHELL_DRAWER) { + mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run); + mAtm.mTaskOrganizerController.registerTaskOrganizer(this); + } + } + + void setRunnableWhenAddingSplashScreen(Runnable r) { + if (DEBUG_ENABLE_SHELL_DRAWER) { + mRunnableWhenAddingSplashScreen = r; + } else { + ((TestWindowManagerPolicy) mWMService.mPolicy).setRunnableWhenAddingSplashScreen(r); + } + } + + @Override + public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { + synchronized (mWMService.mGlobalLock) { + final ActivityRecord activity = mWMService.mRoot.getActivityRecord( + appToken); + IWindow iWindow = mock(IWindow.class); + doReturn(mock(IBinder.class)).when(iWindow).asBinder(); + final WindowState window = WindowTestsBase.createWindow(null, + TYPE_APPLICATION_STARTING, activity, + "Starting window", 0 /* ownerId */, 0 /* userId*/, + false /* internalWindows */, mWMService, mock(Session.class), + iWindow, + mPowerManagerWrapper); + activity.mStartingWindow = window; + mAppWindowMap.put(appToken, window); + mTaskAppMap.put(info.taskInfo.taskId, appToken); + } + if (mRunnableWhenAddingSplashScreen != null) { + mRunnableWhenAddingSplashScreen.run(); + mRunnableWhenAddingSplashScreen = null; + } + } + @Override + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { + synchronized (mWMService.mGlobalLock) { + final IBinder appToken = mTaskAppMap.get(taskId); + if (appToken != null) { + mTaskAppMap.remove(taskId); + final ActivityRecord activity = mWMService.mRoot.getActivityRecord( + appToken); + WindowState win = mAppWindowMap.remove(appToken); + activity.removeChild(win); + activity.mStartingWindow = null; + } + } + } + @Override + public void copySplashScreenView(int taskId) { + } + @Override + public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { + } + @Override + public void onTaskVanished(ActivityManager.RunningTaskInfo info) { + } + @Override + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { + } + @Override + public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) { + } + } + static class TestSplitOrganizer extends ITaskOrganizer.Stub { final ActivityTaskManagerService mService; Task mPrimary; @@ -1161,7 +1248,8 @@ class WindowTestsBase extends SystemServiceTestsBase { public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { } @Override - public void removeStartingWindow(int taskId) { + public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, + boolean playRevealAnimation) { } @Override public void copySplashScreenView(int taskId) { diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 705e93f8883c..1a71f808daa7 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -27,6 +27,7 @@ import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; +import android.net.ipsec.ike.SaProposal; import android.os.Build; import android.os.PersistableBundle; import android.os.RemoteException; @@ -4170,9 +4171,11 @@ public class CarrierConfigManager { KEY_PREFIX + "child_sa_rekey_soft_timer_sec_int"; /** - * Supported DH groups for IKE negotiation. Possible values are {@link #DH_GROUP_NONE}, - * {@link #DH_GROUP_1024_BIT_MODP}, {@link #DH_GROUP_1536_BIT_MODP}, {@link - * #DH_GROUP_2048_BIT_MODP} + * Supported DH groups for IKE negotiation. Possible values are: + * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_NONE}, + * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1024_BIT_MODP}, + * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1536_BIT_MODP}, + * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_2048_BIT_MODP} */ public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY = KEY_PREFIX + "diffie_hellman_groups_int_array"; @@ -4208,23 +4211,29 @@ public class CarrierConfigManager { /** * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of child - * session. Possible values are {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, {@link - * #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256} + * session. Possible values are: + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256} */ public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = KEY_PREFIX + "child_session_aes_cbc_key_size_int_array"; /** * List of supported key sizes for AES Counter (CTR) encryption mode of child session. - * Possible values are {@link #KEY_LEN_UNUSED}, - * {@link #KEY_LEN_AES_128}, {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256} + * Possible values are: + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256} */ public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = KEY_PREFIX + "child_session_aes_ctr_key_size_int_array"; /** * List of supported encryption algorithms for child session. Possible values are - * {@link #ENCRYPTION_ALGORITHM_AES_CBC} + * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC} */ public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array"; @@ -4245,8 +4254,11 @@ public class CarrierConfigManager { /** * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of IKE - * session. Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, {@link - * #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256} + * session. Possible values: + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256} */ public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = KEY_PREFIX + "ike_session_encryption_aes_cbc_key_size_int_array"; @@ -4254,24 +4266,31 @@ public class CarrierConfigManager { /** * List of supported key sizes for AES Counter (CTR) encryption mode of IKE session. - * Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, - * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256} + * Possible values - + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192}, + * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256} */ public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = KEY_PREFIX + "ike_session_encryption_aes_ctr_key_size_int_array"; /** * List of supported encryption algorithms for IKE session. Possible values are - * {@link #ENCRYPTION_ALGORITHM_AES_CBC}, {@link #ENCRYPTION_ALGORITHM_AES_CTR} + * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC}, + * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR} */ public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = KEY_PREFIX + "supported_ike_session_encryption_algorithms_int_array"; /** - * List of supported integrity algorithms for IKE session Possible values are {@link - * #INTEGRITY_ALGORITHM_NONE}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA1_96}, {@link - * #INTEGRITY_ALGORITHM_AES_XCBC_96}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_256_128}, {@link - * #INTEGRITY_ALGORITHM_HMAC_SHA2_384_192}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_512_256} + * List of supported integrity algorithms for IKE session. Possible values are + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_NONE}, + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA1_96}, + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_AES_XCBC_96}, + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_256_128}, + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_384_192}, + * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_512_256} */ public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = KEY_PREFIX + "supported_integrity_algorithms_int_array"; @@ -4291,9 +4310,11 @@ public class CarrierConfigManager { /** * List of supported pseudo random function algorithms for IKE session. Possible values are - * {@link #PSEUDORANDOM_FUNCTION_HMAC_SHA1}, {@link #PSEUDORANDOM_FUNCTION_AES128_XCBC}, - * {@link #PSEUDORANDOM_FUNCTION_SHA2_256}, {@link #PSEUDORANDOM_FUNCTION_SHA2_384}, - * {@link #PSEUDORANDOM_FUNCTION_SHA2_512} + * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_HMAC_SHA1}, + * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_AES128_XCBC}, + * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_256}, + * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_384}, + * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_512} */ public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = KEY_PREFIX + "supported_prf_algorithms_int_array"; @@ -4366,182 +4387,6 @@ public class CarrierConfigManager { public static final int EPDG_ADDRESS_CELLULAR_LOC = 3; /** @hide */ - @IntDef({KEY_LEN_UNUSED, KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256}) - public @interface EncrpytionKeyLengthType {} - - public static final int KEY_LEN_UNUSED = 0; - /** AES Encryption/Ciphering Algorithm key length 128 bits. */ - public static final int KEY_LEN_AES_128 = 128; - /** AES Encryption/Ciphering Algorithm key length 192 bits. */ - public static final int KEY_LEN_AES_192 = 192; - /** AES Encryption/Ciphering Algorithm key length 256 bits. */ - public static final int KEY_LEN_AES_256 = 256; - - /** @hide */ - @IntDef({ - DH_GROUP_NONE, - DH_GROUP_1024_BIT_MODP, - DH_GROUP_1536_BIT_MODP, - DH_GROUP_2048_BIT_MODP, - DH_GROUP_3072_BIT_MODP, - DH_GROUP_4096_BIT_MODP - }) - public @interface DhGroup {} - - /** None Diffie-Hellman Group. */ - public static final int DH_GROUP_NONE = 0; - /** - * 1024-bit MODP Diffie-Hellman Group. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int DH_GROUP_1024_BIT_MODP = 2; - /** - * 1536-bit MODP Diffie-Hellman Group. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int DH_GROUP_1536_BIT_MODP = 5; - /** - * 2048-bit MODP Diffie-Hellman Group. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int DH_GROUP_2048_BIT_MODP = 14; - /** - * 3072-bit MODP Diffie-Hellman Group. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int DH_GROUP_3072_BIT_MODP = 15; - /** - * 4096-bit MODP Diffie-Hellman Group. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int DH_GROUP_4096_BIT_MODP = 16; - - /** @hide */ - @IntDef({ENCRYPTION_ALGORITHM_AES_CBC, ENCRYPTION_ALGORITHM_AES_CTR}) - public @interface EncryptionAlgorithm {} - - /** - * AES-CBC Encryption/Ciphering Algorithm. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; - - /** - * AES-CTR Encryption/Ciphering Algorithm. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13; - - /** @hide */ - @IntDef({ - INTEGRITY_ALGORITHM_NONE, - INTEGRITY_ALGORITHM_HMAC_SHA1_96, - INTEGRITY_ALGORITHM_AES_XCBC_96, - INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, - INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, - INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 - }) - public @interface IntegrityAlgorithm {} - - /** None Authentication/Integrity Algorithm. */ - public static final int INTEGRITY_ALGORITHM_NONE = 0; - /** - * HMAC-SHA1 Authentication/Integrity Algorithm. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; - /** - * AES-XCBC-96 Authentication/Integrity Algorithm. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; - /** - * HMAC-SHA256 Authentication/Integrity Algorithm with 128-bit truncation. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; - /** - * HMAC-SHA384 Authentication/Integrity Algorithm with 192-bit truncation. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; - /** - * HMAC-SHA512 Authentication/Integrity Algorithm with 256-bit truncation. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; - - /** @hide */ - @IntDef({ - PSEUDORANDOM_FUNCTION_HMAC_SHA1, - PSEUDORANDOM_FUNCTION_AES128_XCBC, - PSEUDORANDOM_FUNCTION_SHA2_256, - PSEUDORANDOM_FUNCTION_SHA2_384, - PSEUDORANDOM_FUNCTION_SHA2_512 - }) - public @interface PseudorandomFunction {} - - /** - * HMAC-SHA1 Pseudorandom Function. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; - /** - * AES128-XCBC Pseudorandom Function. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; - /** - * HMAC-SHA2-256 Pseudorandom Function. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5; - /** - * HMAC-SHA2-384 Pseudorandom Function. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6; - /** - * HMAC-SHA2-384 Pseudorandom Function. - * - * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key - * Exchange Protocol Version 2 (IKEv2)</a> - */ - public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7; - - /** @hide */ @IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID}) public @interface IkeIdType {} @@ -4582,31 +4427,33 @@ public class CarrierConfigManager { defaults.putIntArray( KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY, new int[] { - DH_GROUP_1024_BIT_MODP, DH_GROUP_1536_BIT_MODP, DH_GROUP_2048_BIT_MODP + SaProposal.DH_GROUP_1024_BIT_MODP, + SaProposal.DH_GROUP_1536_BIT_MODP, + SaProposal.DH_GROUP_2048_BIT_MODP }); defaults.putIntArray( KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY, - new int[] {ENCRYPTION_ALGORITHM_AES_CBC}); + new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC}); defaults.putIntArray( KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY, - new int[] {ENCRYPTION_ALGORITHM_AES_CBC}); + new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC}); defaults.putIntArray( KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY, new int[] { - INTEGRITY_ALGORITHM_AES_XCBC_96, - INTEGRITY_ALGORITHM_HMAC_SHA1_96, - INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, - INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, - INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, + SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, + SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, }); defaults.putIntArray( KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY, new int[] { - PSEUDORANDOM_FUNCTION_HMAC_SHA1, - PSEUDORANDOM_FUNCTION_AES128_XCBC, - PSEUDORANDOM_FUNCTION_SHA2_256, - PSEUDORANDOM_FUNCTION_SHA2_384, - PSEUDORANDOM_FUNCTION_SHA2_512 + SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1, + SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC, + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256, + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384, + SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512 }); defaults.putInt(KEY_EPDG_AUTHENTICATION_METHOD_INT, AUTHENTICATION_METHOD_EAP_ONLY); @@ -4616,16 +4463,28 @@ public class CarrierConfigManager { defaults.putInt(KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT, 20); defaults.putIntArray( KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY, - new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256}); + new int[] { + SaProposal.KEY_LEN_AES_128, + SaProposal.KEY_LEN_AES_192, + SaProposal.KEY_LEN_AES_256}); defaults.putIntArray( KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY, - new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256}); + new int[] { + SaProposal.KEY_LEN_AES_128, + SaProposal.KEY_LEN_AES_192, + SaProposal.KEY_LEN_AES_256}); defaults.putIntArray( KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY, - new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256}); + new int[] { + SaProposal.KEY_LEN_AES_128, + SaProposal.KEY_LEN_AES_192, + SaProposal.KEY_LEN_AES_256}); defaults.putIntArray( KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY, - new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256}); + new int[] { + SaProposal.KEY_LEN_AES_128, + SaProposal.KEY_LEN_AES_192, + SaProposal.KEY_LEN_AES_256}); defaults.putIntArray( KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC}); diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml index b2719fbcac82..896ec9ae922c 100644 --- a/tests/FlickerTests/AndroidTest.xml +++ b/tests/FlickerTests/AndroidTest.xml @@ -11,6 +11,8 @@ <option name="force-skip-system-props" value="true" /> <!-- set WM tracing verbose level to all --> <option name="run-command" value="cmd window tracing level all" /> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame" /> <!-- restart launcher to activate TAPL --> <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" /> </target_preparer> diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java index d6846faa5c00..e121b68ca8fa 100644 --- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java +++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java @@ -94,17 +94,16 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule { if (platformCompat == null) { throw new IllegalStateException("Could not get IPlatformCompat service!"); } - uiAutomation.adoptShellPermissionIdentity( - Manifest.permission.LOG_COMPAT_CHANGE, - Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG, - Manifest.permission.READ_COMPAT_CHANGE_CONFIG); + adoptShellPermissions(uiAutomation); Compatibility.setOverrides(mConfig); try { platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig), packageName); try { + uiAutomation.dropShellPermissionIdentity(); mTestStatement.evaluate(); } finally { + adoptShellPermissions(uiAutomation); platformCompat.clearOverridesForTest(packageName); } } catch (RemoteException e) { @@ -114,5 +113,12 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule { Compatibility.clearOverrides(); } } + + private static void adoptShellPermissions(UiAutomation uiAutomation) { + uiAutomation.adoptShellPermissionIdentity( + Manifest.permission.LOG_COMPAT_CHANGE, + Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG, + Manifest.permission.READ_COMPAT_CHANGE_CONFIG); + } } } diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index e84b992a1379..0dfec7592274 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -28,6 +28,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE; @@ -44,6 +45,10 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES; import static android.os.Process.INVALID_UID; +import static com.android.modules.utils.build.SdkLevel.isAtLeastR; +import static com.android.modules.utils.build.SdkLevel.isAtLeastS; +import static com.android.testutils.MiscAsserts.assertEmpty; +import static com.android.testutils.MiscAsserts.assertThrows; import static com.android.testutils.ParcelUtils.assertParcelSane; import static com.android.testutils.ParcelUtils.assertParcelingIsLossless; import static com.android.testutils.ParcelUtils.parcelingRoundTrip; @@ -67,7 +72,6 @@ import android.util.ArraySet; import androidx.test.runner.AndroidJUnit4; -import com.android.modules.utils.build.SdkLevel; import com.android.testutils.CompatUtil; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; @@ -85,6 +89,9 @@ import java.util.Set; public class NetworkCapabilitiesTest { private static final String TEST_SSID = "TEST_SSID"; private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID"; + private static final int TEST_SUBID1 = 1; + private static final int TEST_SUBID2 = 2; + private static final int TEST_SUBID3 = 3; @Rule public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule(); @@ -92,14 +99,6 @@ public class NetworkCapabilitiesTest { private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class); private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class); - private boolean isAtLeastR() { - return SdkLevel.isAtLeastR(); - } - - private boolean isAtLeastS() { - return SdkLevel.isAtLeastS(); - } - @Test public void testMaybeMarkCapabilitiesRestricted() { // verify EIMS is restricted @@ -305,7 +304,9 @@ public class NetworkCapabilitiesTest { .setUids(uids) .addCapability(NET_CAPABILITY_EIMS) .addCapability(NET_CAPABILITY_NOT_METERED); - if (isAtLeastR()) { + if (isAtLeastS()) { + netCap.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)); + } else if (isAtLeastR()) { netCap.setOwnerUid(123); netCap.setAdministratorUids(new int[] {5, 11}); } @@ -380,7 +381,7 @@ public class NetworkCapabilitiesTest { private void testParcelSane(NetworkCapabilities cap) { if (isAtLeastS()) { - assertParcelSane(cap, 16); + assertParcelSane(cap, 17); } else if (isAtLeastR()) { assertParcelSane(cap, 15); } else { @@ -614,6 +615,20 @@ public class NetworkCapabilitiesTest { assertFalse(nc2.appliesToUid(12)); assertTrue(nc1.appliesToUid(22)); assertTrue(nc2.appliesToUid(22)); + + // Verify the subscription id list can be combined only when they are equal. + if (isAtLeastS()) { + nc1.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)); + nc2.setSubIds(Set.of(TEST_SUBID2)); + assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1)); + + nc2.setSubIds(Set.of()); + assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1)); + + nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1)); + nc2.combineCapabilities(nc1); + assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubIds()); + } } @Test @IgnoreUpTo(Build.VERSION_CODES.Q) @@ -762,6 +777,24 @@ public class NetworkCapabilitiesTest { nc1.setUids(uidRange(10, 13)); nc2.set(nc1); // Overwrites, as opposed to combineCapabilities assertEquals(nc1, nc2); + + if (isAtLeastS()) { + assertThrows(NullPointerException.class, () -> nc1.setSubIds(null)); + nc1.setSubIds(Set.of()); + nc2.set(nc1); + assertEquals(nc1, nc2); + + nc1.setSubIds(Set.of(TEST_SUBID1)); + nc2.set(nc1); + assertEquals(nc1, nc2); + + nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1)); + nc2.set(nc1); + assertEquals(nc1, nc2); + + nc2.setSubIds(Set.of(TEST_SUBID3, TEST_SUBID2)); + assertNotEquals(nc1, nc2); + } } @Test @@ -842,6 +875,50 @@ public class NetworkCapabilitiesTest { } catch (NullPointerException expected) { } } + private static NetworkCapabilities capsWithSubIds(Integer ... subIds) { + // Since the NetworkRequest would put NOT_VCN_MANAGED capabilities in general, for + // every NetworkCapabilities that simulates networks needs to add it too in order to + // satisfy these requests. + final NetworkCapabilities nc = new NetworkCapabilities.Builder() + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .setSubIds(new ArraySet<>(subIds)).build(); + assertEquals(new ArraySet<>(subIds), nc.getSubIds()); + return nc; + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testSubIds() throws Exception { + final NetworkCapabilities ncWithoutId = capsWithSubIds(); + final NetworkCapabilities ncWithId = capsWithSubIds(TEST_SUBID1); + final NetworkCapabilities ncWithOtherIds = capsWithSubIds(TEST_SUBID1, TEST_SUBID3); + final NetworkCapabilities ncWithoutRequestedIds = capsWithSubIds(TEST_SUBID3); + + final NetworkRequest requestWithoutId = new NetworkRequest.Builder().build(); + assertEmpty(requestWithoutId.networkCapabilities.getSubIds()); + final NetworkRequest requestWithIds = new NetworkRequest.Builder() + .setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build(); + assertEquals(Set.of(TEST_SUBID1, TEST_SUBID2), + requestWithIds.networkCapabilities.getSubIds()); + + assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutId)); + assertTrue(requestWithIds.canBeSatisfiedBy(ncWithOtherIds)); + assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutRequestedIds)); + assertTrue(requestWithIds.canBeSatisfiedBy(ncWithId)); + assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithoutId)); + assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithId)); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testEqualsSubIds() throws Exception { + assertEquals(capsWithSubIds(), capsWithSubIds()); + assertNotEquals(capsWithSubIds(), capsWithSubIds(TEST_SUBID1)); + assertEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID1)); + assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2)); + assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2, TEST_SUBID1)); + assertEquals(capsWithSubIds(TEST_SUBID1, TEST_SUBID2), + capsWithSubIds(TEST_SUBID2, TEST_SUBID1)); + } + @Test public void testLinkBandwidthKbps() { final NetworkCapabilities nc = new NetworkCapabilities(); @@ -1022,5 +1099,11 @@ public class NetworkCapabilitiesTest { fail("Should not set null into NetworkCapabilities.Builder"); } catch (NullPointerException expected) { } assertEquals(nc, new NetworkCapabilities.Builder(nc).build()); + + if (isAtLeastS()) { + final NetworkCapabilities nc2 = new NetworkCapabilities.Builder() + .setSubIds(Set.of(TEST_SUBID1)).build(); + assertEquals(Set.of(TEST_SUBID1), nc2.getSubIds()); + } } } diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index 098b029b75e6..6fc605e269fe 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -220,7 +220,7 @@ public class ConnectivityManagerTest { // register callback when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), - any(), nullable(String.class))).thenReturn(request); + anyInt(), any(), nullable(String.class))).thenReturn(request); manager.requestNetwork(request, callback, handler); // callback triggers @@ -248,7 +248,7 @@ public class ConnectivityManagerTest { // register callback when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), - any(), nullable(String.class))).thenReturn(req1); + anyInt(), any(), nullable(String.class))).thenReturn(req1); manager.requestNetwork(req1, callback, handler); // callback triggers @@ -266,7 +266,7 @@ public class ConnectivityManagerTest { // callback can be registered again when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), - any(), nullable(String.class))).thenReturn(req2); + anyInt(), any(), nullable(String.class))).thenReturn(req2); manager.requestNetwork(req2, callback, handler); // callback triggers @@ -289,8 +289,8 @@ public class ConnectivityManagerTest { info.targetSdkVersion = VERSION_CODES.N_MR1 + 1; when(mCtx.getApplicationInfo()).thenReturn(info); - when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), any(), - nullable(String.class))).thenReturn(request); + when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(), + any(), nullable(String.class))).thenReturn(request); Handler handler = new Handler(Looper.getMainLooper()); manager.requestNetwork(request, callback, handler); @@ -358,34 +358,34 @@ public class ConnectivityManagerTest { manager.requestNetwork(request, callback); verify(mService).requestNetwork(eq(request.networkCapabilities), - eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), + eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); // Verify that register network callback does not calls requestNetwork at all. manager.registerNetworkCallback(request, callback); - verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(), + verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(), any(), any()); - verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), + verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); manager.registerDefaultNetworkCallback(callback); verify(mService).requestNetwork(eq(null), - eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), + eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); manager.requestBackgroundNetwork(request, handler, callback); verify(mService).requestNetwork(eq(request.networkCapabilities), - eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), + eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); manager.registerSystemDefaultNetworkCallback(callback, handler); verify(mService).requestNetwork(eq(null), - eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), + eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 9db5854c6525..fadd1eac14ef 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -1450,6 +1450,8 @@ public class ConnectivityServiceTest { applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) .thenReturn(applicationInfo); + when(mPackageManager.getTargetSdkVersion(anyString())) + .thenReturn(applicationInfo.targetSdkVersion); when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]); // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not. @@ -3756,8 +3758,8 @@ public class ConnectivityServiceTest { networkCapabilities.addTransportType(TRANSPORT_WIFI) .setNetworkSpecifier(new MatchAllNetworkSpecifier()); mService.requestNetwork(networkCapabilities, NetworkRequest.Type.REQUEST.ordinal(), - null, 0, null, ConnectivityManager.TYPE_WIFI, mContext.getPackageName(), - getAttributionTag()); + null, 0, null, ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE, + mContext.getPackageName(), getAttributionTag()); }); class NonParcelableSpecifier extends NetworkSpecifier { @@ -8763,6 +8765,7 @@ public class ConnectivityServiceTest { applicationInfo.targetSdkVersion = targetSdk; when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) .thenReturn(applicationInfo); + when(mPackageManager.getTargetSdkVersion(any())).thenReturn(targetSdk); when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle); @@ -8777,102 +8780,183 @@ public class ConnectivityServiceTest { } } - private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) { + private int getOwnerUidNetCapsPermission(int ownerUid, int callerUid, + boolean includeLocationSensitiveInfo) { final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid); return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid(); + netCap, includeLocationSensitiveInfo, callerUid, + mContext.getPackageName(), getAttributionTag()) + .getOwnerUid(); } - private void verifyWifiInfoCopyNetCapsForCallerPermission( - int callerUid, boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) { + private void verifyWifiInfoCopyNetCapsPermission( + int callerUid, boolean includeLocationSensitiveInfo, + boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) { final WifiInfo wifiInfo = mock(WifiInfo.class); when(wifiInfo.hasLocationSensitiveFields()).thenReturn(true); final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo); mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled( - netCap, callerUid, mContext.getPackageName(), getAttributionTag()); + netCap, includeLocationSensitiveInfo, callerUid, + mContext.getPackageName(), getAttributionTag()); verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable)); } + private void verifyOwnerUidAndWifiInfoNetCapsPermission( + boolean shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag, + boolean shouldInclLocationSensitiveOwnerUidWithIncludeFlag, + boolean shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag, + boolean shouldInclLocationSensitiveWifiInfoWithIncludeFlag) { + final int myUid = Process.myUid(); + + final int expectedOwnerUidWithoutIncludeFlag = + shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag + ? Process.myUid() : INVALID_UID; + assertEquals(expectedOwnerUidWithoutIncludeFlag, getOwnerUidNetCapsPermission( + myUid, myUid, false /* includeLocationSensitiveInfo */)); + + final int expectedOwnerUidWithIncludeFlag = + shouldInclLocationSensitiveOwnerUidWithIncludeFlag ? myUid : INVALID_UID; + assertEquals(expectedOwnerUidWithIncludeFlag, getOwnerUidNetCapsPermission( + myUid, myUid, true /* includeLocationSensitiveInfo */)); + + verifyWifiInfoCopyNetCapsPermission(myUid, + false, /* includeLocationSensitiveInfo */ + shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag); + + verifyWifiInfoCopyNetCapsPermission(myUid, + true, /* includeLocationSensitiveInfo */ + shouldInclLocationSensitiveWifiInfoWithIncludeFlag); + + } + @Test - public void testCreateForCallerWithLocationInfoSanitizedWithFineLocationAfterQ() + public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQ() throws Exception { setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); - final int myUid = Process.myUid(); - assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); + verifyOwnerUidAndWifiInfoNetCapsPermission( + // Ensure that we include owner uid even if the request asks to remove it since the + // app has necessary permissions and targetSdk < S. + true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ + true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ + false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */ + // Ensure that we remove location info if the request asks to remove it even if the + // app has necessary permissions. + true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */ + ); + } + + @Test + public void testCreateWithLocationInfoSanitizedWithFineLocationPreSWithAndWithoutCallbackFlag() + throws Exception { + setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); - verifyWifiInfoCopyNetCapsForCallerPermission(myUid, - true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); + verifyOwnerUidAndWifiInfoNetCapsPermission( + // Ensure that we include owner uid even if the request asks to remove it since the + // app has necessary permissions and targetSdk < S. + true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ + true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ + false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */ + // Ensure that we remove location info if the request asks to remove it even if the + // app has necessary permissions. + true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */ + ); } @Test - public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationPreQ() + public void + testCreateWithLocationInfoSanitizedWithFineLocationAfterSWithAndWithoutCallbackFlag() + throws Exception { + setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION); + + verifyOwnerUidAndWifiInfoNetCapsPermission( + // Ensure that we owner UID if the request asks us to remove it even if the app + // has necessary permissions since targetSdk >= S. + false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ + true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ + false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */ + // Ensure that we remove location info if the request asks to remove it even if the + // app has necessary permissions. + true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */ + ); + } + + @Test + public void testCreateWithLocationInfoSanitizedWithCoarseLocationPreQ() throws Exception { setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION); - final int myUid = Process.myUid(); - assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); - - verifyWifiInfoCopyNetCapsForCallerPermission(myUid, - true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); + verifyOwnerUidAndWifiInfoNetCapsPermission( + // Ensure that we owner UID if the request asks us to remove it even if the app + // has necessary permissions since targetSdk >= S. + true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ + true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ + false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */ + // Ensure that we remove location info if the request asks to remove it even if the + // app has necessary permissions. + true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */ + ); } @Test - public void testCreateForCallerWithLocationInfoSanitizedLocationOff() throws Exception { + public void testCreateWithLocationInfoSanitizedLocationOff() throws Exception { // Test that even with fine location permission, and UIDs matching, the UID is sanitized. setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); - final int myUid = Process.myUid(); - assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); - - verifyWifiInfoCopyNetCapsForCallerPermission(myUid, - false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); + verifyOwnerUidAndWifiInfoNetCapsPermission( + false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ + false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ + false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */ + false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */ + ); } @Test - public void testCreateForCallerWithLocationInfoSanitizedWrongUid() throws Exception { + public void testCreateWithLocationInfoSanitizedWrongUid() throws Exception { // Test that even with fine location permission, not being the owner leads to sanitization. setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); final int myUid = Process.myUid(); - assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid)); - - verifyWifiInfoCopyNetCapsForCallerPermission(myUid, - true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); + assertEquals(Process.INVALID_UID, + getOwnerUidNetCapsPermission(myUid + 1, myUid, + true /* includeLocationSensitiveInfo */)); } @Test - public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationAfterQ() + public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterQ() throws Exception { // Test that not having fine location permission leads to sanitization. setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION); - // Test that without the location permission, the owner field is sanitized. - final int myUid = Process.myUid(); - assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); - - verifyWifiInfoCopyNetCapsForCallerPermission(myUid, - false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); + verifyOwnerUidAndWifiInfoNetCapsPermission( + false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ + false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ + false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */ + false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */ + ); } @Test - public void testCreateForCallerWithLocationInfoSanitizedWithoutLocationPermission() + public void testCreateWithLocationInfoSanitizedWithoutLocationPermission() throws Exception { + // Test that not having fine location permission leads to sanitization. setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */); - // Test that without the location permission, the owner field is sanitized. - final int myUid = Process.myUid(); - assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid)); - - verifyWifiInfoCopyNetCapsForCallerPermission(myUid, - false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */); + verifyOwnerUidAndWifiInfoNetCapsPermission( + false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */ + false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */ + false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */ + false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */ + ); } private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) @@ -9463,8 +9547,8 @@ public class ConnectivityServiceTest { assertThrows("Expect throws for invalid request type " + reqTypeInt, IllegalArgumentException.class, () -> mService.requestNetwork(nc, reqTypeInt, null, 0, null, - ConnectivityManager.TYPE_NONE, mContext.getPackageName(), - getAttributionTag()) + ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE, + mContext.getPackageName(), getAttributionTag()) ); } } diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index f97eabf6366d..6232423b4f9e 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.net.ConnectivityManager; import android.net.INetd; import android.net.IpSecAlgorithm; import android.net.IpSecConfig; @@ -47,6 +48,7 @@ import android.os.Process; import android.system.ErrnoException; import android.system.Os; import android.system.StructStat; +import android.util.Range; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -647,9 +649,9 @@ public class IpSecServiceTest { @Test public void testReserveNetId() { - int start = mIpSecService.TUN_INTF_NETID_START; - for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) { - assertEquals(start + i, mIpSecService.reserveNetId()); + final Range<Integer> netIdRange = ConnectivityManager.getIpSecNetIdRange(); + for (int netId = netIdRange.getLower(); netId <= netIdRange.getUpper(); netId++) { + assertEquals(netId, mIpSecService.reserveNetId()); } // Check that resource exhaustion triggers an exception @@ -661,7 +663,7 @@ public class IpSecServiceTest { // Now release one and try again int releasedNetId = - mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2; + netIdRange.getLower() + (netIdRange.getUpper() - netIdRange.getLower()) / 2; mIpSecService.releaseNetId(releasedNetId); assertEquals(releasedNetId, mIpSecService.reserveNetId()); } diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index e4e24b464838..fec5ef39374a 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -48,18 +48,22 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.net.INetd; import android.net.UidRange; +import android.net.Uri; import android.os.Build; import android.os.SystemConfigManager; import android.os.UserHandle; @@ -70,12 +74,11 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.server.LocalServices; -import com.android.server.pm.PackageList; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.AdditionalAnswers; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; @@ -112,7 +115,6 @@ public class PermissionMonitorTest { @Mock private Context mContext; @Mock private PackageManager mPackageManager; @Mock private INetd mNetdService; - @Mock private PackageManagerInternal mMockPmi; @Mock private UserManager mUserManager; @Mock private PermissionMonitor.Dependencies mDeps; @Mock private SystemConfigManager mSystemConfigManager; @@ -131,16 +133,14 @@ public class PermissionMonitorTest { when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE)) .thenReturn(mSystemConfigManager); when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]); + final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext)); + doReturn(UserHandle.ALL).when(asUserCtx).getUser(); + when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx); mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps)); - LocalServices.removeServiceForTest(PackageManagerInternal.class); - LocalServices.addService(PackageManagerInternal.class, mMockPmi); - when(mMockPmi.getPackageList(any())).thenReturn(new PackageList(new ArrayList<String>(), - /* observer */ null)); when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null); mPermissionMonitor.startMonitoring(); - verify(mMockPmi).getPackageList(mPermissionMonitor); } private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid, @@ -770,4 +770,32 @@ public class PermissionMonitorTest { INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{ MOCK_UID2 }); } + + @Test + public void testIntentReceiver() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + final ArgumentCaptor<BroadcastReceiver> receiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any()); + final BroadcastReceiver receiver = receiverCaptor.getValue(); + + // Verify receiving PACKAGE_ADDED intent. + final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED, + Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */)); + addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1); + setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1, + new String[] { INTERNET, UPDATE_DEVICE_STATS }); + receiver.onReceive(mContext, addedIntent); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET + | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[] { MOCK_UID1 }); + + // Verify receiving PACKAGE_REMOVED intent. + when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(null); + final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED, + Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */)); + removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1); + receiver.onReceive(mContext, removedIntent); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[] { MOCK_UID1 }); + } + } |