diff options
97 files changed, 1448 insertions, 719 deletions
diff --git a/apex/Android.bp b/apex/Android.bp index e8f6e6bf2c46..2f4294588d7a 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -88,18 +88,30 @@ java_defaults { name: "framework-module-stubs-lib-defaults-publicapi", installable: false, sdk_version: "module_current", + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/public", + }, } java_defaults { name: "framework-module-stubs-lib-defaults-systemapi", installable: false, sdk_version: "module_current", + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system", + }, } java_defaults { name: "framework-module-stubs-lib-defaults-module_libs_api", installable: false, sdk_version: "module_current", + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/module-lib", + }, } // The defaults for module_libs comes in two parts - defaults for API checks @@ -153,4 +165,8 @@ stubs_defaults { // module java_library system_server stub libs. java_defaults { name: "service-module-stubs-defaults", + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server", + }, } diff --git a/api/current.txt b/api/current.txt index 4925a472fd12..07dba53884dd 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24357,7 +24357,7 @@ package android.media { } public final class AudioMetadata { - method @NonNull public static android.media.AudioMetadata.Map createMap(); + method @NonNull public static android.media.AudioMetadataMap createMap(); } public static class AudioMetadata.Format { @@ -24375,16 +24375,16 @@ package android.media { method @NonNull public Class<T> getValueClass(); } - public static interface AudioMetadata.Map extends android.media.AudioMetadata.ReadMap { + public interface AudioMetadataMap extends android.media.AudioMetadataReadMap { method @Nullable public <T> T remove(@NonNull android.media.AudioMetadata.Key<T>); method @Nullable public <T> T set(@NonNull android.media.AudioMetadata.Key<T>, @NonNull T); } - public static interface AudioMetadata.ReadMap { + public interface AudioMetadataReadMap { method public <T> boolean containsKey(@NonNull android.media.AudioMetadata.Key<T>); - method @NonNull public android.media.AudioMetadata.Map dup(); + method @NonNull public android.media.AudioMetadataMap dup(); method @Nullable public <T> T get(@NonNull android.media.AudioMetadata.Key<T>); - method public int size(); + method @IntRange(from=0) public int size(); } public final class AudioPlaybackCaptureConfiguration { @@ -24699,7 +24699,7 @@ package android.media { } public static interface AudioTrack.OnCodecFormatChangedListener { - method public void onCodecFormatChanged(@NonNull android.media.AudioTrack, @Nullable android.media.AudioMetadata.ReadMap); + method public void onCodecFormatChanged(@NonNull android.media.AudioTrack, @Nullable android.media.AudioMetadataReadMap); } public static interface AudioTrack.OnPlaybackPositionUpdateListener { @@ -37160,9 +37160,9 @@ package android.os { } public abstract class Vibrator { - method @Nullable public final Boolean areAllEffectsSupported(@NonNull int...); + method public final int areAllEffectsSupported(@NonNull int...); method public final boolean areAllPrimitivesSupported(@NonNull int...); - method @Nullable public boolean[] areEffectsSupported(@NonNull int...); + method @NonNull public int[] areEffectsSupported(@NonNull int...); method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...); method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel(); method public abstract boolean hasAmplitudeControl(); @@ -37173,6 +37173,9 @@ package android.os { method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long[], int, android.media.AudioAttributes); method @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(android.os.VibrationEffect); method @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(android.os.VibrationEffect, android.media.AudioAttributes); + field public static final int VIBRATION_EFFECT_SUPPORT_NO = 2; // 0x2 + field public static final int VIBRATION_EFFECT_SUPPORT_UNKNOWN = 0; // 0x0 + field public static final int VIBRATION_EFFECT_SUPPORT_YES = 1; // 0x1 } public class WorkSource implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 6d5ba34fdf9f..38c265bd50b3 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4178,11 +4178,11 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException; method public void clearAudioServerStateCallback(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); - method @IntRange(from=0) public int getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); + method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes); - method @IntRange(from=0) public int getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); + method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); @@ -4197,7 +4197,7 @@ package android.media { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException; method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) int); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long); method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes); @@ -4272,17 +4272,11 @@ package android.media { } public static class AudioTrack.TunerConfiguration { + ctor @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public AudioTrack.TunerConfiguration(@IntRange(from=1) int, @IntRange(from=1) int); method @IntRange(from=1) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getContentId(); method @IntRange(from=1) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getSyncId(); } - public static class AudioTrack.TunerConfiguration.Builder { - ctor public AudioTrack.TunerConfiguration.Builder(); - method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioTrack.TunerConfiguration build(); - method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioTrack.TunerConfiguration.Builder setContentId(@IntRange(from=1) int); - method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioTrack.TunerConfiguration.Builder setSyncId(@IntRange(from=1) int); - } - public class HwAudioSource { method public boolean isPlaying(); method public void start(); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 20ddba41bda1..1dadbda1918b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1837,6 +1837,12 @@ public class PackageParser { pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false); + final boolean isolatedSplits = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false); + if (isolatedSplits) { + pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING; + } + pkg.mCompileSdkVersion = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0); pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion; @@ -1912,10 +1918,6 @@ public class PackageParser { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE; } - if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false)) { - pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING; - } - // Resource boolean are -1, so 1 means we don't know the value. int supportsSmallScreens = 1; int supportsNormalScreens = 1; diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 079a47056c24..894ad5584922 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -433,6 +433,10 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { R.styleable.AndroidManifest_compileSdkVersion, 0)); setCompileSdkVersionCodename(manifestArray.getNonConfigurationString( R.styleable.AndroidManifest_compileSdkVersionCodename, 0)); + + setIsolatedSplitLoading(manifestArray.getBoolean( + R.styleable.AndroidManifest_isolatedSplits, false)); + } } diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index ec771286d980..e90ccdffa8a8 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -386,15 +386,14 @@ public class ParsingPackageUtils { return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME); } - TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest); + final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest); try { - boolean isCoreApp = parser.getAttributeBooleanValue(null, "coreApp", false); - - ParsingPackage pkg = mCallback.startParsingPackage(pkgName, apkPath, codePath, - manifestArray, isCoreApp); - - ParseResult<ParsingPackage> result = parseBaseApkTags(input, pkg, manifestArray, - res, parser, flags); + final boolean isCoreApp = + parser.getAttributeBooleanValue(null, "coreApp", false); + final ParsingPackage pkg = mCallback.startParsingPackage( + pkgName, apkPath, codePath, manifestArray, isCoreApp); + final ParseResult<ParsingPackage> result = + parseBaseApkTags(input, pkg, manifestArray, res, parser, flags); if (result.isError()) { return result; } @@ -620,14 +619,12 @@ public class ParsingPackageUtils { return sharedUserResult; } - pkg.setInstallLocation(anInt(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION, + pkg.setInstallLocation(anInteger(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION, R.styleable.AndroidManifest_installLocation, sa)) - .setTargetSandboxVersion(anInt(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX, + .setTargetSandboxVersion(anInteger(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX, R.styleable.AndroidManifest_targetSandboxVersion, sa)) /* Set the global "on SD card" flag */ - .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0) - .setIsolatedSplitLoading( - bool(false, R.styleable.AndroidManifest_isolatedSplits, sa)); + .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0); boolean foundApp = false; final int depth = parser.getDepth(); @@ -2658,6 +2655,10 @@ public class ParsingPackageUtils { return sa.getInt(attribute, defaultValue); } + private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) { + return sa.getInteger(attribute, defaultValue); + } + private static int anInt(@StyleableRes int attribute, TypedArray sa) { return sa.getInt(attribute, 0); } @@ -2688,6 +2689,6 @@ public class ParsingPackageUtils { boolean hasFeature(String feature); ParsingPackage startParsingPackage(String packageName, String baseCodePath, String codePath, - TypedArray manifestArray, boolean isCoreApp); + @NonNull TypedArray manifestArray, boolean isCoreApp); } } diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl index 84013e7ebc88..615ae6593330 100644 --- a/core/java/android/os/IVibratorService.aidl +++ b/core/java/android/os/IVibratorService.aidl @@ -28,7 +28,7 @@ interface IVibratorService boolean registerVibratorStateListener(in IVibratorStateListener listener); boolean unregisterVibratorStateListener(in IVibratorStateListener listener); boolean hasAmplitudeControl(); - boolean[] areEffectsSupported(in int[] effectIds); + int[] areEffectsSupported(in int[] effectIds); boolean[] arePrimitivesSupported(in int[] primitiveIds); boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, in VibrationEffect effect, in VibrationAttributes attributes); diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index da20c7f2ae70..2dba8dce62da 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -21,12 +21,13 @@ import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.media.AudioAttributes; -import android.os.IVibratorStateListener; import android.util.ArrayMap; import android.util.Log; + import com.android.internal.annotations.GuardedBy; -import java.util.concurrent.Executor; + import java.util.Objects; +import java.util.concurrent.Executor; /** * Vibrator implementation that controls the main system vibrator. @@ -238,13 +239,13 @@ public class SystemVibrator extends Vibrator { } @Override - public boolean[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) { + public int[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) { try { return mService.areEffectsSupported(effectIds); } catch (RemoteException e) { Log.w(TAG, "Failed to query effect support"); + throw e.rethrowAsRuntimeException(); } - return new boolean[effectIds.length]; } @Override @@ -254,8 +255,8 @@ public class SystemVibrator extends Vibrator { return mService.arePrimitivesSupported(primitiveIds); } catch (RemoteException e) { Log.w(TAG, "Failed to query effect support"); + throw e.rethrowAsRuntimeException(); } - return new boolean[primitiveIds.length]; } diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index f7183b14a97a..86d009e8607e 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -32,6 +32,7 @@ import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; import java.util.concurrent.Executor; /** @@ -72,6 +73,37 @@ public abstract class Vibrator { */ public static final int VIBRATION_INTENSITY_HIGH = 3; + /** + * Vibration effect support: unknown + * + * The hardware doesn't report it's supported effects, so we can't determine whether the + * effect is supported or not. + */ + public static final int VIBRATION_EFFECT_SUPPORT_UNKNOWN = 0; + + /** + * Vibration effect support: supported + * + * This effect is supported by the underlying hardware. + */ + public static final int VIBRATION_EFFECT_SUPPORT_YES = 1; + + /** + * Vibration effect support: unsupported + * + * This effect is <b>not</b> supported by the underlying hardware. + */ + public static final int VIBRATION_EFFECT_SUPPORT_NO = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"VIBRATION_EFFECT_SUPPORT_"}, value = { + VIBRATION_EFFECT_SUPPORT_UNKNOWN, + VIBRATION_EFFECT_SUPPORT_YES, + VIBRATION_EFFECT_SUPPORT_NO, + }) + public @interface VibrationEffectSupport {} + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"VIBRATION_INTENSITY_"}, value = { @@ -318,47 +350,61 @@ public abstract class Vibrator { /** * Query whether the vibrator supports the given effects. * - * If the returned array is {@code null}, the hardware doesn't support querying its supported - * effects. It may support any or all effects, but there's no way to programmatically know - * whether a {@link #vibrate} call will be successful. + * Not all hardware reports its effect capabilities, so the system may not necessarily know + * whether an effect is supported or not. * - * If the returned array is non-null, then it will be the same length as the query array and - * the value at a given index will contain whether the effect at that same index in the - * querying array is supported or not. + * The returned array will be the same length as the query array and the value at a given index + * will contain {@link #VIBRATION_EFFECT_SUPPORT_YES} if the effect at that same index in the + * querying array is supported, {@link #VIBRATION_EFFECT_SUPPORT_NO} if it isn't supported, or + * {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN} if the system can't determine whether it's + * supported or not. * * @param effectIds Which effects to query for. - * @return Whether the effects are supported. Null when the hardware doesn't tell us what it - * supports. + * @return An array containing the systems current knowledge about whether the given effects + * are supported or not. */ - @Nullable - public boolean[] areEffectsSupported( + @NonNull + @VibrationEffectSupport + public int[] areEffectsSupported( @NonNull @VibrationEffect.EffectType int... effectIds) { - return new boolean[effectIds.length]; + final int[] support = new int[effectIds.length]; + Arrays.fill(support, VIBRATION_EFFECT_SUPPORT_NO); + return support; } /** * Query whether the vibrator supports all of the given effects. * - * If the result is {@code null}, the hardware doesn't support querying its supported - * effects. It may support any or all effects, but there's no way to programmatically know - * whether a {@link #vibrate} call will be successful. + * Not all hardware reports its effect capabilities, so the system may not necessarily know + * whether an effect is supported or not. * - * If the returned array is non-null, then it will return whether all of the effects are + * If the result is {@link #VIBRATION_EFFECT_SUPPORT_YES}, all effects in the query are * supported by the hardware. * + * If the result is {@link #VIBRATION_EFFECT_SUPPORT_NO}, at least one of the effects in the + * query is not supported. + * + * If the result is {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN}, the system doesn't know whether + * all of the effects are supported. It may support any or all of the queried effects, + * but there's no way to programmatically know whether a {@link #vibrate} call will successfully + * cause a vibration. It's guaranteed, however, that none of the queried effects are + * definitively unsupported by the hardware. + * * @param effectIds Which effects to query for. - * @return Whether the effects are supported. {@code null} when the hardware doesn't tell us - * what it supports. + * @return Whether all of the effects are supported. */ - @Nullable - public final Boolean areAllEffectsSupported( + @VibrationEffectSupport + public final int areAllEffectsSupported( @NonNull @VibrationEffect.EffectType int... effectIds) { - for (boolean supported : areEffectsSupported(effectIds)) { - if (!supported) { - return false; + int support = VIBRATION_EFFECT_SUPPORT_YES; + for (int supported : areEffectsSupported(effectIds)) { + if (supported == VIBRATION_EFFECT_SUPPORT_NO) { + return VIBRATION_EFFECT_SUPPORT_NO; + } else if (supported == VIBRATION_EFFECT_SUPPORT_UNKNOWN) { + support = VIBRATION_EFFECT_SUPPORT_UNKNOWN; } } - return true; + return support; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d2a03f0ff166..530585d86465 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8571,6 +8571,16 @@ public final class Settings { public static final String QS_TILES = "sysui_qs_tiles"; /** + * Whether this user has enabled Quick controls. + * + * 0 indicates disabled and 1 indicates enabled. A non existent value should be treated as + * enabled. + * + * @hide + */ + public static final String CONTROLS_ENABLED = "controls_enabled"; + + /** * Specifies whether the web action API is enabled. * * @hide diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java index 9accf5b0abf5..4262c4022131 100644 --- a/core/java/android/service/controls/ControlsProviderService.java +++ b/core/java/android/service/controls/ControlsProviderService.java @@ -304,12 +304,11 @@ public abstract class ControlsProviderService extends Service { Preconditions.checkNotNull(context); Preconditions.checkNotNull(componentName); Preconditions.checkNotNull(control); - final ComponentName sysuiComponent = ComponentName.unflattenFromString( - context.getResources().getString( - com.android.internal.R.string.config_systemUIServiceComponent)); + final String controlsPackage = context.getString( + com.android.internal.R.string.config_controlsPackage); Intent intent = new Intent(ACTION_ADD_CONTROL); intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName); - intent.setPackage(sysuiComponent.getPackageName()); + intent.setPackage(controlsPackage); if (isStatelessControl(control)) { intent.putExtra(EXTRA_CONTROL, control); } else { diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index e6bd84391fef..40a460dfece0 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.toInternalType; import static android.view.InsetsState.toPublicType; @@ -367,6 +368,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private int mLastLegacySystemUiFlags; private DisplayCutout mLastDisplayCutout; private boolean mStartingAnimation; + private int mCaptionInsetsHeight = 0; private SyncRtSurfaceTransactionApplier mApplier; @@ -460,7 +462,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting public boolean onStateChanged(InsetsState state) { - boolean localStateChanged = !mState.equals(state); + boolean localStateChanged = !mState.equals(state, true /* excludingCaptionInsets */) + || !captionInsetsUnchanged(); if (!localStateChanged && mLastDispachedState.equals(state)) { return false; } @@ -470,7 +473,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (localStateChanged) { mViewRoot.notifyInsetsChanged(); } - if (!mState.equals(mLastDispachedState)) { + if (!mState.equals(mLastDispachedState, true /* excludingCaptionInsets */)) { sendStateToWindowManager(); } return true; @@ -488,6 +491,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mState.removeSource(source.getType()); } } + if (mCaptionInsetsHeight != 0) { + mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top, + mFrame.right, mFrame.top + mCaptionInsetsHeight)); + } + } + + private boolean captionInsetsUnchanged() { + if (mState.peekSource(ITYPE_CAPTION_BAR) == null + && mCaptionInsetsHeight == 0) { + return true; + } + if (mState.peekSource(ITYPE_CAPTION_BAR) != null + && mCaptionInsetsHeight + == mState.peekSource(ITYPE_CAPTION_BAR).getFrame().height()) { + return true; + } + return false; } /** @@ -964,6 +984,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation InsetsState tmpState = new InsetsState(); for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); + if (consumer.getType() == ITYPE_CAPTION_BAR) continue; if (consumer.getControl() != null) { tmpState.addSource(mState.getSource(consumer.getType())); } @@ -1105,6 +1126,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } @Override + public void setCaptionInsetsHeight(int height) { + mCaptionInsetsHeight = height; + } + + @Override public void setSystemBarsBehavior(@Behavior int behavior) { mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED; if (mViewRoot.mWindowAttributes.insetsFlags.behavior != behavior) { diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 294faaf0b5c8..033ccef3666d 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_IME; import android.annotation.NonNull; @@ -118,6 +119,12 @@ public class InsetsSource implements Parcelable { if (!getIntersection(frame, relativeFrame, mTmpFrame)) { return Insets.NONE; } + // During drag-move and drag-resizing, the caption insets position may not get updated + // before the app frame get updated. To layout the app content correctly during drag events, + // we always return the insets with the corresponding height covering the top. + if (getType() == ITYPE_CAPTION_BAR) { + return Insets.of(0, frame.height(), 0, 0); + } // TODO: Currently, non-floating IME always intersects at bottom due to issues with cutout. // However, we should let the policy decide from the server. diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 40e6f57f2286..c5154662928e 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -45,6 +45,8 @@ import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; +import com.android.internal.annotations.VisibleForTesting; + import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -498,6 +500,19 @@ public class InsetsState implements Parcelable { @Override public boolean equals(Object o) { + return equals(o, false); + } + + /** + * An equals method can exclude the caption insets. This is useful because we assemble the + * caption insets information on the client side, and when we communicate with server, it's + * excluded. + * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but + * ignore the caption insets source value. + * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise. + */ + @VisibleForTesting + public boolean equals(Object o, boolean excludingCaptionInsets) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } @@ -506,11 +521,24 @@ public class InsetsState implements Parcelable { if (!mDisplayFrame.equals(state.mDisplayFrame)) { return false; } - if (mSources.size() != state.mSources.size()) { + int size = mSources.size(); + int otherSize = state.mSources.size(); + if (excludingCaptionInsets) { + if (mSources.get(ITYPE_CAPTION_BAR) != null) { + size--; + } + if (state.mSources.get(ITYPE_CAPTION_BAR) != null) { + otherSize--; + } + } + if (size != otherSize) { return false; } for (int i = mSources.size() - 1; i >= 0; i--) { InsetsSource source = mSources.valueAt(i); + if (excludingCaptionInsets) { + if (source.getType() == ITYPE_CAPTION_BAR) continue; + } InsetsSource otherSource = state.mSources.get(source.getType()); if (otherSource == null) { return false; diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java index 229ee03521bc..a106b2c4726d 100644 --- a/core/java/android/view/PendingInsetsController.java +++ b/core/java/android/view/PendingInsetsController.java @@ -42,6 +42,7 @@ public class PendingInsetsController implements WindowInsetsController { private InsetsController mReplayedInsetsController; private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners = new ArrayList<>(); + private int mCaptionInsetsHeight = 0; @Override public void show(int types) { @@ -80,6 +81,11 @@ public class PendingInsetsController implements WindowInsetsController { } @Override + public void setCaptionInsetsHeight(int height) { + mCaptionInsetsHeight = height; + } + + @Override public void setSystemBarsBehavior(int behavior) { if (mReplayedInsetsController != null) { mReplayedInsetsController.setSystemBarsBehavior(behavior); @@ -134,6 +140,9 @@ public class PendingInsetsController implements WindowInsetsController { if (mAppearanceMask != 0) { controller.setSystemBarsAppearance(mAppearance, mAppearanceMask); } + if (mCaptionInsetsHeight != 0) { + controller.setCaptionInsetsHeight(mCaptionInsetsHeight); + } int size = mRequests.size(); for (int i = 0; i < size; i++) { mRequests.get(i).replay(controller); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4922917c911c..da186087a34a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -28768,6 +28768,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, publicAlternatives = "Use {@link WindowInsets#getInsets(int)}") final Rect mStableInsets = new Rect(); + /** + * Current caption insets to the display coordinate. + */ + final Rect mCaptionInsets = new Rect(); + final DisplayCutout.ParcelableWrapper mDisplayCutout = new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 50202aed36d2..51304dcfe8cb 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -158,6 +158,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; +import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneFallbackEventHandler; import com.android.internal.util.Preconditions; import com.android.internal.view.BaseSurfaceHolder; @@ -2221,6 +2222,19 @@ public final class ViewRootImpl implements ViewParent, Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + private boolean updateCaptionInsets() { + if (!(mView instanceof DecorView)) return false; + final int captionInsetsHeight = ((DecorView) mView).getCaptionInsetsHeight(); + final Rect captionFrame = new Rect(); + if (captionInsetsHeight != 0) { + captionFrame.set(mWinFrame.left, mWinFrame.top, mWinFrame.right, + mWinFrame.top + captionInsetsHeight); + } + if (mAttachInfo.mCaptionInsets.equals(captionFrame)) return false; + mAttachInfo.mCaptionInsets.set(captionFrame); + return true; + } + private boolean shouldDispatchCutout() { return mWindowAttributes.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS @@ -2592,6 +2606,9 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars; dispatchApplyInsets = true; } + if (updateCaptionInsets()) { + dispatchApplyInsets = true; + } if (dispatchApplyInsets || mLastSystemUiVisibility != mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) { mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 0c5c18316e61..ae9afabad533 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -49,9 +49,6 @@ import android.transition.Transition; import android.transition.TransitionManager; import android.util.Pair; import android.view.View.OnApplyWindowInsetsListener; -import android.view.ViewGroup.LayoutParams; -import android.view.WindowInsets.Side.InsetsSide; -import android.view.WindowInsets.Type.InsetsType; import android.view.accessibility.AccessibilityEvent; import java.util.Collections; @@ -323,7 +320,7 @@ public abstract class Window { @UnsupportedAppUsage private boolean mDestroyed; - private boolean mOverlayWithDecorCaptionEnabled = false; + private boolean mOverlayWithDecorCaptionEnabled = true; private boolean mCloseOnSwipeEnabled = false; // The current window attributes. diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index fde184ccfb0e..9b2a6cbce48f 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -17,6 +17,7 @@ package android.view; +import static android.view.WindowInsets.Type.CAPTION_BAR; import static android.view.WindowInsets.Type.DISPLAY_CUTOUT; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.IME; diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java index 0282ecac8920..439223cf568b 100644 --- a/core/java/android/view/WindowInsetsController.java +++ b/core/java/android/view/WindowInsetsController.java @@ -196,6 +196,15 @@ public interface WindowInsetsController { @Appearance int getSystemBarsAppearance(); /** + * Notify the caption insets height change. The information will be used on the client side to, + * make sure the InsetsState has the correct caption insets. + * + * @param height the height of caption bar insets. + * @hide + */ + void setCaptionInsetsHeight(int height); + + /** * Controls the behavior of system bars. * * @param behavior Determines how the bars behave when being hidden by the application. diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 51b73fc674e7..d2508f3616e4 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -78,7 +78,6 @@ import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.InputQueue; import android.view.InsetsState; -import android.view.InsetsController; import android.view.InsetsState.InternalInsetsType; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; @@ -1174,6 +1173,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset, animate && !disallowAnimate, mForceWindowDrawsBarBackgrounds, state); + + if (mHasCaption) { + final int captionColor = calculateStatusBarColor(); + mDecorCaptionView.getCaption().setBackgroundColor(captionColor); + updateDecorCaptionShade(); + } } // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or @@ -1355,7 +1360,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind : state.attributes.isPresent(insetsState, mWindow.getAttributes().flags, force); boolean show = state.attributes.isVisible(state.present, color, mWindow.getAttributes().flags, force); - boolean showView = show && !isResizing() && size > 0; + boolean showView = show && !isResizing() && !mHasCaption && size > 0; boolean visibilityChanged = false; View view = state.view; @@ -2021,6 +2026,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind if (getForeground() != null) { drawableChanged(); } + getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight()); } } @@ -2094,6 +2100,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind mDecorCaptionView.onConfigurationChanged(displayWindowDecor); enableCaption(displayWindowDecor); } + getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight()); } void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { @@ -2182,11 +2189,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind inflater = inflater.from(context); final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption, null); - setDecorCaptionShade(context, view); + setDecorCaptionShade(view); return view; } - private void setDecorCaptionShade(Context context, DecorCaptionView view) { + private void setDecorCaptionShade(DecorCaptionView view) { final int shade = mWindow.getDecorCaptionShade(); switch (shade) { case DECOR_CAPTION_SHADE_LIGHT: @@ -2196,15 +2203,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind setDarkDecorCaptionShade(view); break; default: { - TypedValue value = new TypedValue(); - context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true); - // We invert the shade depending on brightness of the theme. Dark shade for light - // theme and vice versa. Thanks to this the buttons should be visible on the - // background. - if (Color.luminance(value.data) < 0.5) { - setLightDecorCaptionShade(view); - } else { + if ((getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0) { setDarkDecorCaptionShade(view); + } else { + setLightDecorCaptionShade(view); } break; } @@ -2213,7 +2215,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind void updateDecorCaptionShade() { if (mDecorCaptionView != null) { - setDecorCaptionShade(getContext(), mDecorCaptionView); + setDecorCaptionShade(mDecorCaptionView); } } @@ -2484,6 +2486,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } /** + * @hide + * @return the height of insets covering the top of window content area. + */ + public int getCaptionInsetsHeight() { + if (!mWindow.isOverlayWithDecorCaptionEnabled()) return 0; + return getCaptionHeight(); + } + + /** * Converts a DIP measure into physical pixels. * @param dip The dip value. * @return Returns the number of pixels. diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java index b5d787c24fbd..7a01024ffc36 100644 --- a/core/java/com/android/internal/widget/DecorCaptionView.java +++ b/core/java/com/android/internal/widget/DecorCaptionView.java @@ -17,7 +17,6 @@ package com.android.internal.widget; import android.content.Context; -import android.graphics.Color; import android.graphics.Rect; import android.os.RemoteException; import android.util.AttributeSet; @@ -53,8 +52,7 @@ import java.util.ArrayList; * <li>..</li> * </ul> * - * Although this ViewGroup has only two direct sub-Views, its behavior is more complex due to - * overlaying caption on the content and drawing. + * Here describe the behavior of overlaying caption on the content and drawing. * * First, no matter where the content View gets added, it will always be the first child and the * caption will be the second. This way the caption will always be drawn on top of the content when @@ -66,11 +64,9 @@ import java.util.ArrayList; * <li>DecorCaptionView.onInterceptTouchEvent() will try intercepting the touch events if the * down action is performed on top close or maximize buttons; the reason for that is we want these * buttons to always work.</li> - * <li>The content View will receive the touch event. Mind that content is actually underneath the - * caption, so we need to introduce our own dispatch ordering. We achieve this by overriding - * {@link #buildTouchDispatchChildList()}.</li> - * <li>If the touch event is not consumed by the content View, it will go to the caption View - * and the dragging logic will be executed.</li> + * <li>The caption view will try to consume the event to apply the dragging logic.</li> + * <li>If the touch event is not consumed by the caption, the content View will receive the touch + * event</li> * </ul> */ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, @@ -137,11 +133,6 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, mOwner = owner; mShow = show; mOverlayWithAppContent = owner.isOverlayWithDecorCaptionEnabled(); - if (mOverlayWithAppContent) { - // The caption is covering the content, so we make its background transparent to make - // the content visible. - mCaption.setBackgroundColor(Color.TRANSPARENT); - } updateCaptionVisibility(); // By changing the outline provider to BOUNDS, the window can remove its // background without removing the shadow. @@ -236,18 +227,6 @@ public class DecorCaptionView extends ViewGroup implements View.OnTouchListener, } @Override - public ArrayList<View> buildTouchDispatchChildList() { - mTouchDispatchList.ensureCapacity(3); - if (mCaption != null) { - mTouchDispatchList.add(mCaption); - } - if (mContent != null) { - mTouchDispatchList.add(mContent); - } - return mTouchDispatchList; - } - - @Override public boolean shouldDelayChildPressedState() { return false; } diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index cb5a332c6e85..bf3fc5704739 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -214,12 +214,8 @@ static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& valu // ---------------------------------------------------------------------------- -static std::unique_ptr<DynamicLibManager> sDynamicLibManager = - std::make_unique<DynamicLibManager>(); - // Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. struct GuardedAssetManager : public ::AAssetManager { - GuardedAssetManager() : guarded_assetmanager(sDynamicLibManager.get()) {} Guarded<AssetManager2> guarded_assetmanager; }; diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index d09273cdd369..075aa97edb58 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -180,6 +180,14 @@ message SecureSettingsProto { optional SettingProto cmas_additional_broadcast_pkg = 14 [ (android.privacy).dest = DEST_AUTOMATIC ]; repeated SettingProto completed_categories = 15; optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ]; + + message Controls { + option (android.msg_privacy).dest = DEST_EXPLICIT; + + optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + optional Controls controls = 79; + optional SettingProto device_paired = 17 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto dialer_default_application = 18 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto display_density_forced = 19 [ (android.privacy).dest = DEST_AUTOMATIC ]; @@ -580,5 +588,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 79; + // Next tag = 80; } diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 28dcc2fdb573..acda77f185fd 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2779,6 +2779,10 @@ <string name="config_systemUIServiceComponent" translatable="false" >com.android.systemui/com.android.systemui.SystemUIService</string> + <!-- Package handling Quick controls --> + <string name="config_controlsPackage" translatable="false" + >com.android.systemui</string> + <!-- Keyguard component --> <string name="config_keyguardComponent" translatable="false" >com.android.systemui/com.android.systemui.keyguard.KeyguardService</string> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5b880365657c..2ae9d994484a 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1138,17 +1138,17 @@ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_accessFineLocation">access precise location only in the foreground</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_accessFineLocation">This app can get your exact location only when it is in the foreground. Location services must be turned on and available on your device for the app to be able to use them. This may increase battery consumption.</string> + <string name="permdesc_accessFineLocation">This app can get your precise location from location services while the app is in use. Location services for your device must be turned on for the app to get location. This may increase battery usage.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_accessCoarseLocation">access approximate location only in the foreground</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_accessCoarseLocation">This app can get your approximate location only when it is in the foreground. Location services must be turned on and available on your device for the app to be able to use them.</string> + <string name="permdesc_accessCoarseLocation">This app can get your approximate location from location services while the app is in use. Location services for your device must be turned on for the app to get location.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_accessBackgroundLocation">access location in the background</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_accessBackgroundLocation">This app can access location while running in the background, in addition to foreground location access.</string> + <string name="permdesc_accessBackgroundLocation">This app can access location at any time, even while the app is not in use.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_modifyAudioSettings">change your audio settings</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 646ffabedb8d..04c6a41833df 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -365,6 +365,7 @@ <java-symbol type="bool" name="config_hasRecents" /> <java-symbol type="string" name="config_recentsComponentName" /> <java-symbol type="string" name="config_systemUIServiceComponent" /> + <java-symbol type="string" name="config_controlsPackage" /> <java-symbol type="string" name="config_screenRecorderComponent" /> <java-symbol type="string" name="config_somnambulatorComponent" /> <java-symbol type="string" name="config_screenshotServiceComponent" /> diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java index 13000e943141..f4ebe2f9a755 100644 --- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java +++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java @@ -64,8 +64,7 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class ControlProviderServiceTest { - private static final ComponentName TEST_SYSUI_COMPONENT = - ComponentName.unflattenFromString("sysui/.test.cls"); + private static final String TEST_CONTROLS_PACKAGE = "sysui"; private static final ComponentName TEST_COMPONENT = ComponentName.unflattenFromString("test.pkg/.test.cls"); @@ -97,8 +96,8 @@ public class ControlProviderServiceTest { when(mSubscriber.asBinder()).thenCallRealMethod(); when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber); - when(mResources.getString(com.android.internal.R.string.config_systemUIServiceComponent)) - .thenReturn(TEST_SYSUI_COMPONENT.flattenToString()); + when(mResources.getString(com.android.internal.R.string.config_controlsPackage)) + .thenReturn(TEST_CONTROLS_PACKAGE); when(mContext.getResources()).thenReturn(mResources); Bundle b = new Bundle(); @@ -252,7 +251,7 @@ public class ControlProviderServiceTest { eq(Manifest.permission.BIND_CONTROLS)); Intent intent = mIntentArgumentCaptor.getValue(); assertEquals(ControlsProviderService.ACTION_ADD_CONTROL, intent.getAction()); - assertEquals(TEST_SYSUI_COMPONENT.getPackageName(), intent.getPackage()); + assertEquals(TEST_CONTROLS_PACKAGE, intent.getPackage()); assertEquals(TEST_COMPONENT, intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME)); assertTrue(equals(control, intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL))); diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index efdb51dcdfa9..cbb379bf8207 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -21,6 +21,7 @@ import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED; import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY; +import static android.view.InsetsState.ITYPE_CAPTION_BAR; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -88,7 +89,6 @@ import java.util.concurrent.CountDownLatch; @Presubmit @RunWith(AndroidJUnit4.class) public class InsetsControllerTest { - private InsetsController mController; private SurfaceSession mSession = new SurfaceSession(); private SurfaceControl mLeash; @@ -665,6 +665,26 @@ public class InsetsControllerTest { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } + @Test + public void testCaptionInsetsStateAssemble() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + mController.onFrameChanged(new Rect(0, 0, 100, 300)); + final InsetsState state = new InsetsState(mController.getState(), true); + final Rect captionFrame = new Rect(0, 0, 100, 100); + mController.setCaptionInsetsHeight(100); + mController.onStateChanged(state); + final InsetsState currentState = new InsetsState(mController.getState()); + // The caption bar source should be synced with the info in mAttachInfo. + assertEquals(captionFrame, currentState.peekSource(ITYPE_CAPTION_BAR).getFrame()); + assertTrue(currentState.equals(state, true /* excludingCaptionInsets*/)); + mController.setCaptionInsetsHeight(0); + mController.onStateChanged(state); + // The caption bar source should not be there at all, because we don't add empty + // caption to the state from the server. + assertNull(mController.getState().peekSource(ITYPE_CAPTION_BAR)); + }); + } + private void waitUntilNextFrame() throws Exception { final CountDownLatch latch = new CountDownLatch(1); Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT, diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index 721dc98ff4d1..2884777fc997 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -153,6 +153,35 @@ public class InsetsStateTest { } } + + @Test + public void testCalculateInsets_captionStatusBarOverlap() throws Exception { + try (InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(ITYPE_STATUS_BAR).setVisible(true); + mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300)); + mState.getSource(ITYPE_CAPTION_BAR).setVisible(true); + + Rect visibleInsets = mState.calculateVisibleInsets( + new Rect(0, 0, 100, 400), SOFT_INPUT_ADJUST_NOTHING); + assertEquals(new Rect(0, 300, 0, 0), visibleInsets); + } + } + + @Test + public void testCalculateInsets_captionBarOffset() throws Exception { + try (InsetsModeSession session = + new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) { + mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300)); + mState.getSource(ITYPE_CAPTION_BAR).setVisible(true); + + Rect visibleInsets = mState.calculateVisibleInsets( + new Rect(0, 0, 150, 400), SOFT_INPUT_ADJUST_NOTHING); + assertEquals(new Rect(0, 300, 0, 0), visibleInsets); + } + } + @Test public void testStripForDispatch() { mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 1efde8681352..59bdf3dad43e 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -27,6 +27,10 @@ applications that come with the platform <permission name="android.permission.INTERACT_ACROSS_USERS" /> </privapp-permissions> + <privapp-permissions package="com.android.angle"> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + </privapp-permissions> + <privapp-permissions package="com.android.apps.tag"> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> </privapp-permissions> diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index f87f98a59a12..02c85aa34f4b 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -44,7 +44,6 @@ cc_library { "AttributeResolution.cpp", "ChunkIterator.cpp", "ConfigDescription.cpp", - "DynamicLibManager.cpp", "Idmap.cpp", "LoadedArsc.cpp", "Locale.cpp", diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index f20e18453f8b..eaf452b5fa71 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -25,7 +25,6 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" #include "utils/ByteOrder.h" @@ -67,12 +66,7 @@ struct FindEntryResult { StringPoolRef entry_string_ref; }; -AssetManager2::AssetManager2() : dynamic_lib_manager_(std::make_unique<DynamicLibManager>()) { - memset(&configuration_, 0, sizeof(configuration_)); -} - -AssetManager2::AssetManager2(DynamicLibManager* dynamic_lib_manager) - : dynamic_lib_manager_(dynamic_lib_manager) { +AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -91,6 +85,9 @@ void AssetManager2::BuildDynamicRefTable() { package_groups_.clear(); package_ids_.fill(0xff); + // A mapping from apk assets path to the runtime package id of its first loaded package. + std::unordered_map<std::string, uint8_t> apk_assets_package_ids; + // Overlay resources are not directly referenced by an application so their resource ids // can change throughout the application's lifetime. Assign overlay package ids last. std::vector<const ApkAssets*> sorted_apk_assets(apk_assets_); @@ -98,37 +95,25 @@ void AssetManager2::BuildDynamicRefTable() { return !a->IsOverlay(); }); - std::unordered_map<std::string, uint8_t> apk_assets_package_ids; - std::unordered_map<std::string, uint8_t> package_name_package_ids; - - // Assign stable package ids to application packages. - uint8_t next_available_package_id = 0U; - for (const auto& apk_assets : sorted_apk_assets) { - for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { - uint8_t package_id = package->GetPackageId(); - if (package->IsOverlay()) { - package_id = GetDynamicLibManager()->FindUnassignedId(next_available_package_id); - next_available_package_id = package_id + 1; - } else if (package->IsDynamic()) { - package_id = GetDynamicLibManager()->GetAssignedId(package->GetPackageName()); - } - - // Map the path of the apk assets to the package id of its first loaded package. - apk_assets_package_ids[apk_assets->GetPath()] = package_id; - - // Map the package name of the package to the first loaded package with that package id. - package_name_package_ids[package->GetPackageName()] = package_id; - } + // The assets cookie must map to the position of the apk assets in the unsorted apk assets list. + std::unordered_map<const ApkAssets*, ApkAssetsCookie> apk_assets_cookies; + apk_assets_cookies.reserve(apk_assets_.size()); + for (size_t i = 0, n = apk_assets_.size(); i < n; i++) { + apk_assets_cookies[apk_assets_[i]] = static_cast<ApkAssetsCookie>(i); } - const int apk_assets_count = apk_assets_.size(); - for (int i = 0; i < apk_assets_count; i++) { - const auto& apk_assets = apk_assets_[i]; - for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) { - const auto package_id_entry = package_name_package_ids.find(package->GetPackageName()); - CHECK(package_id_entry != package_name_package_ids.end()) - << "no package id assgined to package " << package->GetPackageName(); - const uint8_t package_id = package_id_entry->second; + // 0x01 is reserved for the android package. + int next_package_id = 0x02; + for (const ApkAssets* apk_assets : sorted_apk_assets) { + const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc(); + for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) { + // Get the package ID or assign one if a shared library. + int package_id; + if (package->IsDynamic()) { + package_id = next_package_id++; + } else { + package_id = package->GetPackageId(); + } // Add the mapping for package ID to index if not present. uint8_t idx = package_ids_[package_id]; @@ -162,7 +147,7 @@ void AssetManager2::BuildDynamicRefTable() { target_package_group.overlays_.push_back( ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id, overlay_table.get()), - static_cast<ApkAssetsCookie>(i)}); + apk_assets_cookies[apk_assets]}); } } @@ -174,7 +159,7 @@ void AssetManager2::BuildDynamicRefTable() { // Add the package and to the set of packages with the same ID. package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); - package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i)); + package_group->cookies_.push_back(apk_assets_cookies[apk_assets]); // Add the package name -> build time ID mappings. for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) { @@ -182,6 +167,8 @@ void AssetManager2::BuildDynamicRefTable() { package_group->dynamic_ref_table->mEntries.replaceValueFor( package_name, static_cast<uint8_t>(entry.package_id)); } + + apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id)); } } @@ -1329,16 +1316,6 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const return 0; } -DynamicLibManager* AssetManager2::GetDynamicLibManager() const { - auto dynamic_lib_manager = - std::get_if<std::unique_ptr<DynamicLibManager>>(&dynamic_lib_manager_); - if (dynamic_lib_manager) { - return (*dynamic_lib_manager).get(); - } else { - return *std::get_if<DynamicLibManager*>(&dynamic_lib_manager_); - } -} - std::unique_ptr<Theme> AssetManager2::NewTheme() { return std::unique_ptr<Theme>(new Theme(this)); } diff --git a/libs/androidfw/DynamicLibManager.cpp b/libs/androidfw/DynamicLibManager.cpp deleted file mode 100644 index 895b7695bf26..000000000000 --- a/libs/androidfw/DynamicLibManager.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "androidfw/DynamicLibManager.h" - -namespace android { - -uint8_t DynamicLibManager::GetAssignedId(const std::string& library_package_name) { - auto lib_entry = shared_lib_package_ids_.find(library_package_name); - if (lib_entry != shared_lib_package_ids_.end()) { - return lib_entry->second; - } - - return shared_lib_package_ids_[library_package_name] = next_package_id_++; -} - -uint8_t DynamicLibManager::FindUnassignedId(uint8_t start_package_id) { - return (start_package_id < next_package_id_) ? next_package_id_ : start_package_id; -} - -} // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index b2cec2a42994..e21abade99a4 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -27,7 +27,6 @@ #include "androidfw/ApkAssets.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" -#include "androidfw/DynamicLibManager.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" @@ -95,7 +94,6 @@ class AssetManager2 { }; AssetManager2(); - explicit AssetManager2(DynamicLibManager* dynamic_lib_manager); // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets // are not owned by the AssetManager, and must have a longer lifetime. @@ -126,6 +124,9 @@ class AssetManager2 { // This may be nullptr if the APK represented by `cookie` has no resource table. std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const; + // Retrieve the assigned package id of the package if loaded into this AssetManager + uint8_t GetAssignedPackageId(const LoadedPackage* package) const; + // Returns a string representation of the overlayable API of a package. bool GetOverlayablesToString(const android::StringPiece& package_name, std::string* out) const; @@ -370,11 +371,6 @@ class AssetManager2 { // been seen while traversing bag parents. const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids); - // Retrieve the assigned package id of the package if loaded into this AssetManager - uint8_t GetAssignedPackageId(const LoadedPackage* package) const; - - DynamicLibManager* GetDynamicLibManager() const; - // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector<const ApkAssets*> apk_assets_; @@ -393,9 +389,6 @@ class AssetManager2 { // may need to be purged. ResTable_config configuration_; - // Component responsible for assigning package ids to shared libraries. - std::variant<std::unique_ptr<DynamicLibManager>, DynamicLibManager*> dynamic_lib_manager_; - // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; diff --git a/libs/androidfw/include/androidfw/DynamicLibManager.h b/libs/androidfw/include/androidfw/DynamicLibManager.h deleted file mode 100644 index 1ff7079573d2..000000000000 --- a/libs/androidfw/include/androidfw/DynamicLibManager.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROIDFW_DYNAMICLIBMANAGER_H -#define ANDROIDFW_DYNAMICLIBMANAGER_H - -#include <string> -#include <unordered_map> - -#include "android-base/macros.h" - -namespace android { - -// Manages assigning resource ids for dynamic resources. -class DynamicLibManager { - public: - DynamicLibManager() = default; - - // Retrieves the assigned package id for the library. - uint8_t GetAssignedId(const std::string& library_package_name); - - // Queries in ascending order for the first available package id that is not currently assigned to - // a library. - uint8_t FindUnassignedId(uint8_t start_package_id); - - private: - DISALLOW_COPY_AND_ASSIGN(DynamicLibManager); - - uint8_t next_package_id_ = 0x02; - std::unordered_map<std::string, uint8_t> shared_lib_package_ids_; -}; - -} // namespace android - -#endif //ANDROIDFW_DYNAMICLIBMANAGER_H diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h index 8891512958f0..64924f433245 100644 --- a/libs/androidfw/include/androidfw/MutexGuard.h +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -47,8 +47,7 @@ class Guarded { static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer"); public: - template <typename ...Args> - explicit Guarded(Args&& ...args) : guarded_(std::forward<Args>(args)...) { + explicit Guarded() : guarded_() { } template <typename U = T> diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index ac32699c6dfd..8c255d16fe1f 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -17,9 +17,9 @@ #include "androidfw/AssetManager2.h" #include "androidfw/AssetManager.h" -#include "android-base/logging.h" - #include "TestHelpers.h" +#include "android-base/file.h" +#include "android-base/logging.h" #include "androidfw/ResourceUtils.h" #include "data/appaslib/R.h" #include "data/basic/R.h" @@ -45,37 +45,43 @@ namespace android { class AssetManager2Test : public ::testing::Test { public: void SetUp() override { - basic_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + // Move to the test data directory so the idmap can locate the overlay APK. + std::string original_path = base::GetExecutableDirectory(); + chdir(GetTestDataPath().c_str()); + + basic_assets_ = ApkAssets::Load("basic/basic.apk"); ASSERT_NE(nullptr, basic_assets_); - basic_de_fr_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic_de_fr.apk"); + basic_de_fr_assets_ = ApkAssets::Load("basic/basic_de_fr.apk"); ASSERT_NE(nullptr, basic_de_fr_assets_); - style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + style_assets_ = ApkAssets::Load("styles/styles.apk"); ASSERT_NE(nullptr, style_assets_); - lib_one_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_one/lib_one.apk"); + lib_one_assets_ = ApkAssets::Load("lib_one/lib_one.apk"); ASSERT_NE(nullptr, lib_one_assets_); - lib_two_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_two/lib_two.apk"); + lib_two_assets_ = ApkAssets::Load("lib_two/lib_two.apk"); ASSERT_NE(nullptr, lib_two_assets_); - libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); + libclient_assets_ = ApkAssets::Load("libclient/libclient.apk"); ASSERT_NE(nullptr, libclient_assets_); - appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk", - PROPERTY_DYNAMIC); + appaslib_assets_ = ApkAssets::Load("appaslib/appaslib.apk", PROPERTY_DYNAMIC); ASSERT_NE(nullptr, appaslib_assets_); - system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", - PROPERTY_SYSTEM); + system_assets_ = ApkAssets::Load("system/system.apk", PROPERTY_SYSTEM); ASSERT_NE(nullptr, system_assets_); - app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk"); + app_assets_ = ApkAssets::Load("app/app.apk"); ASSERT_THAT(app_assets_, NotNull()); - overlayable_assets_ = ApkAssets::Load(GetTestDataPath() + "/overlayable/overlayable.apk"); + overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap"); + ASSERT_NE(nullptr, overlay_assets_); + + overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk"); ASSERT_THAT(overlayable_assets_, NotNull()); + chdir(original_path.c_str()); } protected: @@ -88,6 +94,7 @@ class AssetManager2Test : public ::testing::Test { std::unique_ptr<const ApkAssets> appaslib_assets_; std::unique_ptr<const ApkAssets> system_assets_; std::unique_ptr<const ApkAssets> app_assets_; + std::unique_ptr<const ApkAssets> overlay_assets_; std::unique_ptr<const ApkAssets> overlayable_assets_; }; @@ -216,23 +223,24 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); } -TEST_F(AssetManager2Test, AssignsUnchangingPackageIdToSharedLibrary) { - DynamicLibManager lib_manager; - AssetManager2 assetmanager(&lib_manager); +TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) { + AssetManager2 assetmanager; assetmanager.SetApkAssets( - {lib_one_assets_.get(), lib_two_assets_.get(), libclient_assets_.get()}); - - AssetManager2 assetmanager2(&lib_manager); - assetmanager2.SetApkAssets( - {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + {overlayable_assets_.get(), overlay_assets_.get(), lib_one_assets_.get()}); - uint32_t res_id = assetmanager.GetResourceId("com.android.lib_one:string/foo"); - ASSERT_NE(0U, res_id); + auto apk_assets = assetmanager.GetApkAssets(); + ASSERT_EQ(3, apk_assets.size()); + ASSERT_EQ(overlayable_assets_.get(), apk_assets[0]); + ASSERT_EQ(overlay_assets_.get(), apk_assets[1]); + ASSERT_EQ(lib_one_assets_.get(), apk_assets[2]); - uint32_t res_id_2 = assetmanager2.GetResourceId("com.android.lib_one:string/foo"); - ASSERT_NE(0U, res_id_2); + auto get_first_package_id = [&assetmanager](const ApkAssets* apkAssets) -> uint8_t { + return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get()); + }; - ASSERT_EQ(res_id, res_id_2); + ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f); + ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03); + ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02); } TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { @@ -770,7 +778,6 @@ TEST_F(AssetManager2Test, GetOverlayablesToString) { ASSERT_EQ(api.find("not_overlayable"), std::string::npos); ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"), std::string::npos); - } } // namespace android diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index 9950f05560d7..db2a1e8b6e7c 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -432,7 +432,7 @@ public final class AudioDeviceInfo { * @return An array of supported encapsulation modes for the device. This * may be an empty array if no encapsulation modes are supported. */ - public @NonNull int[] getEncapsulationModes() { + public @NonNull @AudioTrack.EncapsulationMode int[] getEncapsulationModes() { // Implement a getter in r-dev or r-tv-dev as needed. return new int[0]; // be careful of returning a copy of any internal data. } @@ -451,7 +451,7 @@ public final class AudioDeviceInfo { * @return An array of supported encapsulation metadata types for the device. This * may be an empty array if no metadata types are supported. */ - public @NonNull int[] getEncapsulationMetadataTypes() { + public @NonNull @AudioTrack.EncapsulationMetadataType int[] getEncapsulationMetadataTypes() { // Implement a getter in r-dev or r-tv-dev as needed. return new int[0]; // be careful of returning a copy of any internal data. } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index fffdd683f22c..ba8f7e60e268 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4769,7 +4769,7 @@ public class AudioManager { * opened on that device. * * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}. - * @param delayMs delay in milliseconds desired. This should be in range of {@code 0} + * @param delayMillis delay in milliseconds desired. This should be in range of {@code 0} * to the value returned by {@link #getMaxAdditionalOutputDeviceDelay()}. * @return true if successful, false if the device does not support output device delay * or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}. @@ -4777,7 +4777,7 @@ public class AudioManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay( - @NonNull AudioDeviceInfo device, @IntRange(from = 0) int delayMs) { + @NonNull AudioDeviceInfo device, @IntRange(from = 0) long delayMillis) { Objects.requireNonNull(device); // Implement the setter in r-dev or r-tv-dev as needed. return false; @@ -4793,7 +4793,7 @@ public class AudioManager { */ @SystemApi @IntRange(from = 0) - public int getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) { + public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) { Objects.requireNonNull(device); // Implement the getter in r-dev or r-tv-dev as needed. return 0; @@ -4811,7 +4811,7 @@ public class AudioManager { */ @SystemApi @IntRange(from = 0) - public int getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) { + public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) { Objects.requireNonNull(device); // Implement the getter in r-dev or r-tv-dev as needed. return 0; diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java index 1a9517cafdde..c91ff0d099cf 100644 --- a/media/java/android/media/AudioMetadata.java +++ b/media/java/android/media/AudioMetadata.java @@ -79,96 +79,11 @@ public final class AudioMetadata { } /** - * A read only {@code Map} interface of {@link Key} value pairs. - * - * <p>Using a {@link Key} interface, the map looks up the corresponding value.</p> - */ - public interface ReadMap { - /** - * Returns true if the key exists in the map. - * - * @param key interface for requesting the value. - * @param <T> type of value. - * @return true if key exists in the Map. - */ - <T> boolean containsKey(@NonNull Key<T> key); - - /** - * Returns a copy of the map. - * - * This is intended for safe conversion between a {@link ReadMap} - * interface and a {@link Map} interface. - * Currently only simple objects are used for key values which - * means a shallow copy is sufficient. - * - * @return a Map copied from the existing map. - */ - @NonNull - Map dup(); // lint checker doesn't like clone(). - - /** - * Returns the value associated with the key. - * - * @param key interface for requesting the value. - * @param <T> type of value. - * @return returns the value of associated with key or null if it doesn't exist. - */ - @Nullable - <T> T get(@NonNull Key<T> key); - - /** - * Returns a {@code Set} of keys associated with the map. - * @hide - */ - @NonNull - Set<Key<?>> keySet(); - - /** - * Returns the number of elements in the map. - */ - int size(); - } - - /** - * A writeable {@link Map} interface of {@link Key} value pairs. - * This interface is not guaranteed to be thread-safe - * unless the supplier for the {@code Map} states it as thread safe. - */ - // TODO: Create a wrapper like java.util.Collections.synchronizedMap? - public interface Map extends ReadMap { - /** - * Removes the value associated with the key. - * @param key interface for storing the value. - * @param <T> type of value. - * @return the value of the key, null if it doesn't exist. - */ - @Nullable - <T> T remove(@NonNull Key<T> key); - - /** - * Sets a value for the key. - * - * @param key interface for storing the value. - * @param <T> type of value. - * @param value a non-null value of type T. - * @return the previous value associated with key or null if it doesn't exist. - */ - // See automatic Kotlin overloading for Java interoperability. - // https://kotlinlang.org/docs/reference/java-interop.html#operators - // See also Kotlin set for overloaded operator indexing. - // https://kotlinlang.org/docs/reference/operator-overloading.html#indexed - // Also the Kotlin mutable-list set. - // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/set.html - @Nullable - <T> T set(@NonNull Key<T> key, @NonNull T value); - } - - /** - * Creates a {@link Map} suitable for adding keys. - * @return an empty {@link Map} instance. + * Creates a {@link AudioMetadataMap} suitable for adding keys. + * @return an empty {@link AudioMetadataMap} instance. */ @NonNull - public static Map createMap() { + public static AudioMetadataMap createMap() { return new BaseMap(); } @@ -339,7 +254,7 @@ public final class AudioMetadata { * It is possible to require the keys to be of a certain class * before allowing a set or get operation. */ - public static class BaseMap implements Map { + public static class BaseMap implements AudioMetadataMap { @Override public <T> boolean containsKey(@NonNull Key<T> key) { Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key)); @@ -348,7 +263,7 @@ public final class AudioMetadata { @Override @NonNull - public Map dup() { + public AudioMetadataMap dup() { BaseMap map = new BaseMap(); map.mHashMap.putAll(this.mHashMap); return map; diff --git a/media/java/android/media/AudioMetadataMap.java b/media/java/android/media/AudioMetadataMap.java new file mode 100644 index 000000000000..196193174754 --- /dev/null +++ b/media/java/android/media/AudioMetadataMap.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +/** + * AudioMetadataMap is a writeable {@code Map}-style + * interface of {@link AudioMetadata.Key} value pairs. + * This interface is not guaranteed to be thread-safe + * unless the underlying implementation for the {@code AudioMetadataMap} + * states it as thread safe. + * + * {@see AudioMetadataReadMap} + */ +// TODO: Create a wrapper like java.util.Collections.synchronizedMap? + +public interface AudioMetadataMap extends AudioMetadataReadMap { + /** + * Removes the value associated with the key. + * @param key interface for storing the value. + * @param <T> type of value. + * @return the value of the key, null if it doesn't exist. + */ + @Nullable + <T> T remove(@NonNull AudioMetadata.Key<T> key); + + /** + * Sets a value for the key. + * + * @param key interface for storing the value. + * @param <T> type of value. + * @param value a non-null value of type T. + * @return the previous value associated with key or null if it doesn't exist. + */ + // See automatic Kotlin overloading for Java interoperability. + // https://kotlinlang.org/docs/reference/java-interop.html#operators + // See also Kotlin set for overloaded operator indexing. + // https://kotlinlang.org/docs/reference/operator-overloading.html#indexed + // Also the Kotlin mutable-list set. + // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/set.html + @Nullable + <T> T set(@NonNull AudioMetadata.Key<T> key, @NonNull T value); +} diff --git a/media/java/android/media/AudioMetadataReadMap.java b/media/java/android/media/AudioMetadataReadMap.java new file mode 100644 index 000000000000..e74242a292d4 --- /dev/null +++ b/media/java/android/media/AudioMetadataReadMap.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.util.Set; + +/** + * A read only {@code Map}-style interface of {@link AudioMetadata.Key} value pairs used + * for {@link AudioMetadata}. + * + * <p>Using a {@link AudioMetadata.Key} interface, + * this map looks up the corresponding value. + * Read-only maps are thread-safe for lookup, but the underlying object + * values may need their own thread protection if mutable.</p> + * + * {@see AudioMetadataMap} + */ +public interface AudioMetadataReadMap { + /** + * Returns true if the key exists in the map. + * + * @param key interface for requesting the value. + * @param <T> type of value. + * @return true if key exists in the Map. + */ + <T> boolean containsKey(@NonNull AudioMetadata.Key<T> key); + + /** + * Returns a copy of the map. + * + * This is intended for safe conversion between a {@link AudioMetadataReadMap} + * interface and a {@link AudioMetadataMap} interface. + * Currently only simple objects are used for key values which + * means a shallow copy is sufficient. + * + * @return a Map copied from the existing map. + */ + @NonNull + AudioMetadataMap dup(); // lint checker doesn't like clone(). + + /** + * Returns the value associated with the key. + * + * @param key interface for requesting the value. + * @param <T> type of value. + * @return returns the value of associated with key or null if it doesn't exist. + */ + @Nullable + <T> T get(@NonNull AudioMetadata.Key<T> key); + + /** + * Returns a {@code Set} of keys associated with the map. + * @hide + */ + @NonNull + Set<AudioMetadata.Key<?>> keySet(); + + /** + * Returns the number of elements in the map. + */ + @IntRange(from = 0) + int size(); +} diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index d17e42996726..1d229b80cb2c 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -918,7 +918,29 @@ public class AudioTrack extends PlayerBase private final int mContentId; private final int mSyncId; - private TunerConfiguration(int contentId, int syncId) { + /** + * Constructs a TunerConfiguration instance for use in {@link AudioTrack.Builder} + * + * @param contentId selects the audio stream to use. + * The contentId may be obtained from + * {@link android.media.tv.tuner.filter.Filter#getId()}. + * This is always a positive number. + * @param syncId selects the clock to use for synchronization + * of audio with other streams such as video. + * The syncId may be obtained from + * {@link android.media.tv.tuner.Tuner#getAvSyncHwId()}. + * This is always a positive number. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public TunerConfiguration( + @IntRange(from = 1) int contentId, @IntRange(from = 1)int syncId) { + if (contentId < 1) { + throw new IllegalArgumentException( + "contentId " + contentId + " must be positive"); + } + if (syncId < 1) { + throw new IllegalArgumentException("syncId " + syncId + " must be positive"); + } mContentId = contentId; mSyncId = syncId; } @@ -938,73 +960,6 @@ public class AudioTrack extends PlayerBase public @IntRange(from = 1) int getSyncId() { return mSyncId; // The Builder ensures this is > 0. } - - /** - * Builder class for {@link AudioTrack.TunerConfiguration} objects. - */ - public static class Builder { - private int mContentId; - private int mSyncId; - - /** - * Sets the contentId from the Tuner filter. - * - * @param contentId selects the audio stream to use. - * The contentId may be obtained from - * {@link android.media.tv.tuner.filter.Filter#getId()}. - * This is always a positive number. - * - * @return the same Builder instance. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public @NonNull Builder setContentId(@IntRange(from = 1) int contentId) { - if (contentId < 1) { - throw new IllegalArgumentException( - "contentId " + contentId + " must be positive"); - } - mContentId = contentId; - return this; - } - - /** - * Sets the syncId from the Tuner filter. - * - * @param syncId selects the clock to use for synchronization - * of audio with other streams such as video. - * The syncId may be obtained from - * {@link android.media.tv.tuner.Tuner#getAvSyncHwId()}. - * This is always a positive number. - * - * @return the same Builder instance. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public @NonNull Builder setSyncId(@IntRange(from = 1) int syncId) { - if (syncId < 1) { - throw new IllegalArgumentException("syncId " + syncId + " must be positive"); - } - mSyncId = syncId; - return this; - } - - /** - * Builds a {@link AudioTrack.TunerConfiguration} instance initialized with - * the parameters set on this {@code Builder}. - * - * @return a new successfully initialized {@link AudioTrack.TunerConfiguration}. - * @throws UnsupportedOperationException if the parameters set on the - * {@code Builder} are incompatible. - */ - @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public @NonNull TunerConfiguration build() { - if (mContentId < 1 || mSyncId < 1) { - throw new UnsupportedOperationException( - "mContentId " + mContentId - + " mSyncId " + mSyncId - + " must be set"); - } - return new TunerConfiguration(mContentId, mSyncId); - } - } } /** @@ -3673,7 +3628,7 @@ public class AudioTrack extends PlayerBase // OnCodecFormatChangedListener notifications uses an instance // of ListenerList to manage its listeners. - private final Utils.ListenerList<AudioMetadata.ReadMap> mCodecFormatChangedListeners = + private final Utils.ListenerList<AudioMetadataReadMap> mCodecFormatChangedListeners = new Utils.ListenerList(); /** @@ -3684,13 +3639,13 @@ public class AudioTrack extends PlayerBase * Called when the compressed codec format changes. * * @param audioTrack is the {@code AudioTrack} instance associated with the codec. - * @param info is a {@link AudioMetadata.ReadMap} of values which contains decoded format + * @param info is a {@link AudioMetadataReadMap} of values which contains decoded format * changes reported by the codec. Not all hardware * codecs indicate codec format changes. Acceptable keys are taken from * {@code AudioMetadata.Format.KEY_*} range, with the associated value type. */ void onCodecFormatChanged( - @NonNull AudioTrack audioTrack, @Nullable AudioMetadata.ReadMap info); + @NonNull AudioTrack audioTrack, @Nullable AudioMetadataReadMap info); } /** @@ -3708,7 +3663,7 @@ public class AudioTrack extends PlayerBase mCodecFormatChangedListeners.add( listener, /* key for removal */ executor, - (int eventCode, AudioMetadata.ReadMap readMap) -> { + (int eventCode, AudioMetadataReadMap readMap) -> { // eventCode is unused by this implementation. listener.onCodecFormatChanged(this, readMap); } @@ -4067,7 +4022,7 @@ public class AudioTrack extends PlayerBase ByteBuffer buffer = (ByteBuffer) obj; buffer.order(ByteOrder.nativeOrder()); buffer.rewind(); - AudioMetadata.ReadMap audioMetaData = AudioMetadata.fromByteBuffer(buffer); + AudioMetadataReadMap audioMetaData = AudioMetadata.fromByteBuffer(buffer); if (audioMetaData == null) { Log.e(TAG, "Unable to get audio metadata from byte buffer"); return; diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 998561316dd4..e5ad569bb24f 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -663,8 +663,8 @@ public final class MediaRoute2Info implements Parcelable { } /** - * Constructor for builder to create {@link MediaRoute2Info} with - * existing {@link MediaRoute2Info} instance. + * Constructor for builder to create {@link MediaRoute2Info} with existing + * {@link MediaRoute2Info} instance. * * @param routeInfo the existing instance to copy data from. */ @@ -690,6 +690,38 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Constructor for builder to create {@link MediaRoute2Info} with existing + * {@link MediaRoute2Info} instance and replace ID with the given {@code id}. + * + * @param id The ID of the new route. Must not be empty. + * @param routeInfo the existing instance to copy data from. + * @hide + */ + public Builder(@NonNull String id, @NonNull MediaRoute2Info routeInfo) { + if (TextUtils.isEmpty(id)) { + throw new IllegalArgumentException("id must not be empty"); + } + Objects.requireNonNull(routeInfo, "routeInfo must not be null"); + + mId = id; + mName = routeInfo.mName; + mFeatures = new ArrayList<>(routeInfo.mFeatures); + mType = routeInfo.mType; + mIsSystem = routeInfo.mIsSystem; + mIconUri = routeInfo.mIconUri; + mDescription = routeInfo.mDescription; + mConnectionState = routeInfo.mConnectionState; + mClientPackageName = routeInfo.mClientPackageName; + mVolumeHandling = routeInfo.mVolumeHandling; + mVolumeMax = routeInfo.mVolumeMax; + mVolume = routeInfo.mVolume; + if (routeInfo.mExtras != null) { + mExtras = new Bundle(routeInfo.mExtras); + } + mProviderId = routeInfo.mProviderId; + } + + /** * Adds a feature for the route. * @param feature a feature that the route has. May be one of predefined features * such as {@link #FEATURE_LIVE_AUDIO}, {@link #FEATURE_LIVE_VIDEO} or diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 3c0ddc4b65c5..736e995451cd 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -96,6 +96,7 @@ public class SecureSettings { Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, Settings.Secure.QS_TILES, + Settings.Secure.CONTROLS_ENABLED, Settings.Secure.DOZE_ENABLED, Settings.Secure.DOZE_ALWAYS_ON, Settings.Secure.DOZE_PICK_UP_GESTURE, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 3e64a378bc6a..b413e8e9dda2 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -141,6 +141,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.QS_TILES, TILE_LIST_VALIDATOR); + VALIDATORS.put(Secure.CONTROLS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DOZE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DOZE_ALWAYS_ON, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DOZE_PICK_UP_GESTURE, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 6fba15f5381f..8a7b9134a5d9 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1972,6 +1972,13 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS); + + final long controlsToken = p.start(SecureSettingsProto.CONTROLS); + dumpSetting(s, p, + Settings.Secure.CONTROLS_ENABLED, + SecureSettingsProto.Controls.ENABLED); + p.end(controlsToken); + dumpSetting(s, p, Settings.Secure.DEVICE_PAIRED, SecureSettingsProto.DEVICE_PAIRED); diff --git a/packages/SystemUI/res/drawable/control_background_ripple.xml b/packages/SystemUI/res/drawable/control_background_ripple.xml new file mode 100644 index 000000000000..37914e272811 --- /dev/null +++ b/packages/SystemUI/res/drawable/control_background_ripple.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <color android:color="@android:color/white" /> + </item> + <item android:drawable="@drawable/control_background" /> +</ripple>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml index 6e1fd2072b32..b83e500fbaeb 100644 --- a/packages/SystemUI/res/layout/controls_base_item.xml +++ b/packages/SystemUI/res/layout/controls_base_item.xml @@ -50,6 +50,7 @@ app:layout_constraintBottom_toBottomOf="@+id/icon" app:layout_constraintStart_toEndOf="@+id/icon" /> + <TextView android:id="@+id/status_extra" android:layout_width="wrap_content" @@ -64,7 +65,7 @@ <TextView android:id="@+id/title" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.Control.Title" android:paddingLeft="@dimen/control_padding_adjustment" @@ -73,12 +74,20 @@ android:focusable="false" android:maxLines="1" android:ellipsize="end" - app:layout_constraintBottom_toTopOf="@+id/subtitle" - app:layout_constraintStart_toStartOf="parent" /> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toTopOf="@id/barrier"/> + + <androidx.constraintlayout.widget.Barrier + android:id="@+id/barrier" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:barrierDirection="top" + app:constraint_referenced_ids="subtitle,favorite" /> <TextView android:id="@+id/subtitle" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.Control.Subtitle" android:paddingLeft="@dimen/control_padding_adjustment" @@ -88,24 +97,22 @@ android:focusable="false" android:maxLines="1" android:ellipsize="end" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@id/favorite" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent"/> + /> - <FrameLayout - android:id="@+id/favorite_container" + <CheckBox + android:id="@+id/favorite" android:visibility="gone" - android:layout_width="48dp" - android:layout_height="48dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|end" + android:button="@drawable/controls_btn_star" + android:layout_marginTop="4dp" + android:layout_marginStart="4dp" + app:layout_constraintStart_toEndOf="@id/subtitle" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toBottomOf="parent"> - - <CheckBox - android:id="@+id/favorite" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="bottom|end" - android:button="@drawable/controls_btn_star"/> - </FrameLayout> - + app:layout_constraintBottom_toBottomOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml new file mode 100644 index 000000000000..6da0c693f389 --- /dev/null +++ b/packages/SystemUI/res/values-television/dimens.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<resources> + <!-- Opacity at which the background for the shutdown UI will be drawn. --> + <item name="shutdown_scrim_behind_alpha" format="float" type="dimen">1.0</item> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-television/styles.xml b/packages/SystemUI/res/values-television/styles.xml index b59f0072b8c3..b01c5d88e3b3 100644 --- a/packages/SystemUI/res/values-television/styles.xml +++ b/packages/SystemUI/res/values-television/styles.xml @@ -17,4 +17,9 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog" /> <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert" /> + + <style name="Animation.ShutdownUi"> + <item name="android:windowEnterAnimation">@null</item> + <item name="android:windowExitAnimation">@null</item> + </style> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ee7f5230145e..864442ecd0c5 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1266,4 +1266,7 @@ <dimen name="screenrecord_status_icon_radius">5dp</dimen> <dimen name="kg_user_switcher_text_size">16sp</dimen> + + <!-- Opacity at which the background for the shutdown UI will be drawn. --> + <item name="shutdown_scrim_behind_alpha" format="float" type="dimen">0.95</item> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 1233d4dc73e7..9f1e63e0aa27 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -305,6 +305,9 @@ <item name="android:windowExitAnimation">@null</item> </style> + <style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast"> + </style> + <!-- Standard animations for hiding and showing the status bar. --> <style name="Animation.StatusBar"> </style> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java index a1cb7f61ad04..0b59ebcd57e9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java @@ -28,6 +28,8 @@ import com.android.launcher3.icons.DotRenderer; import com.android.systemui.Interpolators; import com.android.systemui.R; +import java.util.EnumSet; + /** * View that displays an adaptive icon with an app-badge and a dot. * @@ -42,12 +44,27 @@ public class BadgedImageView extends ImageView { /** Same as value in Launcher3 IconShape */ public static final int DEFAULT_PATH_SIZE = 100; - static final int DOT_STATE_DEFAULT = 0; - static final int DOT_STATE_SUPPRESSED_FOR_FLYOUT = 1; - static final int DOT_STATE_ANIMATING = 2; + /** + * Flags that suppress the visibility of the 'new' dot, for one reason or another. If any of + * these flags are set, the dot will not be shown even if {@link Bubble#showDot()} returns true. + */ + enum SuppressionFlag { + // Suppressed because the flyout is visible - it will morph into the dot via animation. + FLYOUT_VISIBLE, + // Suppressed because this bubble is behind others in the collapsed stack. + BEHIND_STACK, + } - // Flyout gets shown before the dot - private int mCurrentDotState = DOT_STATE_SUPPRESSED_FOR_FLYOUT; + /** + * Start by suppressing the dot because the flyout is visible - most bubbles are added with a + * flyout, so this is a reasonable default. + */ + private final EnumSet<SuppressionFlag> mDotSuppressionFlags = + EnumSet.of(SuppressionFlag.FLYOUT_VISIBLE); + + private float mDotScale = 0f; + private float mAnimatingToDotScale = 0f; + private boolean mDotIsAnimating = false; private BubbleViewProvider mBubble; @@ -57,8 +74,6 @@ public class BadgedImageView extends ImageView { private boolean mOnLeft; private int mDotColor; - private float mDotScale = 0f; - private boolean mDotDrawn; private Rect mTempBounds = new Rect(); @@ -88,23 +103,21 @@ public class BadgedImageView extends ImageView { /** * Updates the view with provided info. */ - public void update(BubbleViewProvider bubble) { + public void setRenderedBubble(BubbleViewProvider bubble) { mBubble = bubble; setImageBitmap(bubble.getBadgedImage()); - setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT); mDotColor = bubble.getDotColor(); drawDot(bubble.getDotPath()); - animateDot(); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - if (isDotHidden()) { - mDotDrawn = false; + + if (!shouldDrawDot()) { return; } - mDotDrawn = mDotScale > 0.1f; + getDrawingRect(mTempBounds); mDrawParams.color = mDotColor; @@ -115,23 +128,33 @@ public class BadgedImageView extends ImageView { mDotRenderer.draw(canvas, mDrawParams); } - /** - * Sets the dot state, does not animate changes. - */ - void setDotState(int state) { - mCurrentDotState = state; - if (state == DOT_STATE_SUPPRESSED_FOR_FLYOUT || state == DOT_STATE_DEFAULT) { - mDotScale = mBubble.showDot() ? 1f : 0f; - invalidate(); + /** Adds a dot suppression flag, updating dot visibility if needed. */ + void addDotSuppressionFlag(SuppressionFlag flag) { + if (mDotSuppressionFlags.add(flag)) { + // Update dot visibility, and animate out if we're now behind the stack. + updateDotVisibility(flag == SuppressionFlag.BEHIND_STACK /* animate */); } } - /** - * Whether the dot should be hidden based on current dot state. - */ - private boolean isDotHidden() { - return (mCurrentDotState == DOT_STATE_DEFAULT && !mBubble.showDot()) - || mCurrentDotState == DOT_STATE_SUPPRESSED_FOR_FLYOUT; + /** Removes a dot suppression flag, updating dot visibility if needed. */ + void removeDotSuppressionFlag(SuppressionFlag flag) { + if (mDotSuppressionFlags.remove(flag)) { + // Update dot visibility, animating if we're no longer behind the stack. + updateDotVisibility(flag == SuppressionFlag.BEHIND_STACK); + } + } + + /** Updates the visibility of the dot, animating if requested. */ + void updateDotVisibility(boolean animate) { + final float targetScale = shouldDrawDot() ? 1f : 0f; + + if (animate) { + animateDotScale(targetScale, null /* after */); + } else { + mDotScale = targetScale; + mAnimatingToDotScale = targetScale; + invalidate(); + } } /** @@ -194,11 +217,11 @@ public class BadgedImageView extends ImageView { } /** Sets the position of the 'new' dot, animating it out and back in if requested. */ - void setDotPosition(boolean onLeft, boolean animate) { - if (animate && onLeft != getDotOnLeft() && !isDotHidden()) { - animateDot(false /* showDot */, () -> { + void setDotPositionOnLeft(boolean onLeft, boolean animate) { + if (animate && onLeft != getDotOnLeft() && shouldDrawDot()) { + animateDotScale(0f /* showDot */, () -> { setDotOnLeft(onLeft); - animateDot(true /* showDot */, null); + animateDotScale(1.0f, null /* after */); }); } else { setDotOnLeft(onLeft); @@ -209,28 +232,34 @@ public class BadgedImageView extends ImageView { return getDotOnLeft(); } - /** Changes the dot's visibility to match the bubble view's state. */ - void animateDot() { - if (mCurrentDotState == DOT_STATE_DEFAULT) { - animateDot(mBubble.showDot(), null); - } + /** Whether to draw the dot in onDraw(). */ + private boolean shouldDrawDot() { + // Always render the dot if it's animating, since it could be animating out. Otherwise, show + // it if the bubble wants to show it, and we aren't suppressing it. + return mDotIsAnimating || (mBubble.showDot() && mDotSuppressionFlags.isEmpty()); } /** - * Animates the dot to show or hide. + * Animates the dot to the given scale, running the optional callback when the animation ends. */ - private void animateDot(boolean showDot, Runnable after) { - if (mDotDrawn == showDot) { - // State is consistent, do nothing. + private void animateDotScale(float toScale, @Nullable Runnable after) { + mDotIsAnimating = true; + + // Don't restart the animation if we're already animating to the given value. + if (mAnimatingToDotScale == toScale || !shouldDrawDot()) { + mDotIsAnimating = false; return; } - setDotState(DOT_STATE_ANIMATING); + mAnimatingToDotScale = toScale; + + final boolean showDot = toScale > 0f; // Do NOT wait until after animation ends to setShowDot // to avoid overriding more recent showDot states. clearAnimation(); - animate().setDuration(200) + animate() + .setDuration(200) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .setUpdateListener((valueAnimator) -> { float fraction = valueAnimator.getAnimatedFraction(); @@ -238,7 +267,7 @@ public class BadgedImageView extends ImageView { setDotScale(fraction); }).withEndAction(() -> { setDotScale(showDot ? 1f : 0f); - setDotState(DOT_STATE_DEFAULT); + mDotIsAnimating = false; if (after != null) { after.run(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 726a7dd111d7..afa3164cbd38 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -247,7 +247,7 @@ class Bubble implements BubbleViewProvider { mExpandedView.update(/* bubble */ this); } if (mIconView != null) { - mIconView.update(/* bubble */ this); + mIconView.setRenderedBubble(/* bubble */ this); } } @@ -306,7 +306,7 @@ class Bubble implements BubbleViewProvider { void markAsAccessedAt(long lastAccessedMillis) { mLastAccessed = lastAccessedMillis; setSuppressNotification(true); - setShowDot(false /* show */, true /* animate */); + setShowDot(false /* show */); } /** @@ -346,12 +346,11 @@ class Bubble implements BubbleViewProvider { /** * Sets whether the bubble for this notification should show a dot indicating updated content. */ - void setShowDot(boolean showDot, boolean animate) { + void setShowDot(boolean showDot) { mShowBubbleUpdateDot = showDot; - if (animate && mIconView != null) { - mIconView.animateDot(); - } else if (mIconView != null) { - mIconView.invalidate(); + + if (mIconView != null) { + mIconView.updateDotVisibility(true /* animate */); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 01c2faa62403..9d885fd3c207 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -331,14 +331,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi @Override public void onZenChanged(int zen) { for (Bubble b : mBubbleData.getBubbles()) { - b.setShowDot(b.showInShade(), true /* animate */); + b.setShowDot(b.showInShade()); } } @Override public void onConfigChanged(ZenModeConfig config) { for (Bubble b : mBubbleData.getBubbles()) { - b.setShowDot(b.showInShade(), true /* animate */); + b.setShowDot(b.showInShade()); } } }); @@ -1101,7 +1101,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } else if (interceptBubbleDismissal) { Bubble bubble = mBubbleData.getBubbleWithKey(entry.getKey()); bubble.setSuppressNotification(true); - bubble.setShowDot(false /* show */, true /* animate */); + bubble.setShowDot(false /* show */); } else { return false; } @@ -1141,7 +1141,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi Bubble bubbleChild = mBubbleData.getBubbleWithKey(child.getKey()); mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry()); bubbleChild.setSuppressNotification(true); - bubbleChild.setShowDot(false /* show */, true /* animate */); + bubbleChild.setShowDot(false /* show */); } else { // non-bubbled children can be removed for (NotifCallback cb : mCallbacks) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 2bd15188b7d3..1c69594469c1 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -288,7 +288,7 @@ public class BubbleData { boolean isBubbleExpandedAndSelected = mExpanded && mSelectedBubble == bubble; boolean suppress = isBubbleExpandedAndSelected || !showInShade || !bubble.showInShade(); bubble.setSuppressNotification(suppress); - bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */); + bubble.setShowDot(!isBubbleExpandedAndSelected /* show */); dispatchPendingChanges(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java index 4fb2d0881ede..13669a68defa 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java @@ -112,7 +112,7 @@ public class BubbleOverflow implements BubbleViewProvider { mPath.transform(matrix); mOverflowBtn.setVisibility(GONE); - mOverflowBtn.update(this); + mOverflowBtn.setRenderedBubble(this); } ImageView getBtn() { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index b65198595095..2231d11b7bc2 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -183,7 +183,7 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V public void onBindViewHolder(ViewHolder vh, int index) { Bubble b = mBubbles.get(index); - vh.iconView.update(b); + vh.iconView.setRenderedBubble(b); vh.iconView.setOnClickListener(view -> { mBubbles.remove(b); notifyDataSetChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 7191a203ea8c..4b036812dbed 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -22,8 +22,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION; import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION; -import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_DEFAULT; -import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_SUPPRESSED_FOR_FLYOUT; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; @@ -226,7 +224,7 @@ public class BubbleStackView extends FrameLayout { private boolean mIsExpanded; /** Whether the stack is currently on the left side of the screen, or animating there. */ - private boolean mStackOnLeftOrWillBe = false; + private boolean mStackOnLeftOrWillBe = true; /** Whether a touch gesture, such as a stack/bubble drag or flyout drag, is in progress. */ private boolean mIsGestureInProgress = false; @@ -936,9 +934,13 @@ public class BubbleStackView extends FrameLayout { mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide(); } + if (bubble.getIconView() == null) { + return; + } + // Set the dot position to the opposite of the side the stack is resting on, since the stack // resting slightly off-screen would result in the dot also being off-screen. - bubble.getIconView().setDotPosition( + bubble.getIconView().setDotPositionOnLeft( !mStackOnLeftOrWillBe /* onLeft */, false /* animate */); mBubbleContainer.addView(bubble.getIconView(), 0, @@ -1698,7 +1700,7 @@ public class BubbleStackView extends FrameLayout { || mBubbleToExpandAfterFlyoutCollapse != null || bubbleView == null) { if (bubbleView != null) { - bubbleView.setDotState(DOT_STATE_DEFAULT); + bubbleView.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); } // Skip the message if none exists, we're expanded or animating expansion, or we're // about to expand a bubble from the previous tapped flyout, or if bubble view is null. @@ -1717,12 +1719,16 @@ public class BubbleStackView extends FrameLayout { mBubbleData.setExpanded(true); mBubbleToExpandAfterFlyoutCollapse = null; } - bubbleView.setDotState(DOT_STATE_DEFAULT); + + // Stop suppressing the dot now that the flyout has morphed into the dot. + bubbleView.removeDotSuppressionFlag( + BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); }; mFlyout.setVisibility(INVISIBLE); - // Don't show the dot when we're animating the flyout - bubbleView.setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT); + // Suppress the dot when we are animating the flyout. + bubbleView.addDotSuppressionFlag( + BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); // Start flyout expansion. Post in case layout isn't complete and getWidth returns 0. post(() -> { @@ -1743,6 +1749,11 @@ public class BubbleStackView extends FrameLayout { }; mFlyout.postDelayed(mAnimateInFlyout, 200); }; + + if (bubble.getIconView() == null) { + return; + } + mFlyout.setupFlyoutStartingAsDot(flyoutMessage, mStackAnimationController.getStackPosition(), getWidth(), mStackAnimationController.isStackOnLeftSide(), @@ -1877,9 +1888,19 @@ public class BubbleStackView extends FrameLayout { for (int i = 0; i < bubbleCount; i++) { BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i); bv.setZ((mMaxBubbles * mBubbleElevation) - i); + // If the dot is on the left, and so is the stack, we need to change the dot position. if (bv.getDotPositionOnLeft() == mStackOnLeftOrWillBe) { - bv.setDotPosition(!mStackOnLeftOrWillBe, animate); + bv.setDotPositionOnLeft(!mStackOnLeftOrWillBe, animate); + } + + if (!mIsExpanded && i > 0) { + // If we're collapsed and this bubble is behind other bubbles, suppress its dot. + bv.addDotSuppressionFlag( + BadgedImageView.SuppressionFlag.BEHIND_STACK); + } else { + bv.removeDotSuppressionFlag( + BadgedImageView.SuppressionFlag.BEHIND_STACK); } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index 7ee162e03dbc..00de8b4a51b8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -287,7 +287,7 @@ public class StackAnimationController extends /** Whether the stack is on the left side of the screen. */ public boolean isStackOnLeftSide() { if (mLayout == null || !isStackPositionSet()) { - return false; + return true; // Default to left, which is where it starts by default. } float stackCenter = mStackPosition.x + mBubbleBitmapSize / 2; diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index 19200e48fc5c..fdb0e4c95bed 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -65,7 +65,7 @@ class ControlsControllerImpl @Inject constructor ( companion object { private const val TAG = "ControlsControllerImpl" - internal const val CONTROLS_AVAILABLE = "systemui.controls_available" + internal const val CONTROLS_AVAILABLE = Settings.Secure.CONTROLS_ENABLED internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE) private const val USER_CHANGE_RETRY_DELAY = 500L // ms private const val DEFAULT_ENABLED = 1 diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index 563c2f677801..764fda05354c 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -68,6 +68,8 @@ class ControlAdapter( width = ViewGroup.LayoutParams.MATCH_PARENT } elevation = this@ControlAdapter.elevation + background = parent.context.getDrawable( + R.drawable.control_background_ripple) } ) { id, favorite -> model?.changeFavoriteStatus(id, favorite) @@ -137,10 +139,7 @@ private class ControlHolder(view: View, val favoriteCallback: ModelFavoriteChang private val title: TextView = itemView.requireViewById(R.id.title) private val subtitle: TextView = itemView.requireViewById(R.id.subtitle) private val removed: TextView = itemView.requireViewById(R.id.status) - private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite) - private val favoriteFrame: ViewGroup = itemView - .requireViewById<ViewGroup>(R.id.favorite_container) - .apply { + private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply { visibility = View.VISIBLE } @@ -155,7 +154,7 @@ private class ControlHolder(view: View, val favoriteCallback: ModelFavoriteChang favorite.setOnClickListener { favoriteCallback(data.control.controlId, favorite.isChecked) } - favoriteFrame.setOnClickListener { favorite.performClick() } + itemView.setOnClickListener { favorite.performClick() } applyRenderInfo(renderInfo) } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 12955a153360..ce29859f12ca 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -49,8 +49,6 @@ import dagger.Lazy; public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks { - private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f; - private final Context mContext; private final Lazy<GlobalActionsDialog> mGlobalActionsDialogLazy; private final KeyguardStateController mKeyguardStateController; @@ -124,7 +122,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); window.setBackgroundDrawable(background); - window.setWindowAnimations(R.style.Animation_Toast); + window.setWindowAnimations(com.android.systemui.R.style.Animation_ShutdownUi); d.setContentView(R.layout.shutdown_dialog); d.setCancelable(false); @@ -153,7 +151,9 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(), mBlurUtils.blurRadiusOfRatio(1)); } else { - background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255)); + float backgroundAlpha = mContext.getResources().getFloat( + com.android.systemui.R.dimen.shutdown_scrim_behind_alpha); + background.setAlpha((int) (backgroundAlpha * 255)); } d.show(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 0403a0505b00..cd737217b84a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -142,6 +142,9 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override public void computeScroll() { if (!mScroller.isFinished() && mScroller.computeScrollOffset()) { + if (!isFakeDragging()) { + beginFakeDrag(); + } fakeDragBy(getScrollX() - mScroller.getCurrX()); // Keep on drawing until the animation has finished. postInvalidateOnAnimation(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 3074e33c46a8..ceefff1d14e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -203,6 +203,7 @@ public class KeyguardBouncer { Log.wtf(TAG, "onFullyShown when view was null"); } else { mKeyguardView.onResume(); + mRoot.announceForAccessibility(mKeyguardView.getAccessibilityTitleForCurrentMode()); } } @@ -438,7 +439,6 @@ public class KeyguardBouncer { mStatusBarHeight = mRoot.getResources().getDimensionPixelOffset( com.android.systemui.R.dimen.status_bar_height); mRoot.setVisibility(View.INVISIBLE); - mRoot.setAccessibilityPaneTitle(mKeyguardView.getAccessibilityTitleForCurrentMode()); final WindowInsets rootInsets = mRoot.getRootWindowInsets(); if (rootInsets != null) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index 35971bd4037c..e052ae2653f0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; @@ -56,11 +57,13 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -91,13 +94,14 @@ public class KeyguardBouncerTest extends SysuiTestCase { private Handler mHandler; @Mock private KeyguardSecurityModel mKeyguardSecurityModel; - + @Rule + public MockitoRule mRule = MockitoJUnit.rule(); + private ViewGroup mRootView; private KeyguardBouncer mBouncer; @Before public void setup() { allowTestableLooperAsMainThread(); - MockitoAnnotations.initMocks(this); mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor); mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel); mDependency.injectMockDependency(KeyguardStateController.class); @@ -115,6 +119,8 @@ public class KeyguardBouncerTest extends SysuiTestCase { protected void inflateView() { super.inflateView(); mKeyguardView = mKeyguardHostView; + mRoot = spy(mRoot); + mRootView = mRoot; } }; } @@ -217,6 +223,7 @@ public class KeyguardBouncerTest extends SysuiTestCase { mBouncer.setExpansion(0); verify(mKeyguardHostView).onResume(); + verify(mRootView).announceForAccessibility(any()); } @Test diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index c84892d675f9..4b2c9215f7b7 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -307,28 +307,22 @@ public class Tethering { userManager, this, mNotificationUpdater); mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); + mNetdCallback = new NetdCallback(); // Load tethering configuration. updateConfiguration(); - // NetdCallback should be registered after updateConfiguration() to ensure - // TetheringConfiguration is created. - mNetdCallback = new NetdCallback(); + } + + /** + * Start to register callbacks. + * Call this function when tethering is ready to handle callback events. + */ + public void startStateMachineUpdaters() { try { mNetd.registerUnsolicitedEventListener(mNetdCallback); } catch (RemoteException e) { mLog.e("Unable to register netd UnsolicitedEventListener"); } - - startStateMachineUpdaters(mHandler); - startTrackDefaultNetwork(); - - final WifiManager wifiManager = getWifiManager(); - if (wifiManager != null) { - wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback()); - } - } - - private void startStateMachineUpdaters(Handler handler) { mCarrierConfigChange.startListening(); mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); @@ -341,7 +335,14 @@ public class Tethering { filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED); filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED); - mContext.registerReceiver(mStateReceiver, filter, null, handler); + mContext.registerReceiver(mStateReceiver, filter, null, mHandler); + + final WifiManager wifiManager = getWifiManager(); + if (wifiManager != null) { + wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback()); + } + + startTrackDefaultNetwork(); } private class TetheringThreadExecutor implements Executor { diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index c5329d8d3316..c30be25dbd22 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -80,6 +80,7 @@ public class TetheringService extends Service { mContext = mDeps.getContext(); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mTethering = makeTethering(mDeps); + mTethering.startStateMachineUpdaters(); } /** diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 5ead1106d75a..a59c6fd9e193 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -484,6 +484,7 @@ public class TetheringTest { mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mTethering = makeTethering(); + mTethering.startStateMachineUpdaters(); verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any()); verify(mNetd).registerUnsolicitedEventListener(any()); final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor = diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 41a104c5d4dc..d9e7c3851906 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -1062,7 +1062,12 @@ public class PackageWatchdog { public void updatePackagesLocked(List<MonitoredPackage> packages) { for (int pIndex = 0; pIndex < packages.size(); pIndex++) { MonitoredPackage p = packages.get(pIndex); - this.packages.put(p.getName(), p); + MonitoredPackage existingPackage = this.packages.get(p.getName()); + if (existingPackage != null) { + existingPackage.updateHealthCheckDuration(p.mDurationMs); + } else { + this.packages.put(p.getName(), p); + } } } @@ -1331,6 +1336,12 @@ public class PackageWatchdog { return updateHealthCheckStateLocked(); } + /** Explicitly update the monitoring duration of the package. */ + @GuardedBy("mLock") + public void updateHealthCheckDuration(long newDurationMs) { + mDurationMs = newDurationMs; + } + /** * Marks the health check as passed and transitions to {@link HealthCheckState.PASSED} * if not yet {@link HealthCheckState.FAILED}. diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index dd9cc641f2dd..ac4a42ca7024 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -605,15 +605,16 @@ public class VibratorService extends IVibratorService.Stub } @Override // Binder call - public boolean[] areEffectsSupported(int[] effectIds) { - // Return null to indicate that the HAL doesn't actually tell us what effects are - // supported. + public int[] areEffectsSupported(int[] effectIds) { + int[] supported = new int[effectIds.length]; if (mSupportedEffects == null) { - return null; - } - boolean[] supported = new boolean[effectIds.length]; - for (int i = 0; i < effectIds.length; i++) { - supported[i] = mSupportedEffects.contains(effectIds[i]); + Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN); + } else { + for (int i = 0; i < effectIds.length; i++) { + supported[i] = mSupportedEffects.contains(effectIds[i]) + ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES + : Vibrator.VIBRATION_EFFECT_SUPPORT_NO; + } } return supported; } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index e6129b9b1f32..0b22586bb373 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -76,6 +76,7 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputMonitor; +import android.view.InputWindowHandle; import android.view.KeyEvent; import android.view.PointerIcon; import android.view.Surface; @@ -220,7 +221,8 @@ public class InputManagerService extends IInputManager.Stub int policyFlags); private static native VerifiedInputEvent nativeVerifyInputEvent(long ptr, InputEvent event); private static native void nativeToggleCapsLock(long ptr, int deviceId); - private static native void nativeDisplayRemoved(long ptr, int displayId); + private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles, + int displayId); private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen); private static native void nativeSetSystemUiVisibility(long ptr, int visibility); private static native void nativeSetFocusedApplication(long ptr, @@ -1534,7 +1536,7 @@ public class InputManagerService extends IInputManager.Stub /** Clean up input window handles of the given display. */ public void onDisplayRemoved(int displayId) { - nativeDisplayRemoved(mPtr, displayId); + nativeSetInputWindows(mPtr, null /* windowHandles */, displayId); } @Override diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 9bbbc3b93fd7..6aae62ea3f18 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -708,7 +708,7 @@ class MediaRouter2ServiceImpl { } List<RoutingSessionInfo> sessionInfos = new ArrayList<>(); - for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mMediaProviders) { + for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mRouteProviders) { sessionInfos.addAll(provider.getSessionInfos()); } return sessionInfos; @@ -1059,7 +1059,7 @@ class MediaRouter2ServiceImpl { //TODO: Make this thread-safe. private final SystemMediaRoute2Provider mSystemProvider; - private final ArrayList<MediaRoute2Provider> mMediaProviders = + private final ArrayList<MediaRoute2Provider> mRouteProviders = new ArrayList<>(); private final List<MediaRoute2ProviderInfo> mLastProviderInfos = new ArrayList<>(); @@ -1074,7 +1074,7 @@ class MediaRouter2ServiceImpl { mServiceRef = new WeakReference<>(service); mUserRecord = userRecord; mSystemProvider = new SystemMediaRoute2Provider(service.mContext, this); - mMediaProviders.add(mSystemProvider); + mRouteProviders.add(mSystemProvider); mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this, this, mUserRecord.mUserId); } @@ -1097,13 +1097,13 @@ class MediaRouter2ServiceImpl { @Override public void onAddProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) { proxy.setCallback(this); - mMediaProviders.add(proxy); + mRouteProviders.add(proxy); proxy.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); } @Override public void onRemoveProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) { - mMediaProviders.remove(proxy); + mRouteProviders.remove(proxy); } @Override @@ -1148,10 +1148,10 @@ class MediaRouter2ServiceImpl { //TODO: notify session info updates private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) { - int providerIndex = getProviderInfoIndex(provider.getUniqueId()); + int providerInfoIndex = getLastProviderInfoIndex(provider.getUniqueId()); MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo(); MediaRoute2ProviderInfo prevInfo = - (providerIndex < 0) ? null : mLastProviderInfos.get(providerIndex); + (providerInfoIndex < 0) ? null : mLastProviderInfos.get(providerInfoIndex); if (Objects.equals(prevInfo, providerInfo)) return; @@ -1171,7 +1171,7 @@ class MediaRouter2ServiceImpl { this, getRouters(), new ArrayList<>(removedRoutes))); } } else { - mLastProviderInfos.set(providerIndex, providerInfo); + mLastProviderInfos.set(providerInfoIndex, providerInfo); List<MediaRoute2Info> addedRoutes = new ArrayList<>(); List<MediaRoute2Info> removedRoutes = new ArrayList<>(); List<MediaRoute2Info> changedRoutes = new ArrayList<>(); @@ -1219,7 +1219,7 @@ class MediaRouter2ServiceImpl { } } - private int getProviderInfoIndex(@NonNull String providerId) { + private int getLastProviderInfoIndex(@NonNull String providerId) { for (int i = 0; i < mLastProviderInfos.size(); i++) { MediaRoute2ProviderInfo providerInfo = mLastProviderInfos.get(i); if (TextUtils.equals(providerInfo.getUniqueId(), providerId)) { @@ -1795,13 +1795,13 @@ class MediaRouter2ServiceImpl { new RouteDiscoveryPreference.Builder(discoveryPreferences) .build(); } - for (MediaRoute2Provider provider : mMediaProviders) { + for (MediaRoute2Provider provider : mRouteProviders) { provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference); } } private MediaRoute2Provider findProvider(@Nullable String providerId) { - for (MediaRoute2Provider provider : mMediaProviders) { + for (MediaRoute2Provider provider : mRouteProviders) { if (TextUtils.equals(provider.getUniqueId(), providerId)) { return provider; } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index c7d14e0afe7b..c69787d602e2 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -54,6 +54,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE"; + static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION"; private final AudioManager mAudioManager; @@ -67,14 +68,17 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SystemMediaRoute2Provider.class.getName()); private String mSelectedRouteId; + // For apps without MODIFYING_AUDIO_ROUTING permission. + // This should be the currently selected route. MediaRoute2Info mDefaultRoute; + MediaRoute2Info mDeviceRoute; final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { @Override public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) { mHandler.post(() -> { - updateDefaultRoute(newRoutes); + updateDeviceRoute(newRoutes); notifyProviderState(); }); } @@ -97,7 +101,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); } catch (RemoteException e) { } - updateDefaultRoute(newAudioRoutes); + updateDeviceRoute(newAudioRoutes); mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> { publishProviderState(); @@ -109,7 +113,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } }); updateSessionInfosIfNeeded(); - mContext.registerReceiver(new VolumeChangeReceiver(), new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION)); @@ -150,7 +153,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { @Override public void transferToRoute(long requestId, String sessionId, String routeId) { - if (TextUtils.equals(routeId, mDefaultRoute.getId())) { + if (TextUtils.equals(routeId, mDeviceRoute.getId())) { mBtRouteProvider.transferTo(null); } else { mBtRouteProvider.transferTo(routeId); @@ -170,7 +173,11 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // Do nothing since we don't support grouping volume yet. } - private void updateDefaultRoute(AudioRoutesInfo newRoutes) { + public MediaRoute2Info getDefaultRoute() { + return mDefaultRoute; + } + + private void updateDeviceRoute(AudioRoutesInfo newRoutes) { int name = R.string.default_audio_route_name; if (newRoutes != null) { mCurAudioRoutesInfo.mainType = newRoutes.mainType; @@ -185,8 +192,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { name = com.android.internal.R.string.default_audio_route_name_usb; } } - mDefaultRoute = new MediaRoute2Info.Builder( - DEFAULT_ROUTE_ID, mContext.getResources().getText(name).toString()) + mDeviceRoute = new MediaRoute2Info.Builder( + DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) .setVolumeHandling(mAudioManager.isVolumeFixed() ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) @@ -203,7 +210,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); - builder.addRoute(mDefaultRoute); + builder.addRoute(mDeviceRoute); if (mBtRouteProvider != null) { for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) { builder.addRoute(route); @@ -228,11 +235,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute(); if (selectedRoute == null) { - selectedRoute = mDefaultRoute; + selectedRoute = mDeviceRoute; } else { - builder.addTransferableRoute(mDefaultRoute.getId()); + builder.addTransferableRoute(mDeviceRoute.getId()); } mSelectedRouteId = selectedRoute.getId(); + mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute).build(); builder.addSelectedRoute(mSelectedRouteId); for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) { @@ -282,8 +290,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0); if (newVolume != oldVolume) { - if (TextUtils.equals(mDefaultRoute.getId(), mSelectedRouteId)) { - mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute) + if (TextUtils.equals(mDeviceRoute.getId(), mSelectedRouteId)) { + mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) .setVolume(newVolume) .build(); } else { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 31dc09416ac8..87061e1d488c 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -107,6 +107,7 @@ import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -2842,20 +2843,18 @@ public class NotificationManagerService extends SystemService { record = mToastQueue.get(index); record.update(duration); } else { - // Limit the number of toasts that any given package except the android - // package can enqueue. Prevents DOS attacks and deals with leaks. - if (!isSystemToast) { - int count = 0; - final int N = mToastQueue.size(); - for (int i = 0; i < N; i++) { - final ToastRecord r = mToastQueue.get(i); - if (r.pkg.equals(pkg)) { - count++; - if (count >= MAX_PACKAGE_NOTIFICATIONS) { - Slog.e(TAG, "Package has already posted " + count - + " toasts. Not showing more. Package=" + pkg); - return; - } + // Limit the number of toasts that any given package can enqueue. + // Prevents DOS attacks and deals with leaks. + int count = 0; + final int N = mToastQueue.size(); + for (int i = 0; i < N; i++) { + final ToastRecord r = mToastQueue.get(i); + if (r.pkg.equals(pkg)) { + count++; + if (count >= MAX_PACKAGE_NOTIFICATIONS) { + Slog.e(TAG, "Package has already posted " + count + + " toasts. Not showing more. Package=" + pkg); + return; } } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2f814f598a05..29f7d523e9a0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -5201,7 +5201,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } finishLaunchTickingLocked(); if (task != null) { - task.hasBeenVisible = true; + task.setHasBeenVisible(true); } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 18332b9484c0..88cdd1781aee 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -62,7 +62,7 @@ final class InputMonitor { // When true, need to call updateInputWindowsLw(). private boolean mUpdateInputWindowsNeeded = true; private boolean mUpdateInputWindowsPending; - private boolean mUpdateInputWindowsImmediately; + private boolean mApplyImmediately; // Currently focused input window handle. private InputWindowHandle mFocusedInputWindowHandle; @@ -347,20 +347,14 @@ final class InputMonitor { } } - /** - * Immediately update the input transaction and merge into the passing Transaction that could be - * collected and applied later. - */ - void updateInputWindowsImmediately(SurfaceControl.Transaction t) { + void updateInputWindowsImmediately() { mHandler.removeCallbacks(mUpdateInputWindows); - mUpdateInputWindowsImmediately = true; + mApplyImmediately = true; mUpdateInputWindows.run(); - mUpdateInputWindowsImmediately = false; - t.merge(mInputTransaction); + mApplyImmediately = false; } - /** - * Called when the current input focus changes. + /* Called when the current input focus changes. * Layer assignment is assumed to be complete by the time this is called. */ public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { @@ -471,7 +465,10 @@ final class InputMonitor { if (mAddWallpaperInputConsumerHandle) { mWallpaperInputConsumer.show(mInputTransaction, 0); } - if (!mUpdateInputWindowsImmediately) { + + if (mApplyImmediately) { + mInputTransaction.apply(); + } else { mDisplayContent.getPendingTransaction().merge(mInputTransaction); mDisplayContent.scheduleAnimation(); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 7a41ea57610d..91b4ec95f8fd 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -258,7 +258,7 @@ class Task extends WindowContainer<WindowContainer> { boolean autoRemoveRecents; // If true, we should automatically remove the task from // recents when activity finishes boolean askedCompatMode;// Have asked the user about compat mode for this task. - boolean hasBeenVisible; // Set if any activities in the task have been visible to the user. + private boolean mHasBeenVisible; // Set if any activities in the task have been visible String stringName; // caching of toString() result. boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity @@ -483,6 +483,10 @@ class Task extends WindowContainer<WindowContainer> { */ ITaskOrganizer mTaskOrganizer; private int mLastTaskOrganizerWindowingMode = -1; + /** + * Prevent duplicate calls to onTaskAppeared. + */ + boolean mTaskAppearedSent; /** * Last Picture-in-Picture params applicable to the task. Updated when the app @@ -1517,7 +1521,7 @@ class Task extends WindowContainer<WindowContainer> { // We will automatically remove the task either if it has explicitly asked for // this, or it is empty and has never contained an activity that got shown to // the user. - return autoRemoveRecents || (!hasChild() && !hasBeenVisible); + return autoRemoveRecents || (!hasChild() && !getHasBeenVisible()); } /** @@ -2030,7 +2034,7 @@ class Task extends WindowContainer<WindowContainer> { } private void saveLaunchingStateIfNeeded(DisplayContent display) { - if (!hasBeenVisible) { + if (!getHasBeenVisible()) { // Not ever visible to user. return; } @@ -3558,7 +3562,7 @@ class Task extends WindowContainer<WindowContainer> { pw.print(prefix); pw.print("mRootProcess="); pw.println(mRootProcess); } pw.print(prefix); pw.print("taskId=" + mTaskId); pw.println(" stackId=" + getRootTaskId()); - pw.print(prefix + "hasBeenVisible=" + hasBeenVisible); + pw.print(prefix + "mHasBeenVisible=" + getHasBeenVisible()); pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode)); pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture); pw.print(" isResizeable=" + isResizeable()); @@ -4087,14 +4091,42 @@ class Task extends WindowContainer<WindowContainer> { super.reparentSurfaceControl(t, newParent); } + void setHasBeenVisible(boolean hasBeenVisible) { + mHasBeenVisible = hasBeenVisible; + if (hasBeenVisible) { + sendTaskAppeared(); + if (!isRootTask()) { + getRootTask().setHasBeenVisible(true); + } + } + } + + boolean getHasBeenVisible() { + return mHasBeenVisible; + } + + /** In the case that these three conditions are true, we want to send the Task to + * the organizer: + * 1. We have a SurfaceControl + * 2. An organizer has been set + * 3. We have finished drawing + * Any time any of these conditions are updated, the updating code should call + * sendTaskAppeared. + */ + private boolean taskAppearedReady() { + return mSurfaceControl != null && mTaskOrganizer != null && getHasBeenVisible(); + } + private void sendTaskAppeared() { - if (mSurfaceControl != null && mTaskOrganizer != null) { + if (taskAppearedReady() && !mTaskAppearedSent) { + mTaskAppearedSent = true; mAtmService.mTaskOrganizerController.onTaskAppeared(mTaskOrganizer, this); } } private void sendTaskVanished() { - if (mTaskOrganizer != null) { + if (mTaskOrganizer != null && mTaskAppearedSent) { + mTaskAppearedSent = false; mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this); } } @@ -4113,6 +4145,7 @@ class Task extends WindowContainer<WindowContainer> { void taskOrganizerUnregistered() { mTaskOrganizer = null; + mTaskAppearedSent = false; mLastTaskOrganizerWindowingMode = -1; onTaskOrganizerChanged(); if (mCreatedByOrganizer) { diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index be0d6f8a0b9f..f046e8adc478 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -268,9 +268,8 @@ class TaskPositioner implements IBinder.DeathRecipient { mDisplayContent.getDisplayRotation().pause(); // Notify InputMonitor to take mDragWindowHandle. - final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); - mDisplayContent.getInputMonitor().updateInputWindowsImmediately(t); - t.syncInputWindows().apply(); + mDisplayContent.getInputMonitor().updateInputWindowsImmediately(); + new SurfaceControl.Transaction().syncInputWindows().apply(true); final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics(); mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 075772566d56..23ba528b6aee 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7746,23 +7746,19 @@ public class WindowManagerService extends IWindowManager.Stub public void syncInputTransactions() { waitForAnimationsToComplete(); - // Collect all input transactions from all displays to make sure we could sync all input - // windows at same time. - final SurfaceControl.Transaction t = mTransactionFactory.get(); synchronized (mGlobalLock) { mWindowPlacerLocked.performSurfacePlacementIfScheduled(); mRoot.forAllDisplays(displayContent -> - displayContent.getInputMonitor().updateInputWindowsImmediately(t)); + displayContent.getInputMonitor().updateInputWindowsImmediately()); } - t.syncInputWindows().apply(); + mTransactionFactory.get().syncInputWindows().apply(true); } private void waitForAnimationsToComplete() { synchronized (mGlobalLock) { long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS; - while ((mAnimator.isAnimationScheduled() - || mRoot.isAnimating(TRANSITION | CHILDREN)) && timeoutRemaining > 0) { + while (mRoot.isAnimating(TRANSITION | CHILDREN) && timeoutRemaining > 0) { long startTime = System.currentTimeMillis(); try { mGlobalLock.wait(timeoutRemaining); diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 9bc5d34c11af..e3f9ae8969b3 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -206,7 +206,7 @@ public: status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel); status_t pilferPointers(const sp<IBinder>& token); - void displayRemoved(JNIEnv* env, int32_t displayId); + void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId); void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj); void setFocusedDisplay(JNIEnv* env, int32_t displayId); void setInputDispatchMode(bool enabled, bool frozen); @@ -771,10 +771,55 @@ void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration } } -void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) { - // Set an empty list to remove all handles from the specific display. - std::vector<sp<InputWindowHandle>> windowHandles; - mInputManager->getDispatcher()->setInputWindows({{displayId, windowHandles}}); +void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, + int32_t displayId) { + std::vector<sp<InputWindowHandle> > windowHandles; + + if (windowHandleObjArray) { + jsize length = env->GetArrayLength(windowHandleObjArray); + for (jsize i = 0; i < length; i++) { + jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i); + if (! windowHandleObj) { + break; // found null element indicating end of used portion of the array + } + + sp<InputWindowHandle> windowHandle = + android_view_InputWindowHandle_getHandle(env, windowHandleObj); + if (windowHandle != nullptr) { + windowHandles.push_back(windowHandle); + } + env->DeleteLocalRef(windowHandleObj); + } + } + + mInputManager->getDispatcher()->setInputWindows(windowHandles, displayId); + + // Do this after the dispatcher has updated the window handle state. + bool newPointerGesturesEnabled = true; + size_t numWindows = windowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + const sp<InputWindowHandle>& windowHandle = windowHandles[i]; + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures + & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) { + newPointerGesturesEnabled = false; + } + } + + bool pointerGesturesEnabledChanged = false; + { // acquire lock + AutoMutex _l(mLock); + + if (mLocked.pointerGesturesEnabled != newPointerGesturesEnabled) { + mLocked.pointerGesturesEnabled = newPointerGesturesEnabled; + pointerGesturesEnabledChanged = true; + } + } // release lock + + if (pointerGesturesEnabledChanged) { + mInputManager->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT); + } } void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId, @@ -1522,10 +1567,11 @@ static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */, im->getInputManager()->getReader()->toggleCapsLockState(deviceId); } -static void nativeDisplayRemoved(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId) { +static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */, + jlong ptr, jobjectArray windowHandleObjArray, jint displayId) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - im->displayRemoved(env, displayId); + im->setInputWindows(env, windowHandleObjArray, displayId); } static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */, @@ -1769,7 +1815,8 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", (void*)nativeVerifyInputEvent}, {"nativeToggleCapsLock", "(JI)V", (void*)nativeToggleCapsLock}, - {"nativeDisplayRemoved", "(JI)V", (void*)nativeDisplayRemoved}, + {"nativeSetInputWindows", "(J[Landroid/view/InputWindowHandle;I)V", + (void*)nativeSetInputWindows}, {"nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V", (void*)nativeSetFocusedApplication}, {"nativeSetFocusedDisplay", "(JI)V", (void*)nativeSetFocusedDisplay}, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e304bca4553c..b0eb14852251 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -9184,8 +9184,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } - Log.w(LOG_TAG, String.format("Package %s (uid=%d, pid=%d) cannot access Device IDs", - packageName, uid, pid)); return false; } @@ -15742,9 +15740,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + final int suspendedState = suspended + ? PERSONAL_APPS_SUSPENDED_EXPLICITLY + : PERSONAL_APPS_NOT_SUSPENDED; mInjector.binderWithCleanCallingIdentity( - () -> applyPersonalAppsSuspension( - callingUserId, PERSONAL_APPS_SUSPENDED_EXPLICITLY)); + () -> applyPersonalAppsSuspension(callingUserId, suspendedState)); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_PERSONAL_APPS_SUSPENDED) diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 449e75cd11a0..e58e91179931 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -91,7 +91,15 @@ android_test { enabled: false, }, - data: [":JobTestApp"], + data: [ + ":JobTestApp", + ], + + java_resources: [ + ":PackageParserTestApp1", + ":PackageParserTestApp2", + ":PackageParserTestApp3", + ], resource_zips: [":FrameworksServicesTests_apks_as_resources"], } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index a19d91976307..d760629552b8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -17,11 +17,15 @@ package com.android.server.pm; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + import android.annotation.NonNull; +import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; @@ -30,7 +34,6 @@ import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageParser; import android.content.pm.PackageUserState; -import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.content.pm.parsing.ParsingPackage; @@ -49,6 +52,7 @@ import android.util.ArraySet; import android.util.DisplayMetrics; import androidx.annotation.Nullable; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -69,9 +73,11 @@ import org.junit.runner.RunWith; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -90,6 +96,9 @@ public class PackageParserTest { private File mTmpDir; private static final File FRAMEWORK = new File("/system/framework/framework-res.apk"); + private static final String TEST_APP1_APK = "PackageParserTestApp1.apk"; + private static final String TEST_APP2_APK = "PackageParserTestApp2.apk"; + private static final String TEST_APP3_APK = "PackageParserTestApp3.apk"; @Before public void setUp() throws IOException { @@ -209,6 +218,61 @@ public class PackageParserTest { assertSame(deserialized.getSharedUserId(), deserialized2.getSharedUserId()); } + private static PackageParser2 makeParser() { + return new PackageParser2(null, false, null, null, null); + } + + private File extractFile(String filename) throws Exception { + final Context context = InstrumentationRegistry.getTargetContext(); + final File tmpFile = File.createTempFile(filename, ".apk"); + try (InputStream inputStream = context.getAssets().openNonAsset(filename)) { + Files.copy(inputStream, tmpFile.toPath(), REPLACE_EXISTING); + } + return tmpFile; + } + + /** + * Tests AndroidManifest.xml with no android:isolatedSplits attribute. + */ + @Test + public void testParseIsolatedSplitsDefault() throws Exception { + final File testFile = extractFile(TEST_APP1_APK); + try { + final ParsedPackage pkg = makeParser().parsePackage(testFile, 0, false); + assertFalse("isolatedSplits", pkg.isIsolatedSplitLoading()); + } finally { + testFile.delete(); + } + } + + /** + * Tests AndroidManifest.xml with an android:isolatedSplits attribute set to a constant. + */ + @Test + public void testParseIsolatedSplitsConstant() throws Exception { + final File testFile = extractFile(TEST_APP2_APK); + try { + final ParsedPackage pkg = makeParser().parsePackage(testFile, 0, false); + assertTrue("isolatedSplits", pkg.isIsolatedSplitLoading()); + } finally { + testFile.delete(); + } + } + + /** + * Tests AndroidManifest.xml with an android:isolatedSplits attribute set to a resource. + */ + @Test + public void testParseIsolatedSplitsResource() throws Exception { + final File testFile = extractFile(TEST_APP3_APK); + try { + final ParsedPackage pkg = makeParser().parsePackage(testFile, 0, false); + assertTrue("isolatedSplits", pkg.isIsolatedSplitLoading()); + } finally { + testFile.delete(); + } + } + /** * A trivial subclass of package parser that only caches the package name, and throws away * all other information. diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp new file mode 100644 index 000000000000..c409438e94ae --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp @@ -0,0 +1,53 @@ +// 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. + +android_test_helper_app { + name: "PackageParserTestApp1", + sdk_version: "current", + srcs: ["**/*.java"], + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + manifest: "AndroidManifestApp1.xml", +} + +android_test_helper_app { + name: "PackageParserTestApp2", + sdk_version: "current", + srcs: ["**/*.java"], + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + manifest: "AndroidManifestApp2.xml", +} + +android_test_helper_app { + name: "PackageParserTestApp3", + sdk_version: "current", + srcs: ["**/*.java"], + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + resource_dirs: ["res"], + manifest: "AndroidManifestApp3.xml", +} diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp1.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp1.xml new file mode 100644 index 000000000000..01d335d2cbbd --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp1.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.servicestests.apps.packageparserapp" > + + <application> + <activity android:name=".TestActivity" + android:exported="true" /> + </application> + +</manifest>
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp2.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp2.xml new file mode 100644 index 000000000000..567946cc70d7 --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp2.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.servicestests.apps.packageparserapp" + android:isolatedSplits="true" > + + <application> + <activity android:name=".TestActivity" + android:exported="true" /> + </application> + +</manifest>
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp3.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp3.xml new file mode 100644 index 000000000000..77285a805950 --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp3.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.servicestests.apps.packageparserapp" + android:isolatedSplits="@bool/config_isIsolated" > + + <application> + <activity android:name=".TestActivity" + android:exported="true" /> + </application> + +</manifest>
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml new file mode 100644 index 000000000000..6a4cc653494f --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <bool name="config_isIsolated">true</bool> +</resources>
\ No newline at end of file diff --git a/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/TestActivity.java b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/TestActivity.java new file mode 100644 index 000000000000..2eacb96fb2aa --- /dev/null +++ b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/TestActivity.java @@ -0,0 +1,28 @@ +/* + * 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.servicestests.apps.packageparserapp; + +import android.app.Activity; +import android.os.Bundle; + +public class TestActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + finish(); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index 8f3ff52b8018..ae467c0c811d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -120,7 +120,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { .build(); mTestTask.mUserId = TEST_USER_ID; mTestTask.mLastNonFullscreenBounds = TEST_BOUNDS; - mTestTask.hasBeenVisible = true; + mTestTask.setHasBeenVisible(true); mTaskWithDifferentComponent = new TaskBuilder(mSupervisor) .setComponent(ALTERNATIVE_COMPONENT).build(); @@ -346,7 +346,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { .build(); anotherTaskOfTheSameUser.setWindowingMode(WINDOWING_MODE_FREEFORM); anotherTaskOfTheSameUser.setBounds(200, 300, 400, 500); - anotherTaskOfTheSameUser.hasBeenVisible = true; + anotherTaskOfTheSameUser.setHasBeenVisible(true); mTarget.saveTask(anotherTaskOfTheSameUser); stack = mTestDisplay.createStack(TEST_WINDOWING_MODE, @@ -358,7 +358,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { .build(); anotherTaskOfDifferentUser.setWindowingMode(WINDOWING_MODE_FREEFORM); anotherTaskOfDifferentUser.setBounds(300, 400, 500, 600); - anotherTaskOfDifferentUser.hasBeenVisible = true; + anotherTaskOfDifferentUser.setHasBeenVisible(true); mTarget.saveTask(anotherTaskOfDifferentUser); mTarget.onCleanupUser(TEST_USER_ID); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java index ed635ce3f69e..4cc84a65fb29 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -96,10 +96,28 @@ public class TaskOrganizerTests extends WindowTestsBase { return registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); } + Task createTask(ActivityStack stack, boolean fakeDraw) { + final Task task = createTaskInStack(stack, 0); + + if (fakeDraw) { + task.setHasBeenVisible(true); + } + return task; + } + + Task createTask(ActivityStack stack) { + // Fake draw notifications for most of our tests. + return createTask(stack, true); + } + + ActivityStack createStack() { + return createTaskStackOnDisplay(mDisplayContent); + } + @Test public void testAppearVanish() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); final ITaskOrganizer organizer = registerMockOrganizer(); task.setTaskOrganizer(organizer); @@ -110,9 +128,42 @@ public class TaskOrganizerTests extends WindowTestsBase { } @Test + public void testAppearWaitsForVisibility() throws RemoteException { + final ActivityStack stack = createStack(); + final Task task = createTask(stack, false); + final ITaskOrganizer organizer = registerMockOrganizer(); + + task.setTaskOrganizer(organizer); + + verify(organizer, never()).onTaskAppeared(any()); + task.setHasBeenVisible(true); + assertTrue(stack.getHasBeenVisible()); + + verify(organizer).onTaskAppeared(any()); + + task.removeImmediately(); + verify(organizer).onTaskVanished(any()); + } + + @Test + public void testNoVanishedIfNoAppear() throws RemoteException { + final ActivityStack stack = createStack(); + final Task task = createTask(stack, false /* hasBeenVisible */); + final ITaskOrganizer organizer = registerMockOrganizer(); + + // In this test we skip making the Task visible, and verify + // that even though a TaskOrganizer is set remove doesn't emit + // a vanish callback, because we never emitted appear. + task.setTaskOrganizer(organizer); + verify(organizer, never()).onTaskAppeared(any()); + task.removeImmediately(); + verify(organizer, never()).onTaskVanished(any()); + } + + @Test public void testSwapOrganizer() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED); @@ -125,8 +176,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testSwapWindowingModes() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED); @@ -139,8 +190,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testClearOrganizer() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); final ITaskOrganizer organizer = registerMockOrganizer(); stack.setTaskOrganizer(organizer); @@ -154,8 +205,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testUnregisterOrganizer() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); final ITaskOrganizer organizer = registerMockOrganizer(); stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); @@ -169,12 +220,12 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); - final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent); - final Task task2 = createTaskInStack(stack2, 0 /* userId */); - final ActivityStack stack3 = createTaskStackOnDisplay(mDisplayContent); - final Task task3 = createTaskInStack(stack3, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); + final ActivityStack stack2 = createStack(); + final Task task2 = createTask(stack2); + final ActivityStack stack3 = createStack(); + final Task task3 = createTask(stack3); final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); // First organizer is registered, verify a task appears when changing windowing mode @@ -202,9 +253,9 @@ public class TaskOrganizerTests extends WindowTestsBase { public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED); - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); - final Task task2 = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); + final Task task2 = createTask(stack); stack.setWindowingMode(WINDOWING_MODE_PINNED); verify(organizer, times(1)).onTaskAppeared(any()); @@ -214,8 +265,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException { - final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack = createStack(); + final Task task = createTask(stack); stack.setWindowingMode(WINDOWING_MODE_PINNED); final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED); @@ -560,8 +611,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testTrivialBLASTCallback() throws RemoteException { - final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ActivityStack stackController1 = createStack(); + final Task task = createTask(stackController1); final ITaskOrganizer organizer = registerMockOrganizer(); spyOn(task); @@ -582,8 +633,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testOverlappingBLASTCallback() throws RemoteException { - final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ActivityStack stackController1 = createStack(); + final Task task = createTask(stackController1); final ITaskOrganizer organizer = registerMockOrganizer(); spyOn(task); @@ -611,8 +662,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testBLASTCallbackWithWindow() { - final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ActivityStack stackController1 = createStack(); + final Task task = createTask(stackController1); final ITaskOrganizer organizer = registerMockOrganizer(); final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); makeWindowVisible(w); @@ -635,8 +686,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testBLASTCallbackWithInvisibleWindow() { - final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ActivityStack stackController1 = createStack(); + final Task task = createTask(stackController1); final ITaskOrganizer organizer = registerMockOrganizer(); final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); @@ -657,8 +708,8 @@ public class TaskOrganizerTests extends WindowTestsBase { @Test public void testBLASTCallbackWithChildWindow() { - final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ActivityStack stackController1 = createStack(); + final Task task = createTask(stackController1); final ITaskOrganizer organizer = registerMockOrganizer(); final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); final WindowState child = createWindow(w, TYPE_APPLICATION, "Other Window"); @@ -708,6 +759,8 @@ public class TaskOrganizerTests extends WindowTestsBase { record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; spyOn(record); doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean()); + + record.getRootTask().setHasBeenVisible(true); return record; } @@ -755,4 +808,23 @@ public class TaskOrganizerTests extends WindowTestsBase { assertEquals(3, ratio.getNumerator()); assertEquals(4, ratio.getDenominator()); } + + @Test + public void testPreventDuplicateAppear() throws RemoteException { + final ActivityStack stack = createStack(); + final Task task = createTask(stack); + final ITaskOrganizer organizer = registerMockOrganizer(); + + task.setTaskOrganizer(organizer); + // setHasBeenVisible was already called once by the set-up code. + task.setHasBeenVisible(true); + verify(organizer, times(1)).onTaskAppeared(any()); + + task.taskOrganizerUnregistered(); + task.setTaskOrganizer(organizer); + verify(organizer, times(2)).onTaskAppeared(any()); + + task.removeImmediately(); + verify(organizer).onTaskVanished(any()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index 56c19a47b68a..1ad4079c5193 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -874,11 +874,11 @@ public class TaskRecordTests extends ActivityTestsBase { spyOn(persister); final Task task = getTestTask(); - task.hasBeenVisible = false; + task.setHasBeenVisible(false); task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - task.hasBeenVisible = true; + task.setHasBeenVisible(true); task.onConfigurationChanged(task.getParent().getConfiguration()); verify(persister).saveTask(task, task.getDisplayContent()); @@ -890,7 +890,7 @@ public class TaskRecordTests extends ActivityTestsBase { spyOn(persister); final Task task = getTestTask(); - task.hasBeenVisible = false; + task.setHasBeenVisible(false); task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN); final DisplayContent oldDisplay = task.getDisplayContent(); @@ -900,7 +900,7 @@ public class TaskRecordTests extends ActivityTestsBase { persister.getLaunchParams(task, null, params); assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode); - task.hasBeenVisible = true; + task.setHasBeenVisible(true); task.removeImmediately(); verify(persister).saveTask(task, oldDisplay); @@ -915,10 +915,10 @@ public class TaskRecordTests extends ActivityTestsBase { spyOn(persister); final Task task = getTestTask(); - task.hasBeenVisible = false; + task.setHasBeenVisible(false); task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN); - task.hasBeenVisible = true; + task.setHasBeenVisible(true); task.onConfigurationChanged(task.getParent().getConfiguration()); verify(persister, never()).saveTask(same(task), any()); @@ -930,11 +930,11 @@ public class TaskRecordTests extends ActivityTestsBase { spyOn(persister); final Task task = getTestTask(); - task.hasBeenVisible = false; + task.setHasBeenVisible(false); task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); task.getStack().setWindowingMode(WINDOWING_MODE_PINNED); - task.hasBeenVisible = true; + task.setHasBeenVisible(true); task.onConfigurationChanged(task.getParent().getConfiguration()); verify(persister, never()).saveTask(same(task), any()); diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 7e02966779a2..1a38a42873b7 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -361,7 +361,8 @@ public final class TelephonyPermissions { TelephonyCommonStatsLog.write(TelephonyCommonStatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED, callingPackage, message, /* isPreinstalled= */ false, false); } - Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message); + Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message + ":" + + subId); // if the target SDK is pre-Q then check if the calling package would have previously // had access to device identifiers. if (callingPackageInfo != null && ( diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 2957192ecf0f..d011dbbbe5db 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -1123,6 +1123,28 @@ public class PackageWatchdogTest { assertThat(testController.getSyncRequests()).isEqualTo(expectedSyncRequests); } + /** + * Ensure that the failure history of a package is preserved when making duplicate calls to + * observe the package. + */ + @Test + public void testFailureHistoryIsPreserved() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver observer = new TestObserver(OBSERVER_NAME_1); + watchdog.startObservingHealth(observer, List.of(APP_A), SHORT_DURATION); + for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) { + watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); + } + mTestLooper.dispatchAll(); + assertThat(observer.mMitigatedPackages).isEmpty(); + watchdog.startObservingHealth(observer, List.of(APP_A), LONG_DURATION); + watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); + mTestLooper.dispatchAll(); + assertThat(observer.mMitigatedPackages).isEqualTo(List.of(APP_A)); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() |