Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-dev
diff --git a/Android.bp b/Android.bp
index 9023a19..4d8924d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -273,6 +273,11 @@
installable: false,
}
+filegroup {
+ name: "framework-jarjar-rules",
+ srcs: ["framework-jarjar-rules.txt"],
+}
+
java_defaults {
name: "framework-minus-apex-defaults",
defaults: ["framework-aidl-export-defaults"],
@@ -294,7 +299,7 @@
"--core-library",
"--multi-dex",
],
- jarjar_rules: "framework-jarjar-rules.txt",
+ jarjar_rules: ":framework-jarjar-rules",
javac_shard_size: 150,
plugins: [
"view-inspector-annotation-processor",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index ed24d43..b6c45ed 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -255,7 +255,7 @@
modules_system_stubs = [
"android.net.ipsec.ike.stubs.system",
- "art.module.public.api.stubs", // Only has public stubs
+ "art.module.public.api.stubs.system",
"conscrypt.module.public.api.stubs", // Only has public stubs
"framework-appsearch.stubs.system",
"framework-connectivity.stubs.system",
diff --git a/api/Android.bp b/api/Android.bp
index b85dc46..db1f64c 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -161,6 +161,7 @@
genrule {
name: "frameworks-base-api-system-current.txt",
srcs: [
+ ":art.module.public.api{.system.api.txt}",
":android.net.ipsec.ike{.system.api.txt}",
":framework-appsearch{.system.api.txt}",
":framework-connectivity{.system.api.txt}",
@@ -215,6 +216,7 @@
genrule {
name: "frameworks-base-api-system-removed.txt",
srcs: [
+ ":art.module.public.api{.system.removed-api.txt}",
":android.net.ipsec.ike{.system.removed-api.txt}",
":framework-appsearch{.system.removed-api.txt}",
":framework-connectivity{.system.removed-api.txt}",
@@ -251,6 +253,7 @@
genrule {
name: "frameworks-base-api-module-lib-current.txt",
srcs: [
+ ":art.module.public.api{.module-lib.api.txt}",
":android.net.ipsec.ike{.module-lib.api.txt}",
":framework-appsearch{.module-lib.api.txt}",
":framework-connectivity{.module-lib.api.txt}",
@@ -307,6 +310,7 @@
genrule {
name: "frameworks-base-api-module-lib-removed.txt",
srcs: [
+ ":art.module.public.api{.module-lib.removed-api.txt}",
":android.net.ipsec.ike{.module-lib.removed-api.txt}",
":framework-appsearch{.module-lib.removed-api.txt}",
":framework-connectivity{.module-lib.removed-api.txt}",
diff --git a/boot/Android.bp b/boot/Android.bp
index e8d88a5..3caede4 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -48,57 +48,13 @@
// bootclasspath.
fragments: [
{
- apex: "com.android.appsearch",
- module: "com.android.appsearch-bootclasspath-fragment",
- },
- {
apex: "com.android.art",
module: "art-bootclasspath-fragment",
},
{
- apex: "com.android.conscrypt",
- module: "com.android.conscrypt-bootclasspath-fragment",
- },
- {
apex: "com.android.i18n",
module: "i18n-bootclasspath-fragment",
},
- {
- apex: "com.android.ipsec",
- module: "com.android.ipsec-bootclasspath-fragment",
- },
- {
- apex: "com.android.media",
- module: "com.android.media-bootclasspath-fragment",
- },
- {
- apex: "com.android.mediaprovider",
- module: "com.android.mediaprovider-bootclasspath-fragment",
- },
- {
- apex: "com.android.os.statsd",
- module: "com.android.os.statsd-bootclasspath-fragment",
- },
- {
- apex: "com.android.permission",
- module: "com.android.permission-bootclasspath-fragment",
- },
- {
- apex: "com.android.scheduling",
- module: "com.android.scheduling-bootclasspath-fragment",
- },
- {
- apex: "com.android.sdkext",
- module: "com.android.sdkext-bootclasspath-fragment",
- },
- {
- apex: "com.android.tethering",
- module: "com.android.tethering-bootclasspath-fragment",
- },
- {
- apex: "com.android.wifi",
- module: "com.android.wifi-bootclasspath-fragment",
- },
],
// Additional information needed by hidden api processing.
diff --git a/core/api/current.txt b/core/api/current.txt
index f546f53..d73e3a0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5958,7 +5958,6 @@
method @NonNull public android.app.Notification.Builder setRemoteInputHistory(CharSequence[]);
method @NonNull public android.app.Notification.Builder setSettingsText(CharSequence);
method @NonNull public android.app.Notification.Builder setShortcutId(String);
- method @Deprecated @NonNull public android.app.Notification.Builder setShowForegroundImmediately(boolean);
method @NonNull public android.app.Notification.Builder setShowWhen(boolean);
method @NonNull public android.app.Notification.Builder setSmallIcon(@DrawableRes int);
method @NonNull public android.app.Notification.Builder setSmallIcon(@DrawableRes int, int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9befc3b..6f68e3f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10529,7 +10529,7 @@
method @Nullable public android.media.MediaSyncEvent getMediaSyncEvent();
method public int getPersonalizedScore();
method public int getScore();
- method public boolean isPersonalizedHotwordDetection();
+ method public boolean isHotwordDetectionPersonalized();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int AUDIO_CHANNEL_UNSET = -1; // 0xffffffff
field public static final int CONFIDENCE_LEVEL_HIGH = 5; // 0x5
@@ -10549,11 +10549,11 @@
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setAudioChannel(int);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setConfidenceLevel(int);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setExtras(@NonNull android.os.PersistableBundle);
+ method @NonNull public android.service.voice.HotwordDetectedResult.Builder setHotwordDetectionPersonalized(boolean);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setHotwordDurationMillis(int);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setHotwordOffsetMillis(int);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setHotwordPhraseId(int);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setMediaSyncEvent(@NonNull android.media.MediaSyncEvent);
- method @NonNull public android.service.voice.HotwordDetectedResult.Builder setPersonalizedHotwordDetection(boolean);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setPersonalizedScore(int);
method @NonNull public android.service.voice.HotwordDetectedResult.Builder setScore(int);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 1865141..9b594b78 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4595,22 +4595,6 @@
}
/**
- * Set to {@code true} to require that the Notification associated with a
- * foreground service is shown as soon as the service's {@code startForeground()}
- * method is called, even if the system's UI policy might otherwise defer
- * its visibility to a later time.
- * @deprecated Use setForegroundServiceBehavior(int) instead
- */
- @Deprecated
- @NonNull
- public Builder setShowForegroundImmediately(boolean showImmediately) {
- setForegroundServiceBehavior(showImmediately
- ? FOREGROUND_SERVICE_IMMEDIATE
- : FOREGROUND_SERVICE_DEFAULT);
- return this;
- }
-
- /**
* Specify a desired visibility policy for a Notification associated with a
* foreground service. By default, the system can choose to defer
* visibility of the notification for a short time after the service is
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index be62deb7..edd6047 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -101,7 +101,7 @@
// We also check if the image has dark pixels in it,
// to avoid bright images with some dark spots.
private static final float DARK_PIXEL_CONTRAST = 5.5f;
- private static final float MAX_DARK_AREA = 0.025f;
+ private static final float MAX_DARK_AREA = 0.05f;
private final List<Color> mMainColors;
private final Map<Integer, Integer> mAllColors;
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index 95e43f3..846f2f9 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -118,7 +118,7 @@
* Returns whether the trigger has happened due to model having been personalized to fit user's
* voice.
*/
- private boolean mPersonalizedHotwordDetection = false;
+ private boolean mHotwordDetectionPersonalized = false;
/**
* Score for the hotword trigger.
@@ -270,7 +270,7 @@
int hotwordOffsetMillis,
int hotwordDurationMillis,
int audioChannel,
- boolean personalizedHotwordDetection,
+ boolean hotwordDetectionPersonalized,
int score,
int personalizedScore,
int hotwordPhraseId,
@@ -282,7 +282,7 @@
this.mHotwordOffsetMillis = hotwordOffsetMillis;
this.mHotwordDurationMillis = hotwordDurationMillis;
this.mAudioChannel = audioChannel;
- this.mPersonalizedHotwordDetection = personalizedHotwordDetection;
+ this.mHotwordDetectionPersonalized = hotwordDetectionPersonalized;
this.mScore = score;
this.mPersonalizedScore = personalizedScore;
this.mHotwordPhraseId = hotwordPhraseId;
@@ -334,8 +334,8 @@
* voice.
*/
@DataClass.Generated.Member
- public boolean isPersonalizedHotwordDetection() {
- return mPersonalizedHotwordDetection;
+ public boolean isHotwordDetectionPersonalized() {
+ return mHotwordDetectionPersonalized;
}
/**
@@ -400,7 +400,7 @@
"hotwordOffsetMillis = " + mHotwordOffsetMillis + ", " +
"hotwordDurationMillis = " + mHotwordDurationMillis + ", " +
"audioChannel = " + mAudioChannel + ", " +
- "personalizedHotwordDetection = " + mPersonalizedHotwordDetection + ", " +
+ "hotwordDetectionPersonalized = " + mHotwordDetectionPersonalized + ", " +
"score = " + mScore + ", " +
"personalizedScore = " + mPersonalizedScore + ", " +
"hotwordPhraseId = " + mHotwordPhraseId + ", " +
@@ -426,7 +426,7 @@
&& mHotwordOffsetMillis == that.mHotwordOffsetMillis
&& mHotwordDurationMillis == that.mHotwordDurationMillis
&& mAudioChannel == that.mAudioChannel
- && mPersonalizedHotwordDetection == that.mPersonalizedHotwordDetection
+ && mHotwordDetectionPersonalized == that.mHotwordDetectionPersonalized
&& mScore == that.mScore
&& mPersonalizedScore == that.mPersonalizedScore
&& mHotwordPhraseId == that.mHotwordPhraseId
@@ -445,7 +445,7 @@
_hash = 31 * _hash + mHotwordOffsetMillis;
_hash = 31 * _hash + mHotwordDurationMillis;
_hash = 31 * _hash + mAudioChannel;
- _hash = 31 * _hash + Boolean.hashCode(mPersonalizedHotwordDetection);
+ _hash = 31 * _hash + Boolean.hashCode(mHotwordDetectionPersonalized);
_hash = 31 * _hash + mScore;
_hash = 31 * _hash + mPersonalizedScore;
_hash = 31 * _hash + mHotwordPhraseId;
@@ -460,7 +460,7 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
int flg = 0;
- if (mPersonalizedHotwordDetection) flg |= 0x20;
+ if (mHotwordDetectionPersonalized) flg |= 0x20;
if (mMediaSyncEvent != null) flg |= 0x2;
dest.writeInt(flg);
dest.writeInt(mConfidenceLevel);
@@ -486,7 +486,7 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
int flg = in.readInt();
- boolean personalizedHotwordDetection = (flg & 0x20) != 0;
+ boolean hotwordDetectionPersonalized = (flg & 0x20) != 0;
int confidenceLevel = in.readInt();
MediaSyncEvent mediaSyncEvent = (flg & 0x2) == 0 ? null : (MediaSyncEvent) in.readTypedObject(MediaSyncEvent.CREATOR);
int hotwordOffsetMillis = in.readInt();
@@ -504,7 +504,7 @@
this.mHotwordOffsetMillis = hotwordOffsetMillis;
this.mHotwordDurationMillis = hotwordDurationMillis;
this.mAudioChannel = audioChannel;
- this.mPersonalizedHotwordDetection = personalizedHotwordDetection;
+ this.mHotwordDetectionPersonalized = hotwordDetectionPersonalized;
this.mScore = score;
this.mPersonalizedScore = personalizedScore;
this.mHotwordPhraseId = hotwordPhraseId;
@@ -541,7 +541,7 @@
private int mHotwordOffsetMillis;
private int mHotwordDurationMillis;
private int mAudioChannel;
- private boolean mPersonalizedHotwordDetection;
+ private boolean mHotwordDetectionPersonalized;
private int mScore;
private int mPersonalizedScore;
private int mHotwordPhraseId;
@@ -618,10 +618,10 @@
* voice.
*/
@DataClass.Generated.Member
- public @NonNull Builder setPersonalizedHotwordDetection(boolean value) {
+ public @NonNull Builder setHotwordDetectionPersonalized(boolean value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
- mPersonalizedHotwordDetection = value;
+ mHotwordDetectionPersonalized = value;
return this;
}
@@ -708,7 +708,7 @@
mAudioChannel = AUDIO_CHANNEL_UNSET;
}
if ((mBuilderFieldsSet & 0x20) == 0) {
- mPersonalizedHotwordDetection = false;
+ mHotwordDetectionPersonalized = false;
}
if ((mBuilderFieldsSet & 0x40) == 0) {
mScore = defaultScore();
@@ -728,7 +728,7 @@
mHotwordOffsetMillis,
mHotwordDurationMillis,
mAudioChannel,
- mPersonalizedHotwordDetection,
+ mHotwordDetectionPersonalized,
mScore,
mPersonalizedScore,
mHotwordPhraseId,
@@ -745,10 +745,10 @@
}
@DataClass.Generated(
- time = 1621631039729L,
+ time = 1621943150502L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java",
- inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mPersonalizedHotwordDetection\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/TunnelModeEnabledListener.java b/core/java/android/view/TunnelModeEnabledListener.java
new file mode 100644
index 0000000..c158da9
--- /dev/null
+++ b/core/java/android/view/TunnelModeEnabledListener.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Listens for tunnel mode enabled/disabled events from SurfaceFlinger.
+ * {@hide}
+ */
+public abstract class TunnelModeEnabledListener {
+
+ private long mNativeListener;
+ private final Executor mExecutor;
+
+ public TunnelModeEnabledListener(Executor executor) {
+ mExecutor = executor;
+ mNativeListener = nativeCreate(this);
+ }
+
+ /**
+ * Destroys the listener.
+ */
+ public void destroy() {
+ if (mNativeListener == 0) {
+ return;
+ }
+ unregister(this);
+ nativeDestroy(mNativeListener);
+ mNativeListener = 0;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ destroy();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Reports when tunnel mode has been enabled/disabled.
+ */
+ public abstract void onTunnelModeEnabledChanged(boolean tunnelModeEnabled);
+
+ /**
+ * Registers a listener.
+ */
+ public static void register(TunnelModeEnabledListener listener) {
+ if (listener.mNativeListener == 0) {
+ return;
+ }
+ nativeRegister(listener.mNativeListener);
+ }
+
+ /**
+ * Unregisters a listener.
+ */
+ public static void unregister(TunnelModeEnabledListener listener) {
+ if (listener.mNativeListener == 0) {
+ return;
+ }
+ nativeUnregister(listener.mNativeListener);
+ }
+
+ /**
+ * Dispatch tunnel mode enabled.
+ *
+ * Called from native code on a binder thread.
+ */
+ @VisibleForTesting
+ public static void dispatchOnTunnelModeEnabledChanged(TunnelModeEnabledListener listener,
+ boolean tunnelModeEnabled) {
+ listener.mExecutor.execute(() -> listener.onTunnelModeEnabledChanged(tunnelModeEnabled));
+ }
+
+ private static native long nativeCreate(TunnelModeEnabledListener thiz);
+ private static native void nativeDestroy(long ptr);
+ private static native void nativeRegister(long ptr);
+ private static native void nativeUnregister(long ptr);
+}
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 5bb8a8e..40cce7c 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppGlobals;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -61,8 +62,9 @@
@Deprecated
public class AnalogClock extends View {
private static final String LOG_TAG = "AnalogClock";
+
/** How many times per second that the seconds hand advances. */
- private static final long SECONDS_HAND_FPS = 30;
+ private final int mSecondsHandFps;
private Clock mClock;
@Nullable
@@ -106,6 +108,10 @@
public AnalogClock(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mSecondsHandFps = AppGlobals.getIntCoreSetting(
+ WidgetFlags.KEY_ANALOG_CLOCK_SECONDS_HAND_FPS,
+ WidgetFlags.ANALOG_CLOCK_SECONDS_HAND_FPS_DEFAULT);
+
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.AnalogClock, defStyleAttr, defStyleRes);
saveAttributeDataForStyleable(context, com.android.internal.R.styleable.AnalogClock,
@@ -743,7 +749,7 @@
// n positions between two given numbers, where n is the number of ticks per second. This
// ensures the second hand advances by a consistent distance despite our handler callbacks
// occurring at inconsistent frequencies.
- mSeconds = Math.round(rawSeconds * SECONDS_HAND_FPS) / (float) SECONDS_HAND_FPS;
+ mSeconds = Math.round(rawSeconds * mSecondsHandFps) / (float) mSecondsHandFps;
mMinutes = localTime.getMinute() + mSeconds / 60.0f;
mHour = localTime.getHour() + mMinutes / 60.0f;
mChanged = true;
@@ -781,7 +787,7 @@
// How many milliseconds through the second we currently are.
long millisOfSecond = Duration.ofNanos(localTime.getNano()).toMillis();
// How many milliseconds there are between tick positions for the seconds hand.
- double millisPerTick = 1000 / (double) SECONDS_HAND_FPS;
+ double millisPerTick = 1000 / (double) mSecondsHandFps;
// How many milliseconds we are past the last tick position.
long millisPastLastTick = Math.round(millisOfSecond % millisPerTick);
// How many milliseconds there are until the next tick position.
diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java
index 1a49365..0971268 100644
--- a/core/java/android/widget/WidgetFlags.java
+++ b/core/java/android/widget/WidgetFlags.java
@@ -199,6 +199,17 @@
*/
public static final float MAGNIFIER_ASPECT_RATIO_DEFAULT = 5.5f;
+ /** The flag of the fps of the analog clock seconds hand. */
+ public static final String ANALOG_CLOCK_SECONDS_HAND_FPS =
+ "AnalogClockFeature__analog_clock_seconds_hand_fps";
+
+ /** The key name used in app core settings for {@link #ANALOG_CLOCK_SECONDS_HAND_FPS}. */
+ public static final String KEY_ANALOG_CLOCK_SECONDS_HAND_FPS =
+ "widget__analog_clock_seconds_hand_fps";
+
+ /** Default value for the flag {@link #ANALOG_CLOCK_SECONDS_HAND_FPS}. */
+ public static final int ANALOG_CLOCK_SECONDS_HAND_FPS_DEFAULT = 1;
+
private WidgetFlags() {
}
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 1f805c9..1468633 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -128,6 +128,7 @@
"android_graphics_BLASTBufferQueue.cpp",
"android_view_SurfaceSession.cpp",
"android_view_TextureView.cpp",
+ "android_view_TunnelModeEnabledListener.cpp",
"android_view_VelocityTracker.cpp",
"android_view_VerifiedKeyEvent.cpp",
"android_view_VerifiedMotionEvent.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f76cccb..7e8fc7e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -127,6 +127,7 @@
extern int register_android_view_SurfaceSession(JNIEnv* env);
extern int register_android_view_CompositionSamplingListener(JNIEnv* env);
extern int register_android_view_TextureView(JNIEnv* env);
+extern int register_android_view_TunnelModeEnabledListener(JNIEnv* env);
extern int register_android_database_CursorWindow(JNIEnv* env);
extern int register_android_database_SQLiteConnection(JNIEnv* env);
extern int register_android_database_SQLiteGlobal(JNIEnv* env);
@@ -1521,6 +1522,7 @@
REG_JNI(register_android_view_SurfaceSession),
REG_JNI(register_android_view_CompositionSamplingListener),
REG_JNI(register_android_view_TextureView),
+ REG_JNI(register_android_view_TunnelModeEnabledListener),
REG_JNI(register_com_google_android_gles_jni_EGLImpl),
REG_JNI(register_com_google_android_gles_jni_GLImpl),
REG_JNI(register_android_opengl_jni_EGL14),
diff --git a/core/jni/android_view_TunnelModeEnabledListener.cpp b/core/jni/android_view_TunnelModeEnabledListener.cpp
new file mode 100644
index 0000000..af7bae8
--- /dev/null
+++ b/core/jni/android_view_TunnelModeEnabledListener.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TunnelModeEnabledListener"
+
+#include "android_util_Binder.h"
+#include "core_jni_helpers.h"
+
+#include <nativehelper/JNIHelp.h>
+
+#include <android/gui/BnTunnelModeEnabledListener.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <utils/Log.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <ui/Rect.h>
+
+namespace android {
+
+namespace {
+
+struct {
+ jclass mClass;
+ jmethodID mDispatchOnTunnelModeEnabledChanged;
+} gListenerClassInfo;
+
+struct TunnelModeEnabledListener : public gui::BnTunnelModeEnabledListener {
+ TunnelModeEnabledListener(JNIEnv* env, jobject listener)
+ : mListener(env->NewWeakGlobalRef(listener)) {}
+
+ binder::Status onTunnelModeEnabledChanged(bool tunnelModeEnabled) override {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ LOG_ALWAYS_FATAL_IF(env == nullptr,
+ "Unable to retrieve JNIEnv in onTunnelModeEnabledChanged.");
+
+ jobject listener = env->NewGlobalRef(mListener);
+ if (listener == NULL) {
+ // Weak reference went out of scope
+ return binder::Status::ok();
+ }
+ env->CallStaticVoidMethod(gListenerClassInfo.mClass,
+ gListenerClassInfo.mDispatchOnTunnelModeEnabledChanged, listener,
+ static_cast<jboolean>(tunnelModeEnabled));
+ env->DeleteGlobalRef(listener);
+
+ if (env->ExceptionCheck()) {
+ ALOGE("TunnelModeEnabledListener.onTunnelModeEnabledChanged() failed.");
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+ return binder::Status::ok();
+ }
+
+protected:
+ virtual ~TunnelModeEnabledListener() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->DeleteWeakGlobalRef(mListener);
+ }
+
+private:
+ jweak mListener;
+};
+
+jlong nativeCreate(JNIEnv* env, jclass clazz, jobject obj) {
+ TunnelModeEnabledListener* listener = new TunnelModeEnabledListener(env, obj);
+ listener->incStrong((void*)nativeCreate);
+ return reinterpret_cast<jlong>(listener);
+}
+
+void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
+ TunnelModeEnabledListener* listener = reinterpret_cast<TunnelModeEnabledListener*>(ptr);
+ listener->decStrong((void*)nativeCreate);
+}
+
+void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<TunnelModeEnabledListener> listener = reinterpret_cast<TunnelModeEnabledListener*>(ptr);
+ if (SurfaceComposerClient::addTunnelModeEnabledListener(listener) != OK) {
+ constexpr auto error_msg = "Couldn't addTunnelModeEnabledListener";
+ ALOGE(error_msg);
+ jniThrowRuntimeException(env, error_msg);
+ }
+}
+
+void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<TunnelModeEnabledListener> listener = reinterpret_cast<TunnelModeEnabledListener*>(ptr);
+
+ if (SurfaceComposerClient::removeTunnelModeEnabledListener(listener) != OK) {
+ constexpr auto error_msg = "Couldn't removeTunnelModeEnabledListener";
+ ALOGE(error_msg);
+ jniThrowRuntimeException(env, error_msg);
+ }
+}
+
+const JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ {"nativeCreate", "(Landroid/view/TunnelModeEnabledListener;)J", (void*)nativeCreate},
+ {"nativeDestroy", "(J)V", (void*)nativeDestroy},
+ {"nativeRegister", "(J)V", (void*)nativeRegister},
+ {"nativeUnregister", "(J)V", (void*)nativeUnregister}};
+
+} // namespace
+
+int register_android_view_TunnelModeEnabledListener(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/view/TunnelModeEnabledListener", gMethods,
+ NELEM(gMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ jclass clazz = env->FindClass("android/view/TunnelModeEnabledListener");
+ gListenerClassInfo.mClass = MakeGlobalRefOrDie(env, clazz);
+ gListenerClassInfo.mDispatchOnTunnelModeEnabledChanged =
+ env->GetStaticMethodID(clazz, "dispatchOnTunnelModeEnabledChanged",
+ "(Landroid/view/TunnelModeEnabledListener;Z)V");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ab53b4c..28f18a4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3971,6 +3971,9 @@
<!-- URI for in call notification sound -->
<string translatable="false" name="config_inCallNotificationSound">/product/media/audio/ui/InCallNotification.ogg</string>
+ <!-- URI for default ringtone sound file to be used for silent ringer vibration -->
+ <string translatable="false" name="config_defaultRingtoneVibrationSound">/product/media/audio/ui/AttentionalHaptics.ogg</string>
+
<!-- Default number of notifications from the same app before they are automatically grouped by the OS -->
<integer translatable="false" name="config_autoGroupAtCount">4</integer>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 552898b..ef5cfe3 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3236,6 +3236,8 @@
<public name="config_systemCompanionDeviceProvider"/>
<!-- @hide @SystemApi -->
<public name="config_systemUi" />
+ <!-- @hide For use by platform and tools only. Developers should not specify this value. -->
+ <public name="config_defaultRingtoneVibrationSound"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01020055">
diff --git a/core/tests/coretests/src/android/view/TunnelModeEnabledListenerTest.java b/core/tests/coretests/src/android/view/TunnelModeEnabledListenerTest.java
new file mode 100644
index 0000000..65dd34f
--- /dev/null
+++ b/core/tests/coretests/src/android/view/TunnelModeEnabledListenerTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+@Presubmit
+public class TunnelModeEnabledListenerTest {
+ private TestableTunnelModeEnabledListener mListener;
+
+ @Before
+ public void init() {
+ mListener = new TestableTunnelModeEnabledListener();
+ }
+
+ @Test
+ public void testRegisterUnregister() {
+ TunnelModeEnabledListener.register(mListener);
+ TunnelModeEnabledListener.unregister(mListener);
+ }
+
+ @Test
+ public void testDispatchUpdatesListener() {
+ TunnelModeEnabledListener.dispatchOnTunnelModeEnabledChanged(mListener, true);
+ assertEquals(true, mListener.mTunnelModeEnabled.get());
+ TunnelModeEnabledListener.dispatchOnTunnelModeEnabledChanged(mListener, false);
+ assertEquals(false, mListener.mTunnelModeEnabled.get());
+ }
+
+ @Test
+ public void testRegisterUpdatesListener() throws Exception {
+ TunnelModeEnabledListener.register(mListener);
+ TimeUnit.SECONDS.sleep(1);
+ assertTrue(mListener.mTunnelModeEnabledUpdated.get());
+ TunnelModeEnabledListener.unregister(mListener);
+ }
+
+ private static class TestableTunnelModeEnabledListener extends TunnelModeEnabledListener {
+ AtomicBoolean mTunnelModeEnabled;
+ AtomicBoolean mTunnelModeEnabledUpdated;
+
+ TestableTunnelModeEnabledListener() {
+ super(Runnable::run);
+ mTunnelModeEnabled = new AtomicBoolean(false);
+ mTunnelModeEnabledUpdated = new AtomicBoolean();
+ }
+
+ @Override
+ public void onTunnelModeEnabledChanged(boolean tunnelModeEnabled) {
+ mTunnelModeEnabled.set(tunnelModeEnabled);
+ mTunnelModeEnabledUpdated.set(true);
+ }
+ }
+
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 19332c7..8d4739d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -473,6 +473,7 @@
<permission name="android.permission.UPDATE_FONTS" />
<!-- Permission required for hotword detection service CTS tests -->
<permission name="android.permission.MANAGE_HOTWORD_DETECTION" />
+ <permission name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" />
<permission name="android.permission.MANAGE_APP_HIBERNATION"/>
<!-- Permission required for CTS test - ResourceObserverNativeTest -->
<permission name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 187e104..c37e88a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -277,8 +277,9 @@
// waiting for setContentView before relayoutWindow
SplashScreenView contentView = viewSupplier.get();
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
- // if record == null, either the starting window added fail or removed already.
- if (record != null) {
+ // If record == null, either the starting window added fail or removed already.
+ // Do not add this view if the token is mismatch.
+ if (record != null && appToken == record.mAppToken) {
// if view == null then creation of content view was failed.
if (contentView != null) {
try {
@@ -297,15 +298,16 @@
try {
final WindowManager wm = context.getSystemService(WindowManager.class);
- postAddWindow(taskId, appToken, rootLayout, wm, params);
-
- // We use the splash screen worker thread to create SplashScreenView while adding the
- // window, as otherwise Choreographer#doFrame might be delayed on this thread.
- // And since Choreographer#doFrame won't happen immediately after adding the window, if
- // the view is not added to the PhoneWindow on the first #doFrame, the view will not be
- // rendered on the first frame. So here we need to synchronize the view on the window
- // before first round relayoutWindow, which will happen after insets animation.
- mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null);
+ if (postAddWindow(taskId, appToken, rootLayout, wm, params)) {
+ // We use the splash screen worker thread to create SplashScreenView while adding
+ // the window, as otherwise Choreographer#doFrame might be delayed on this thread.
+ // And since Choreographer#doFrame won't happen immediately after adding the window,
+ // if the view is not added to the PhoneWindow on the first #doFrame, the view will
+ // not be rendered on the first frame. So here we need to synchronize the view on
+ // the window before first round relayoutWindow, which will happen after insets
+ // animation.
+ mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null);
+ }
} catch (RuntimeException e) {
// don't crash if something else bad happens, for example a
// failure loading resources because we are loading from an app
@@ -347,7 +349,8 @@
final int taskId = startingWindowInfo.taskInfo.taskId;
final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
snapshot, mSplashScreenExecutor, () -> removeWindowNoAnimate(taskId));
- final StartingWindowRecord tView = new StartingWindowRecord(null/* decorView */, surface);
+ final StartingWindowRecord tView = new StartingWindowRecord(appToken,
+ null/* decorView */, surface);
mStartingWindowRecords.put(taskId, tView);
}
@@ -382,7 +385,7 @@
ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
}
- protected void postAddWindow(int taskId, IBinder appToken, View view, WindowManager wm,
+ protected boolean postAddWindow(int taskId, IBinder appToken, View view, WindowManager wm,
WindowManager.LayoutParams params) {
boolean shouldSaveView = true;
try {
@@ -401,12 +404,13 @@
}
if (shouldSaveView) {
removeWindowNoAnimate(taskId);
- saveSplashScreenRecord(taskId, view);
+ saveSplashScreenRecord(appToken, taskId, view);
}
+ return shouldSaveView;
}
- private void saveSplashScreenRecord(int taskId, View view) {
- final StartingWindowRecord tView = new StartingWindowRecord(view,
+ private void saveSplashScreenRecord(IBinder appToken, int taskId, View view) {
+ final StartingWindowRecord tView = new StartingWindowRecord(appToken, view,
null/* TaskSnapshotWindow */);
mStartingWindowRecords.put(taskId, tView);
}
@@ -468,12 +472,15 @@
* Record the view or surface for a starting window.
*/
private static class StartingWindowRecord {
+ private final IBinder mAppToken;
private final View mDecorView;
private final TaskSnapshotWindow mTaskSnapshotWindow;
private SplashScreenView mContentView;
private boolean mSetSplashScreen;
- StartingWindowRecord(View decorView, TaskSnapshotWindow taskSnapshotWindow) {
+ StartingWindowRecord(IBinder appToken, View decorView,
+ TaskSnapshotWindow taskSnapshotWindow) {
+ mAppToken = appToken;
mDecorView = decorView;
mTaskSnapshotWindow = taskSnapshotWindow;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 2623535..4e3e133 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -83,11 +83,12 @@
}
@Override
- protected void postAddWindow(int taskId, IBinder appToken,
+ protected boolean postAddWindow(int taskId, IBinder appToken,
View view, WindowManager wm, WindowManager.LayoutParams params) {
// listen for addView
mAddWindowForTask = taskId;
mViewThemeResId = view.getContext().getThemeResId();
+ return true;
}
@Override
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 79d505e..f8297bc 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -405,9 +405,11 @@
*/
public void play() {
if (mLocalPlayer != null) {
- // do not play ringtones if stream volume is 0
- // (typically because ringer mode is silent).
- if (mAudioManager.getStreamVolume(
+ // Play ringtones if stream volume is over 0 or if it is a haptic-only ringtone
+ // (typically because ringer mode is vibrate).
+ boolean isHapticOnly = AudioManager.hasHapticChannels(mUri)
+ && !mAudioAttributes.areHapticChannelsMuted() && mVolume == 0;
+ if (isHapticOnly || mAudioManager.getStreamVolume(
AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) {
startLocalPlayer();
}
diff --git a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
index ed447f8..3d93964 100644
--- a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
+++ b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
@@ -16,6 +16,7 @@
package com.android.settingslib.transition;
+import androidx.annotation.IntDef;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
@@ -29,11 +30,31 @@
import com.google.android.material.transition.platform.MaterialSharedAxis;
import com.google.android.material.transition.platform.SlideDistanceProvider;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A helper class to apply Settings Transition
*/
public class SettingsTransitionHelper {
+ /**
+ * Flags indicating the type of the transition.
+ */
+ @IntDef({
+ TransitionType.TRANSITION_NONE,
+ TransitionType.TRANSITION_SHARED_AXIS,
+ TransitionType.TRANSITION_SLIDE,
+ TransitionType.TRANSITION_FADE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransitionType {
+ int TRANSITION_NONE = -1;
+ int TRANSITION_SHARED_AXIS = 0;
+ int TRANSITION_SLIDE = 1;
+ int TRANSITION_FADE = 2;
+ }
+
private static final String TAG = "SettingsTransitionHelper";
private static final long DURATION = 450L;
private static final float FADE_THROUGH_THRESHOLD = 0.22F;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4558a8a..fe92e26 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1222,19 +1222,22 @@
*/
public Pair<Drawable, String> getDrawableWithDescription() {
Uri uri = BluetoothUtils.getUriMetaData(mDevice, BluetoothDevice.METADATA_MAIN_ICON);
+ Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
+ mContext, this);
+
if (BluetoothUtils.isAdvancedDetailsHeader(mDevice) && uri != null) {
BitmapDrawable drawable = mDrawableCache.get(uri.toString());
if (drawable != null) {
Resources resources = mContext.getResources();
return new Pair<>(new AdaptiveOutlineDrawable(
- resources, drawable.getBitmap()),
- BluetoothUtils.getBtClassDrawableWithDescription(mContext, this).second);
+ resources, drawable.getBitmap()), pair.second);
}
refresh();
}
- return BluetoothUtils.getBtRainbowDrawableWithDescription(mContext, this);
+ return new Pair<>(BluetoothUtils.buildBtRainbowDrawable(
+ mContext, pair.first, getAddress().hashCode()), pair.second);
}
void releaseLruCache() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 38172f7..f523354 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -31,7 +31,9 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.graphics.drawable.BitmapDrawable;
import android.media.AudioManager;
+import android.util.LruCache;
import com.android.settingslib.R;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
@@ -961,6 +963,10 @@
@Test
public void getDrawableWithDescription_isAdvancedDevice_returnAdvancedIcon() {
+ LruCache lruCache = mock(LruCache.class);
+ mCachedDevice.mDrawableCache = lruCache;
+ BitmapDrawable drawable = mock(BitmapDrawable.class);
+ when(lruCache.get("fake_uri")).thenReturn(drawable);
when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON))
.thenReturn("fake_uri".getBytes());
when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 6e256c1..d2947c6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -528,6 +528,7 @@
<!-- Permission required for hotword detection service CTS tests -->
<uses-permission android:name="android.permission.MANAGE_HOTWORD_DETECTION" />
+ <uses-permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE" />
<uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/>
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 1cf0c5f..2b87737 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -4,12 +4,15 @@
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.ActivityManager
+import android.app.ActivityTaskManager
import android.app.PendingIntent
import android.content.Context
import android.graphics.Matrix
import android.graphics.Rect
+import android.graphics.RectF
import android.os.Looper
import android.os.RemoteException
+import android.util.Log
import android.util.MathUtils
import android.view.IRemoteAnimationFinishedCallback
import android.view.IRemoteAnimationRunner
@@ -30,6 +33,8 @@
* nicely into the starting window.
*/
class ActivityLaunchAnimator(context: Context) {
+ private val TAG = this::class.java.simpleName
+
companion object {
const val ANIMATION_DURATION = 500L
const val ANIMATION_DURATION_FADE_OUT_CONTENT = 183L
@@ -78,29 +83,49 @@
* If [controller] is null or [animate] is false, then the intent will be started and no
* animation will run.
*
+ * If possible, you should pass the [packageName] of the intent that will be started so that
+ * trampoline activity launches will also be animated.
+ *
* This method will throw any exception thrown by [intentStarter].
*/
@JvmOverloads
- inline fun startIntentWithAnimation(
+ fun startIntentWithAnimation(
controller: Controller?,
animate: Boolean = true,
+ packageName: String? = null,
intentStarter: (RemoteAnimationAdapter?) -> Int
) {
if (controller == null || !animate) {
+ Log.d(TAG, "Starting intent with no animation")
intentStarter(null)
controller?.callOnIntentStartedOnMainThread(willAnimate = false)
return
}
+ Log.d(TAG, "Starting intent with a launch animation")
val runner = Runner(controller)
val animationAdapter = RemoteAnimationAdapter(
runner,
ANIMATION_DURATION,
ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
)
+
+ // Register the remote animation for the given package to also animate trampoline
+ // activity launches.
+ if (packageName != null) {
+ try {
+ ActivityTaskManager.getService().registerRemoteAnimationForNextActivityStart(
+ packageName, animationAdapter)
+ } catch (e: RemoteException) {
+ Log.w(TAG, "Unable to register the remote animation", e)
+ }
+ }
+
val launchResult = intentStarter(animationAdapter)
val willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT ||
launchResult == ActivityManager.START_SUCCESS
+
+ Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate")
controller.callOnIntentStartedOnMainThread(willAnimate)
// If we expect an animation, post a timeout to cancel it in case the remote animation is
@@ -110,7 +135,6 @@
}
}
- @PublishedApi
internal fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) {
if (Looper.myLooper() != Looper.getMainLooper()) {
this.launchContainer.context.mainExecutor.execute {
@@ -125,15 +149,21 @@
* Same as [startIntentWithAnimation] but allows [intentStarter] to throw a
* [PendingIntent.CanceledException] which must then be handled by the caller. This is useful
* for Java caller starting a [PendingIntent].
+ *
+ * If possible, you should pass the [packageName] of the intent that will be started so that
+ * trampoline activity launches will also be animated.
*/
@Throws(PendingIntent.CanceledException::class)
@JvmOverloads
fun startPendingIntentWithAnimation(
controller: Controller?,
animate: Boolean = true,
+ packageName: String? = null,
intentStarter: PendingIntentStarter
) {
- startIntentWithAnimation(controller, animate) { intentStarter.startPendingIntent(it) }
+ startIntentWithAnimation(controller, animate, packageName) {
+ intentStarter.startPendingIntent(it)
+ }
}
/** Create a new animation [Runner] controlled by [controller]. */
@@ -278,11 +308,14 @@
@VisibleForTesting
inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
private val launchContainer = controller.launchContainer
- @PublishedApi internal val context = launchContainer.context
+ private val context = launchContainer.context
private val transactionApplier = SyncRtSurfaceTransactionApplier(launchContainer)
private var animator: ValueAnimator? = null
+ private val matrix = Matrix()
+ private val invertMatrix = Matrix()
private var windowCrop = Rect()
+ private var windowCropF = RectF()
private var timedOut = false
private var cancelled = false
@@ -294,7 +327,6 @@
// posting it.
private var onTimeout = Runnable { onAnimationTimedOut() }
- @PublishedApi
internal fun postTimeout() {
launchContainer.postDelayed(onTimeout, LAUNCH_TIMEOUT)
}
@@ -336,11 +368,13 @@
remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
iCallback: IRemoteAnimationFinishedCallback
) {
+ Log.d(TAG, "Remote animation started")
val window = remoteAnimationTargets.firstOrNull {
it.mode == RemoteAnimationTarget.MODE_OPENING
}
if (window == null) {
+ Log.d(TAG, "Aborting the animation as no window is opening")
removeTimeout()
invokeCallback(iCallback)
controller.onLaunchAnimationCancelled()
@@ -399,10 +433,12 @@
animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
+ Log.d(TAG, "Animation started")
controller.onLaunchAnimationStart(isExpandingFullyAbove)
}
override fun onAnimationEnd(animation: Animator?) {
+ Log.d(TAG, "Animation ended")
invokeCallback(iCallback)
controller.onLaunchAnimationEnd(isExpandingFullyAbove)
}
@@ -447,19 +483,52 @@
}
private fun applyStateToWindow(window: RemoteAnimationTarget, state: State) {
- val m = Matrix()
- m.postTranslate(0f, (state.top - window.sourceContainerBounds.top).toFloat())
- windowCrop.set(state.left, 0, state.right, state.height)
+ val screenBounds = window.screenSpaceBounds
+ val centerX = (screenBounds.left + screenBounds.right) / 2f
+ val centerY = (screenBounds.top + screenBounds.bottom) / 2f
+ val width = screenBounds.right - screenBounds.left
+ val height = screenBounds.bottom - screenBounds.top
- val cornerRadius = minOf(state.topCornerRadius, state.bottomCornerRadius)
+ // Scale the window. We use the max of (widthRatio, heightRatio) so that there is no
+ // blank space on any side.
+ val widthRatio = state.width.toFloat() / width
+ val heightRatio = state.height.toFloat() / height
+ val scale = maxOf(widthRatio, heightRatio)
+ matrix.reset()
+ matrix.setScale(scale, scale, centerX, centerY)
+
+ // Align it to the top and center it in the x-axis.
+ val heightChange = height * scale - height
+ val translationX = state.centerX - centerX
+ val translationY = state.top - screenBounds.top + heightChange / 2f
+ matrix.postTranslate(translationX, translationY)
+
+ // Crop it. The matrix will also be applied to the crop, so we apply the inverse
+ // operation. Given that we only scale (by factor > 0) then translate, we can assume
+ // that the matrix is invertible.
+ val cropX = state.left.toFloat() - screenBounds.left
+ val cropY = state.top.toFloat() - screenBounds.top
+ windowCropF.set(cropX, cropY, cropX + state.width, cropY + state.height)
+ matrix.invert(invertMatrix)
+ invertMatrix.mapRect(windowCropF)
+ windowCrop.set(
+ windowCropF.left.roundToInt(),
+ windowCropF.top.roundToInt(),
+ windowCropF.right.roundToInt(),
+ windowCropF.bottom.roundToInt()
+ )
+
+ // The scale will also be applied to the corner radius, so we divide by the scale to
+ // keep the original radius.
+ val cornerRadius = minOf(state.topCornerRadius, state.bottomCornerRadius) / scale
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash)
- .withAlpha(1f)
- .withMatrix(m)
- .withWindowCrop(windowCrop)
- .withLayer(window.prefixOrderIndex)
- .withCornerRadius(cornerRadius)
- .withVisibility(true)
- .build()
+ .withAlpha(1f)
+ .withMatrix(matrix)
+ .withWindowCrop(windowCrop)
+ .withLayer(window.prefixOrderIndex)
+ .withCornerRadius(cornerRadius)
+ .withVisibility(true)
+ .build()
transactionApplier.scheduleApply(params)
}
@@ -474,12 +543,13 @@
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash)
if (fadeInProgress > 0) {
- val m = Matrix()
- m.postTranslate(0f, (state.top - navigationBar.sourceContainerBounds.top).toFloat())
+ matrix.reset()
+ matrix.setTranslate(
+ 0f, (state.top - navigationBar.sourceContainerBounds.top).toFloat())
windowCrop.set(state.left, 0, state.right, state.height)
params
.withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress))
- .withMatrix(m)
+ .withMatrix(matrix)
.withWindowCrop(windowCrop)
.withVisibility(true)
} else {
@@ -496,6 +566,7 @@
return
}
+ Log.d(TAG, "Remote animation timed out")
timedOut = true
controller.onLaunchAnimationCancelled()
}
@@ -505,6 +576,7 @@
return
}
+ Log.d(TAG, "Remote animation was cancelled")
cancelled = true
removeTimeout()
context.mainExecutor.execute {
diff --git a/packages/SystemUI/res/drawable/ic_cake.xml b/packages/SystemUI/res/drawable/ic_cake.xml
new file mode 100644
index 0000000..9c83b43
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_cake.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19,14v-4c0,-1.1 -0.9,-2 -2,-2h-4L13,6.55c0.15,-0.09 0.29,-0.18 0.41,-0.31 0.39,-0.39 0.59,-0.92 0.59,-1.42s-0.2,-1.02 -0.59,-1.41L12,2l-1.41,1.41c-0.39,0.39 -0.59,0.91 -0.59,1.41s0.2,1.03 0.59,1.42c0.13,0.13 0.27,0.22 0.41,0.31L11,8L7,8c-1.1,0 -2,0.9 -2,2v4c-1.1,0 -2,0.9 -2,2v4c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-4c0,-1.1 -0.9,-2 -2,-2zM7,10h10v4L7,14v-4zM19,20L5,20v-4h14v4z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_celebration.xml b/packages/SystemUI/res/drawable/ic_celebration.xml
new file mode 100644
index 0000000..10fe406
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_celebration.xml
@@ -0,0 +1,37 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M2,22l14,-5L7,8L2,22zM12.35,16.18L5.3,18.7l2.52,-7.05L12.35,16.18z"
+ android:fillColor="#FFFFFFFF"/>
+ <path
+ android:pathData="M14.53,12.53l5.59,-5.59c0.49,-0.49 1.28,-0.49 1.77,0l0.59,0.59l1.06,-1.06l-0.59,-0.59c-1.07,-1.07 -2.82,-1.07 -3.89,0l-5.59,5.59L14.53,12.53z"
+ android:fillColor="#FFFFFFFF"/>
+ <path
+ android:pathData="M10.06,6.88L9.47,7.47l1.06,1.06l0.59,-0.59c1.07,-1.07 1.07,-2.82 0,-3.89l-0.59,-0.59L9.47,4.53l0.59,0.59C10.54,5.6 10.54,6.4 10.06,6.88z"
+ android:fillColor="#FFFFFFFF"/>
+ <path
+ android:pathData="M17.06,11.88l-1.59,1.59l1.06,1.06l1.59,-1.59c0.49,-0.49 1.28,-0.49 1.77,0l1.61,1.61l1.06,-1.06l-1.61,-1.61C19.87,10.81 18.13,10.81 17.06,11.88z"
+ android:fillColor="#FFFFFFFF"/>
+ <path
+ android:pathData="M15.06,5.88l-3.59,3.59l1.06,1.06l3.59,-3.59c1.07,-1.07 1.07,-2.82 0,-3.89l-1.59,-1.59l-1.06,1.06l1.59,1.59C15.54,4.6 15.54,5.4 15.06,5.88z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_gift.xml b/packages/SystemUI/res/drawable/ic_gift.xml
new file mode 100644
index 0000000..fab36c3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_gift.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M22,5h-5.18C16.93,4.69 17,4.35 17,4c0,-1.65 -1.35,-3 -3,-3c-0.77,0 -1.47,0.3 -2,0.78C11.47,1.3 10.77,1 10,1C8.35,1 7,2.35 7,4c0,0.35 0.07,0.69 0.18,1H2v6h2v11h16V11h2V5zM14,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1s-1,-0.45 -1,-1S13.45,3 14,3zM9,4c0,-0.55 0.45,-1 1,-1s1,0.45 1,1s-0.45,1 -1,1S9,4.55 9,4zM4,7h7v2H4V7zM6,11h5v9H6V11zM18,20h-5v-9h5V20zM20,9h-7V7h7V9z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_pages.xml b/packages/SystemUI/res/drawable/ic_pages.xml
new file mode 100644
index 0000000..9cd076d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_pages.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M19,5v14L5,19L5,5h14m0,-2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,17l1.57,-3.43L17,12l-3.43,-1.57L12,7l-1.57,3.43L7,12l3.43,1.57L12,17z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_play_games.xml b/packages/SystemUI/res/drawable/ic_play_games.xml
new file mode 100644
index 0000000..20096f4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_play_games.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M20.47,8c-0.39,-2.3 -2.4,-4 -4.81,-4L8.34,4C5.93,4 3.93,5.7 3.53,8 3.53,8 2,16.8 2,16.93 2,18.07 2.93,19 4.07,19c0.57,0 1.09,-0.2 1.47,-0.58L9,15h6l3.46,3.42c0.38,0.38 0.89,0.58 1.47,0.58 1.15,0 2.07,-0.93 2.07,-2.07C22,16.8 20.47,8 20.47,8zM11,10L9,10v2L8,12v-2L6,10L6,9h2L8,7h1v2h2v1zM13.8,10.3c-0.44,0 -0.8,-0.36 -0.8,-0.8 0,-0.44 0.36,-0.8 0.8,-0.8 0.44,0 0.8,0.36 0.8,0.8 0,0.44 -0.36,0.8 -0.8,0.8zM15.49,12c-0.44,0 -0.8,-0.36 -0.8,-0.8 0,-0.44 0.36,-0.8 0.8,-0.8 0.44,0 0.8,0.36 0.8,0.8 0,0.44 -0.36,0.8 -0.8,0.8zM15.49,8.6c-0.44,0 -0.8,-0.36 -0.8,-0.8s0.36,-0.8 0.8,-0.8c0.44,0 0.8,0.36 0.8,0.8s-0.36,0.8 -0.8,0.8zM17.2,10.3c-0.44,0 -0.8,-0.36 -0.8,-0.8 0,-0.44 0.36,-0.8 0.8,-0.8s0.8,0.36 0.8,0.8c0,0.44 -0.36,0.8 -0.8,0.8z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_video.xml b/packages/SystemUI/res/drawable/ic_video.xml
new file mode 100644
index 0000000..3668338
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_video.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M9,7v8l7,-4zM21,3L3,3c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h5v2h8v-2h5c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,17L3,17L3,5h18v12z"
+ android:fillColor="#FFFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
index 4994c69..e294dad 100644
--- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
@@ -34,8 +34,6 @@
<ImageView
android:id="@+id/person_icon"
- android:layout_marginStart="-2dp"
- android:layout_marginTop="-2dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
@@ -73,12 +71,12 @@
<include layout="@layout/people_tile_emoji_background_large" />
<TextView
- android:layout_gravity="top"
android:id="@+id/name"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/below_name_text_padding"
- android:gravity="start|top"
+ android:layout_gravity="start|center_vertical"
+ android:gravity="start|center_vertical"
android:singleLine="true"
android:ellipsize="end"
android:text="@string/empty_user_name"
@@ -103,7 +101,7 @@
<ImageView
android:id="@+id/predefined_icon"
- android:tint="?android:attr/colorAccent"
+ android:tint="?android:attr/textColorTertiary"
android:gravity="start|center_vertical"
android:layout_width="@dimen/regular_predefined_icon"
android:layout_height="@dimen/regular_predefined_icon" />
diff --git a/packages/SystemUI/res/layout/people_tile_medium_empty.xml b/packages/SystemUI/res/layout/people_tile_medium_empty.xml
index bebc872..8b2fddc 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_empty.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_empty.xml
@@ -27,19 +27,23 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ImageView
- android:id="@+id/person_icon"
- android:layout_marginTop="-2dp"
- android:layout_marginStart="-2dp"
- android:layout_width="64dp"
- android:layout_height="64dp" />
- <ImageView
- android:id="@+id/availability"
- android:gravity="top"
- android:layout_marginStart="-2dp"
- android:layout_width="10dp"
- android:layout_height="10dp"
- android:background="@drawable/availability_dot_10dp" />
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="64dp"
+ android:layout_height="64dp" />
+ <ImageView
+ android:id="@+id/availability"
+ android:layout_width="10dp"
+ android:layout_height="10dp"
+ android:gravity="top"
+ android:layout_gravity="top"
+ android:layout_marginStart="-2dp"
+ android:background="@drawable/availability_dot_10dp" />
+ </LinearLayout>
<LinearLayout
android:orientation="vertical"
android:paddingStart="4dp"
diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index a7a6354..47cab42 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -47,8 +47,6 @@
<ImageView
android:gravity="start"
android:id="@+id/person_icon"
- android:layout_marginStart="-2dp"
- android:layout_marginTop="-2dp"
android:layout_width="52dp"
android:layout_height="52dp" />
@@ -112,7 +110,8 @@
android:clipToOutline="true">
<TextView
android:id="@+id/name"
- android:gravity="center_vertical"
+ android:layout_gravity="start|center_vertical"
+ android:gravity="start|center_vertical"
android:layout_weight="1"
android:text="@string/empty_user_name"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
@@ -140,7 +139,7 @@
/>
<ImageView
android:id="@+id/predefined_icon"
- android:tint="?android:attr/colorAccent"
+ android:tint="?android:attr/textColorTertiary"
android:gravity="end|center_vertical"
android:layout_width="@dimen/regular_predefined_icon"
android:layout_height="@dimen/regular_predefined_icon" />
diff --git a/packages/SystemUI/res/layout/people_tile_small.xml b/packages/SystemUI/res/layout/people_tile_small.xml
index 4e5c04c..7a1371d 100644
--- a/packages/SystemUI/res/layout/people_tile_small.xml
+++ b/packages/SystemUI/res/layout/people_tile_small.xml
@@ -39,7 +39,7 @@
<ImageView
android:id="@+id/predefined_icon"
- android:tint="?android:attr/colorAccent"
+ android:tint="?android:attr/textColorTertiary"
android:layout_gravity="center"
android:layout_width="18dp"
android:layout_height="22dp" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 3543fd1..c16f13e 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -64,10 +64,6 @@
android:clipToPadding="false"
android:clipChildren="false">
- <include
- layout="@layout/keyguard_status_view"
- android:visibility="gone"/>
-
<com.android.systemui.scrim.ScrimView
android:id="@+id/scrim_notifications"
android:layout_width="0dp"
@@ -78,7 +74,11 @@
systemui:layout_constraintEnd_toEndOf="parent"
systemui:layout_constraintTop_toTopOf="parent"
systemui:layout_constraintBottom_toBottomOf="parent"
- />
+ />
+
+ <include
+ layout="@layout/keyguard_status_view"
+ android:visibility="gone"/>
<include layout="@layout/dock_info_overlay" />
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 792d2ec..146f430 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -54,14 +54,16 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.systemui.SystemUI;
+import com.android.systemui.assist.ui.DisplayUtils;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.inject.Inject;
import javax.inject.Provider;
@@ -79,13 +81,14 @@
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final CommandQueue mCommandQueue;
- private final StatusBarStateController mStatusBarStateController;
private final ActivityTaskManager mActivityTaskManager;
@Nullable private final FingerprintManager mFingerprintManager;
@Nullable private final FaceManager mFaceManager;
private final Provider<UdfpsController> mUdfpsControllerFactory;
private final Provider<SidefpsController> mSidefpsControllerFactory;
@Nullable private final PointF mFaceAuthSensorLocation;
+ @Nullable private final PointF mFingerprintLocation;
+ private final Set<Callback> mCallbacks = new HashSet<>();
// TODO: These should just be saved from onSaveState
private SomeArgs mCurrentDialogArgs;
@@ -142,6 +145,10 @@
if (mSidefpsProps != null) {
mSidefpsController = mSidefpsControllerFactory.get();
}
+
+ for (Callback cb : mCallbacks) {
+ cb.onAllAuthenticatorsRegistered();
+ }
}
};
@@ -195,6 +202,20 @@
}
}
+ /**
+ * Adds a callback. See {@link Callback}.
+ */
+ public void addCallback(@NonNull Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ /**
+ * Removes a callback. See {@link Callback}.
+ */
+ public void removeCallback(@NonNull Callback callback) {
+ mCallbacks.remove(callback);
+ }
+
@Override
public void dozeTimeTick() {
if (mUdfpsController != null) {
@@ -335,6 +356,17 @@
}
/**
+ * @return where the fingerprint sensor exists in pixels in portrait mode. devices without an
+ * overridden value will use the default value even if they don't have a fingerprint sensor
+ */
+ @Nullable public PointF getFingerprintSensorLocation() {
+ if (getUdfpsSensorLocation() != null) {
+ return getUdfpsSensorLocation();
+ }
+ return mFingerprintLocation;
+ }
+
+ /**
* @return where the face authentication sensor exists relative to the screen in pixels in
* portrait mode.
*/
@@ -387,7 +419,6 @@
@Inject
public AuthController(Context context, CommandQueue commandQueue,
- StatusBarStateController statusBarStateController,
ActivityTaskManager activityTaskManager,
@Nullable FingerprintManager fingerprintManager,
@Nullable FaceManager faceManager,
@@ -395,7 +426,6 @@
Provider<SidefpsController> sidefpsControllerFactory) {
super(context);
mCommandQueue = commandQueue;
- mStatusBarStateController = statusBarStateController;
mActivityTaskManager = activityTaskManager;
mFingerprintManager = fingerprintManager;
mFaceManager = faceManager;
@@ -414,6 +444,10 @@
(float) faceAuthLocation[1]);
}
+ mFingerprintLocation = new PointF(DisplayUtils.getWidth(mContext) / 2,
+ mContext.getResources().getDimensionPixelSize(
+ com.android.systemui.R.dimen.physical_fingerprint_sensor_center_screen_location_y));
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -711,4 +745,12 @@
.setMultiSensorConfig(multiSensorConfig)
.build(sensorIds, credentialAllowed, mFpProps, mFaceProps);
}
+
+ interface Callback {
+ /**
+ * Called when authenticators are registered. If authenticators are already
+ * registered before this call, this callback will never be triggered.
+ */
+ void onAllAuthenticatorsRegistered();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 257bd25..cf577a3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -28,6 +28,7 @@
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.ViewController
@@ -40,6 +41,7 @@
*/
@StatusBarScope
class AuthRippleController @Inject constructor(
+ private val statusBar: StatusBar,
private val sysuiContext: Context,
private val authController: AuthController,
private val configurationController: ConfigurationController,
@@ -49,13 +51,14 @@
private val bypassController: KeyguardBypassController,
rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView) {
- private var fingerprintSensorLocation: PointF? = null
+ var fingerprintSensorLocation: PointF? = null
private var faceSensorLocation: PointF? = null
@VisibleForTesting
public override fun onViewAttached() {
updateRippleColor()
updateSensorLocation()
+ authController.addCallback(authControllerCallback)
configurationController.addCallback(configurationChangedListener)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
@@ -63,6 +66,7 @@
@VisibleForTesting
public override fun onViewDetached() {
+ authController.removeCallback(authControllerCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
configurationController.removeCallback(configurationChangedListener)
commandRegistry.unregisterCommand("auth-ripple")
@@ -97,9 +101,10 @@
})
}
- private fun updateSensorLocation() {
- fingerprintSensorLocation = authController.udfpsSensorLocation
+ fun updateSensorLocation() {
+ fingerprintSensorLocation = authController.fingerprintSensorLocation
faceSensorLocation = authController.faceAuthSensorLocation
+ statusBar.updateCircleReveal()
}
private fun updateRippleColor() {
@@ -134,6 +139,8 @@
}
}
+ private val authControllerCallback = AuthController.Callback { updateSensorLocation() }
+
inner class AuthRippleCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
if (args.isEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 374ddae..01fbe39 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -23,7 +23,11 @@
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PointF
+import android.media.AudioAttributes
+import android.os.VibrationEffect
+import android.os.Vibrator
import android.util.AttributeSet
+import android.util.MathUtils
import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
@@ -31,15 +35,26 @@
private const val RIPPLE_ANIMATION_DURATION: Long = 1533
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
+private const val RIPPLE_VIBRATION_PRIMITIVE: Int = VibrationEffect.Composition.PRIMITIVE_LOW_TICK
+private const val RIPPLE_VIBRATION_SIZE: Int = 60
+private const val RIPPLE_VIBRATION_SCALE_START: Float = 0.6f
+private const val RIPPLE_VIBRATION_SCALE_DECAY: Float = -0.1f
/**
* Expanding ripple effect on the transition from biometric authentication success to showing
* launcher.
*/
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
+ private val vibrator: Vibrator? = context?.getSystemService(Vibrator::class.java)
private var rippleInProgress: Boolean = false
private val rippleShader = RippleShader()
private val ripplePaint = Paint()
+ private val rippleVibrationEffect = createVibrationEffect(vibrator)
+ private val rippleVibrationAttrs =
+ AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .build()
init {
rippleShader.color = 0xffffffff.toInt() // default color
@@ -95,6 +110,7 @@
visibility = GONE
}
})
+ vibrate()
animatorSet.start()
visibility = VISIBLE
rippleInProgress = true
@@ -108,4 +124,23 @@
// draw over the entire screen
canvas?.drawRect(0f, 0f, width.toFloat(), height.toFloat(), ripplePaint)
}
+
+ private fun vibrate() {
+ if (rippleVibrationEffect != null) {
+ vibrator?.vibrate(rippleVibrationEffect, rippleVibrationAttrs)
+ }
+ }
+
+ private fun createVibrationEffect(vibrator: Vibrator?): VibrationEffect? {
+ if (vibrator?.areAllPrimitivesSupported(RIPPLE_VIBRATION_PRIMITIVE) == false) {
+ return null
+ }
+ val composition = VibrationEffect.startComposition()
+ for (i in 0 until RIPPLE_VIBRATION_SIZE) {
+ val scale =
+ RIPPLE_VIBRATION_SCALE_START * MathUtils.exp(RIPPLE_VIBRATION_SCALE_DECAY * i)
+ composition.addPrimitive(RIPPLE_VIBRATION_PRIMITIVE, scale, 0 /* delay */)
+ }
+ return composition.compose()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 3075617..5c360a6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -496,7 +496,6 @@
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
checkArgument(mSensorProps != null);
- mStatusBar.setSensorRect(getSensorLocation());
mCoreLayoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 98d8866..6a025a7 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -184,10 +184,4 @@
// Refresh tile views to sync new conversations.
buildActivity();
}
-
- @Override
- protected void onPause() {
- super.onPause();
- finish();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
index f3cb359..96aeb60 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
@@ -29,6 +29,8 @@
import android.util.IconDrawableFactory;
import android.util.Log;
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -58,7 +60,7 @@
mIconDrawableFactory = iconDrawableFactory;
mImportantConversationColor = context.getColor(R.color.important_conversation);
mAccentColor = Utils.getColorAttr(context,
- com.android.internal.R.attr.colorAccentPrimary).getDefaultColor();
+ com.android.internal.R.attr.colorAccentPrimaryVariant).getDefaultColor();
mContext = context;
}
@@ -83,7 +85,8 @@
* Returns a {@link Drawable} for the entire conversation. The shortcut icon will be badged
* with the launcher icon of the app specified by packageName.
*/
- public Drawable getPeopleTileDrawable(Drawable headDrawable, String packageName, int userId,
+ public Drawable getPeopleTileDrawable(RoundedBitmapDrawable headDrawable, String packageName,
+ int userId,
boolean important, boolean newStory) {
return new PeopleStoryIconDrawable(headDrawable, getAppBadge(packageName, userId),
mIconBitmapSize, mImportantConversationColor, important, mIconSize, mDensity,
@@ -96,7 +99,7 @@
*/
public static class PeopleStoryIconDrawable extends Drawable {
private float mFullIconSize;
- private Drawable mAvatar;
+ private RoundedBitmapDrawable mAvatar;
private Drawable mBadgeIcon;
private int mIconSize;
private Paint mPriorityRingPaint;
@@ -105,12 +108,13 @@
private Paint mStoryPaint;
private float mDensity;
- PeopleStoryIconDrawable(Drawable avatar,
+ PeopleStoryIconDrawable(RoundedBitmapDrawable avatar,
Drawable badgeIcon,
int iconSize,
@ColorInt int ringColor,
boolean showImportantRing, float fullIconSize, float density,
@ColorInt int accentColor, boolean showStoryRing) {
+ avatar.setCircular(true);
mAvatar = avatar;
mBadgeIcon = badgeIcon;
mIconSize = iconSize;
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 59c7fd1..72d382a 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -45,9 +45,6 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
-import android.icu.text.MeasureFormat;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
@@ -61,6 +58,9 @@
import android.widget.RemoteViews;
import android.widget.TextView;
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.systemui.R;
@@ -72,6 +72,7 @@
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -94,6 +95,8 @@
public static final int LAYOUT_LARGE = 2;
private static final int MIN_CONTENT_MAX_LINES = 2;
+ private static final int NAME_MAX_LINES_WITHOUT_LAST_INTERACTION = 3;
+ private static final int NAME_MAX_LINES_WITH_LAST_INTERACTION = 1;
private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_NOTIF_CONTENT = 16 + 22 + 8 + 16;
private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_STATUS_CONTENT = 16 + 16 + 24 + 4 + 16;
@@ -225,7 +228,9 @@
Log.d(TAG,
"Create status view for: " + statusesForEntireView.get(0).getActivity());
}
- return createStatusRemoteViews(statusesForEntireView.get(0));
+ ConversationStatus mostRecentlyStartedStatus = statusesForEntireView.stream().max(
+ Comparator.comparing(s -> s.getStartTimeMillis())).get();
+ return createStatusRemoteViews(mostRecentlyStartedStatus);
}
return createLastInteractionRemoteViews();
@@ -579,11 +584,33 @@
setMaxLines(views, false);
}
setAvailabilityDotPadding(views, R.dimen.availability_dot_status_padding);
- // TODO: Set status pre-defined icons
- views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
+ views.setImageViewResource(R.id.predefined_icon, getDrawableForStatus(status));
return views;
}
+ private int getDrawableForStatus(ConversationStatus status) {
+ switch (status.getActivity()) {
+ case ACTIVITY_NEW_STORY:
+ return R.drawable.ic_pages;
+ case ACTIVITY_ANNIVERSARY:
+ return R.drawable.ic_celebration;
+ case ACTIVITY_UPCOMING_BIRTHDAY:
+ return R.drawable.ic_gift;
+ case ACTIVITY_BIRTHDAY:
+ return R.drawable.ic_cake;
+ case ACTIVITY_LOCATION:
+ return R.drawable.ic_location;
+ case ACTIVITY_GAME:
+ return R.drawable.ic_play_games;
+ case ACTIVITY_VIDEO:
+ return R.drawable.ic_video;
+ case ACTIVITY_AUDIO:
+ return R.drawable.ic_music_note;
+ default:
+ return R.drawable.ic_person;
+ }
+ }
+
/**
* Update the padding of the availability dot. The padding on the availability dot decreases
* on the status layouts compared to all other layouts.
@@ -658,7 +685,6 @@
}
private RemoteViews decorateBackground(RemoteViews views, CharSequence content) {
- int visibility = View.GONE;
CharSequence emoji = getDoubleEmoji(content);
if (!TextUtils.isEmpty(emoji)) {
setEmojiBackground(views, emoji);
@@ -768,6 +794,7 @@
}
private RemoteViews setViewForContentLayout(RemoteViews views) {
+ views = decorateBackground(views, "");
if (mLayoutSize == LAYOUT_SMALL) {
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
views.setViewVisibility(R.id.name, View.GONE);
@@ -819,6 +846,7 @@
private RemoteViews createLastInteractionRemoteViews() {
RemoteViews views = new RemoteViews(mContext.getPackageName(), getEmptyLayout());
+ views.setInt(R.id.name, "setMaxLines", NAME_MAX_LINES_WITH_LAST_INTERACTION);
if (mLayoutSize == LAYOUT_SMALL) {
views.setViewVisibility(R.id.name, View.VISIBLE);
views.setViewVisibility(R.id.predefined_icon, View.GONE);
@@ -836,6 +864,9 @@
} else {
if (DEBUG) Log.d(TAG, "Hide last interaction");
views.setViewVisibility(R.id.last_interaction, View.GONE);
+ if (mLayoutSize == LAYOUT_MEDIUM) {
+ views.setInt(R.id.name, "setMaxLines", NAME_MAX_LINES_WITHOUT_LAST_INTERACTION);
+ }
}
return views;
}
@@ -891,8 +922,9 @@
context.getPackageManager(),
IconDrawableFactory.newInstance(context, false),
maxAvatarSize);
- Drawable drawable = icon.loadDrawable(context);
- Drawable personDrawable = storyIcon.getPeopleTileDrawable(drawable,
+ RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(
+ context.getResources(), icon.getBitmap());
+ Drawable personDrawable = storyIcon.getPeopleTileDrawable(roundedDrawable,
tile.getPackageName(), getUserId(tile), tile.isImportantConversation(),
hasNewStory);
return convertDrawableToBitmap(personDrawable);
@@ -907,14 +939,11 @@
}
long now = System.currentTimeMillis();
Duration durationSinceLastInteraction = Duration.ofMillis(now - lastInteraction);
- MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
- MeasureFormat.FormatWidth.WIDE);
if (durationSinceLastInteraction.toDays() <= ONE_DAY) {
return null;
} else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK) {
- return context.getString(R.string.days_timestamp, formatter.formatMeasures(
- new Measure(durationSinceLastInteraction.toDays(),
- MeasureUnit.DAY)));
+ return context.getString(R.string.days_timestamp,
+ durationSinceLastInteraction.toDays());
} else if (durationSinceLastInteraction.toDays() == DAYS_IN_A_WEEK) {
return context.getString(R.string.one_week_timestamp);
} else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK * 2) {
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 9e0dd72..2602d7a 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -667,12 +667,16 @@
if (icon != null) {
updatedTile.setUserIcon(icon);
}
+ if (DEBUG) Log.d(TAG, "Statuses: " + conversation.getStatuses().toString());
+ NotificationChannel channel = conversation.getParentNotificationChannel();
+ if (channel != null) {
+ if (DEBUG) Log.d(TAG, "Important:" + channel.isImportantConversation());
+ updatedTile.setIsImportantConversation(channel.isImportantConversation());
+ }
updatedTile
.setContactUri(uri)
.setStatuses(conversation.getStatuses())
- .setLastInteractionTimestamp(conversation.getLastEventTimestamp())
- .setIsImportantConversation(conversation.getParentNotificationChannel() != null
- && conversation.getParentNotificationChannel().isImportantConversation());
+ .setLastInteractionTimestamp(conversation.getLastEventTimestamp());
updateAppWidgetOptionsAndView(appWidgetId, updatedTile.build());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 9b2ed3a..b92f7c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -548,8 +548,11 @@
*/
public void setNotificationsBounds(float left, float top, float right, float bottom) {
if (mClipsQsScrim) {
- // "top - 1" to have 1 px of scrims overlap, see: b/186644628
- mNotificationsScrim.setDrawableBounds(left, top - 1, right, bottom);
+ // notification scrim's rounded corners are anti-aliased, but clipping of the QS scrim
+ // can't be and it's causing jagged corners. That's why notification scrim needs
+ // to overlap QS scrim by one pixel - both vertically (top - 1) and
+ // horizontally (left - 1 and right + 1), see: b/186644628
+ mNotificationsScrim.setDrawableBounds(left - 1, top - 1, right + 1, bottom);
mScrimBehind.setBottomEdgePosition((int) top);
} else {
mNotificationsScrim.setDrawableBounds(left, top, right, bottom);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index eec896a..1364d47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -73,9 +73,9 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.RectF;
import android.media.AudioAttributes;
import android.metrics.LogMaker;
import android.net.Uri;
@@ -150,6 +150,7 @@
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.camera.CameraIntents;
import com.android.systemui.charging.WirelessChargingAnimation;
@@ -326,9 +327,15 @@
public static final int FADE_KEYGUARD_DURATION = 300;
public static final int FADE_KEYGUARD_DURATION_PULSING = 96;
+ public static final long[] CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS =
+ new long[]{20, 20, 20, 20, 100, 20};
+ public static final int[] CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES =
+ new int[]{39, 82, 139, 213, 0, 127};
- /** If true, the system is in the half-boot-to-decryption-screen state.
- * Prudently disable QS and notifications. */
+ /**
+ * If true, the system is in the half-boot-to-decryption-screen state.
+ * Prudently disable QS and notifications.
+ */
public static final boolean ONLY_CORE_APPS;
/** If true, the lockscreen will show a distinct wallpaper */
@@ -380,6 +387,7 @@
protected NotificationShadeWindowView mNotificationShadeWindowView;
protected StatusBarWindowView mPhoneStatusBarWindow;
protected PhoneStatusBarView mStatusBarView;
+ private AuthRippleController mAuthRippleController;
private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
protected NotificationShadeWindowController mNotificationShadeWindowController;
protected StatusBarWindowController mStatusBarWindowController;
@@ -600,7 +608,7 @@
private int mLastCameraLaunchSource;
protected PowerManager.WakeLock mGestureWakeLock;
private Vibrator mVibrator;
- private long[] mCameraLaunchGestureVibePattern;
+ private VibrationEffect mCameraLaunchGestureVibrationEffect;
private final int[] mTmpInt2 = new int[2];
@@ -1311,12 +1319,8 @@
mGestureWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"GestureWakeLock");
mVibrator = mContext.getSystemService(Vibrator.class);
- int[] pattern = mContext.getResources().getIntArray(
- R.array.config_cameraLaunchGestureVibePattern);
- mCameraLaunchGestureVibePattern = new long[pattern.length];
- for (int i = 0; i < pattern.length; i++) {
- mCameraLaunchGestureVibePattern[i] = pattern[i];
- }
+ mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
+ mVibrator, context.getResources());
// receive broadcasts
registerBroadcastReceiver();
@@ -1556,7 +1560,9 @@
mPhoneStatusBarWindow = mSuperStatusBarViewFactory.getStatusBarWindowView();
mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
statusBarComponent.getLockIconViewController().init();
- statusBarComponent.getAuthRippleController().init();
+
+ mAuthRippleController = statusBarComponent.getAuthRippleController();
+ mAuthRippleController.init();
}
protected void startKeyguard() {
@@ -2801,7 +2807,7 @@
int[] result = new int[]{ActivityManager.START_CANCELED};
mActivityLaunchAnimator.startIntentWithAnimation(animController,
- areLaunchAnimationsEnabled(), (adapter) -> {
+ areLaunchAnimationsEnabled(), intent.getPackage(), (adapter) -> {
ActivityOptions options = new ActivityOptions(
getActivityOptions(mDisplayId, adapter));
options.setDisallowEnterPictureInPictureWhileLaunching(
@@ -3791,7 +3797,8 @@
@Override
public void onDozeAmountChanged(float linear, float eased) {
- if (mFeatureFlags.useNewLockscreenAnimations()) {
+ if (mFeatureFlags.useNewLockscreenAnimations()
+ && !mCircleRevealAnimator.isRunning()) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
}
@@ -3828,6 +3835,23 @@
Trace.endSection();
}
+ /**
+ * Update the parameters for the dozing circle reveal that animates when the user authenticates
+ * from AOD using the fingerprint sensor.
+ */
+ public void updateCircleReveal() {
+ final PointF fpLocation = mAuthRippleController.getFingerprintSensorLocation();
+ if (fpLocation != null) {
+ mCircleReveal =
+ new CircleReveal(
+ fpLocation.x,
+ fpLocation.y,
+ 0,
+ Math.max(Math.max(fpLocation.x, getDisplayWidth() - fpLocation.x),
+ Math.max(fpLocation.y, getDisplayHeight() - fpLocation.y)));
+ }
+ }
+
private void startCircleReveal() {
mLightRevealScrim.setRevealEffect(mCircleReveal);
mCircleRevealAnimator.cancel();
@@ -3840,7 +3864,6 @@
private boolean shouldShowCircleReveal() {
return mCircleReveal != null && !mCircleRevealAnimator.isRunning()
- && mKeyguardUpdateMonitor.isUdfpsEnrolled()
&& mBiometricUnlockController.getBiometricType() == FINGERPRINT;
}
@@ -4062,12 +4085,37 @@
private void vibrateForCameraGesture() {
// Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
- mVibrator.vibrate(mCameraLaunchGestureVibePattern, -1 /* repeat */);
+ mVibrator.vibrate(mCameraLaunchGestureVibrationEffect);
+ }
+
+ private static VibrationEffect getCameraGestureVibrationEffect(Vibrator vibrator,
+ Resources resources) {
+ if (vibrator.areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+ VibrationEffect.Composition.PRIMITIVE_CLICK)) {
+ return VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 50)
+ .compose();
+ }
+ if (vibrator.hasAmplitudeControl()) {
+ return VibrationEffect.createWaveform(
+ CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
+ CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
+ /* repeat= */ -1);
+ }
+
+ int[] pattern = resources.getIntArray(R.array.config_cameraLaunchGestureVibePattern);
+ long[] timings = new long[pattern.length];
+ for (int i = 0; i < pattern.length; i++) {
+ timings[i] = pattern[i];
+ }
+ return VibrationEffect.createWaveform(timings, /* repeat= */ -1);
}
/**
* @return true if the screen is currently fully off, i.e. has finished turning off and has
- * since not started turning on.
+ * since not started turning on.
*/
public boolean isScreenFullyOff() {
return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF;
@@ -4278,15 +4326,6 @@
updateScrimController();
}
- /**
- * Set the location of the sensor on UDFPS if existent.
- */
- public void setSensorRect(RectF rect) {
- final float startRadius = (rect.right - rect.left) / 2f;
- mCircleReveal = new CircleReveal(rect.centerX(), rect.centerY(),
- startRadius, rect.centerY() - startRadius);
- }
-
@VisibleForTesting
public void updateScrimController() {
Trace.beginSection("StatusBar#updateScrimController");
@@ -4565,7 +4604,7 @@
animationController, this, intent.isActivity()) : null;
mActivityLaunchAnimator.startPendingIntentWithAnimation(
- controller, areLaunchAnimationsEnabled(),
+ controller, areLaunchAnimationsEnabled(), intent.getCreatorPackage(),
(animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null,
null, getActivityOptions(mDisplayId, animationAdapter)));
} catch (PendingIntent.CanceledException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index ab58aae6..5cd4e13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -437,7 +437,7 @@
mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
!wasOccluded && mStatusBar.areLaunchAnimationsEnabled(),
- (adapter) -> {
+ intent.getCreatorPackage(), (adapter) -> {
long eventTime = row.getAndResetLastActionUpTime();
Bundle options = eventTime > 0
? getActivityOptions(
@@ -469,6 +469,7 @@
mActivityLaunchAnimator.startIntentWithAnimation(
animationController, mStatusBar.areLaunchAnimationsEnabled(),
+ intent.getPackage(),
(adapter) -> TaskStackBuilder.create(mContext)
.addNextIntentWithParentStack(intent)
.startActivities(getActivityOptions(
@@ -499,7 +500,7 @@
true /* isActivityIntent */);
mActivityLaunchAnimator.startIntentWithAnimation(animationController,
- mStatusBar.areLaunchAnimationsEnabled(),
+ mStatusBar.areLaunchAnimationsEnabled(), intent.getPackage(),
(adapter) -> tsb.startActivities(
getActivityOptions(mStatusBar.getDisplayId(), adapter),
UserHandle.CURRENT));
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
index 603d690..8187956 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
@@ -76,29 +76,41 @@
}
val linearInterp = LinearInterpolator()
val scaleInterp = PathInterpolator(0.3f, 0f, 1f, 1f)
- val sX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.9f).apply {
+ val viewScaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.9f).apply {
interpolator = scaleInterp
duration = 250
}
- val sY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.9f).apply {
+ val viewScaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.9f).apply {
interpolator = scaleInterp
duration = 250
}
- val vA = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f).apply {
+ val viewElevation = ObjectAnimator.ofFloat(view, "elevation",
+ view.elevation, 0f).apply {
+ interpolator = linearInterp
+ duration = 40
+ startDelay = 150
+ }
+ val viewAlpha = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f).apply {
interpolator = linearInterp
duration = 100
startDelay = 150
}
- val tA = ObjectAnimator.ofFloat(text, "alpha", 1f, 0f).apply {
+ val textAlpha = ObjectAnimator.ofFloat(text, "alpha", 1f, 0f).apply {
interpolator = linearInterp
duration = 166
}
- val iA = ObjectAnimator.ofFloat(icon, "alpha", 1f, 0f).apply {
+ val iconAlpha = ObjectAnimator.ofFloat(icon, "alpha", 1f, 0f).apply {
interpolator = linearInterp
duration = 166
}
return AnimatorSet().apply {
- playTogether(sX, sY, vA, tA, iA)
+ playTogether(
+ viewScaleX,
+ viewScaleY,
+ viewElevation,
+ viewAlpha,
+ textAlpha,
+ iconAlpha)
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 897d78b..f2ef5c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -51,7 +51,11 @@
// We start in a new thread so that we can ensure that the callbacks are called in the main
// thread.
thread {
- activityLaunchAnimator.startIntentWithAnimation(controller, animate, intentStarter)
+ activityLaunchAnimator.startIntentWithAnimation(
+ controller = controller,
+ animate = animate,
+ intentStarter = intentStarter
+ )
}.join()
}
@@ -135,11 +139,14 @@
verify(controller).onLaunchAnimationStart(anyBoolean())
}
- private fun fakeWindow() = RemoteAnimationTarget(
- 0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
- Point(), Rect(), Rect(), WindowConfiguration(), false, SurfaceControl(), Rect(),
- ActivityManager.RunningTaskInfo()
- )
+ private fun fakeWindow(): RemoteAnimationTarget {
+ val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
+ return RemoteAnimationTarget(
+ 0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
+ Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(),
+ ActivityManager.RunningTaskInfo()
+ )
+ }
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index 711f293..06f9253 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.PromptInfo;
import android.os.Bundle;
import android.test.suitebuilder.annotation.SmallTest;
@@ -41,7 +42,6 @@
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -195,8 +195,6 @@
assertEquals(AuthBiometricView.STATE_AUTHENTICATING, mBiometricView.mState);
}
- // TODO: b(/189031816)
- @Ignore
@Test
public void testError_sendsActionError() {
initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
@@ -205,7 +203,7 @@
waitForIdleSync();
verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_ERROR);
- assertEquals(AuthBiometricView.STATE_ERROR, mBiometricView.mState);
+ assertEquals(AuthBiometricView.STATE_IDLE, mBiometricView.mState);
}
@Test
@@ -241,8 +239,6 @@
verify(mCallback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED));
}
- // TODO: b(/189031816)
- @Ignore
@Test
public void testRestoresState() {
final boolean requireConfirmation = true; // set/init from AuthController
@@ -258,6 +254,12 @@
public TextView getIndicatorView() {
return indicatorView;
}
+
+ @Override
+ public int getDelayAfterError() {
+ // keep a real delay to test saving in the error state
+ return BiometricPrompt.HIDE_DIALOG_DELAY;
+ }
});
final String failureMessage = "testFailureMessage";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index bb2e55f..db5648a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -62,7 +62,6 @@
import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import org.junit.Before;
@@ -96,8 +95,6 @@
@Mock
private CommandQueue mCommandQueue;
@Mock
- private StatusBarStateController mStatusBarStateController;
- @Mock
private ActivityTaskManager mActivityTaskManager;
@Mock
private FingerprintManager mFingerprintManager;
@@ -152,7 +149,7 @@
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props);
mAuthController = new TestableAuthController(context, mCommandQueue,
- mStatusBarStateController, mActivityTaskManager, mFingerprintManager, mFaceManager,
+ mActivityTaskManager, mFingerprintManager, mFaceManager,
() -> mUdfpsController, () -> mSidefpsController);
mAuthController.start();
@@ -561,13 +558,12 @@
private PromptInfo mLastBiometricPromptInfo;
TestableAuthController(Context context, CommandQueue commandQueue,
- StatusBarStateController statusBarStateController,
ActivityTaskManager activityTaskManager,
FingerprintManager fingerprintManager,
FaceManager faceManager,
Provider<UdfpsController> udfpsControllerFactory,
Provider<SidefpsController> sidefpsControllerFactory) {
- super(context, commandQueue, statusBarStateController, activityTaskManager,
+ super(context, commandQueue, activityTaskManager,
fingerprintManager, faceManager, udfpsControllerFactory,
sidefpsControllerFactory);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 247748a..240fdf3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController
import org.junit.Before
import org.junit.Test
@@ -44,6 +45,7 @@
@RunWith(AndroidTestingRunner::class)
class AuthRippleControllerTest : SysuiTestCase() {
private lateinit var controller: AuthRippleController
+ @Mock private lateinit var statusBar: StatusBar
@Mock private lateinit var rippleView: AuthRippleView
@Mock private lateinit var commandRegistry: CommandRegistry
@Mock private lateinit var configurationController: ConfigurationController
@@ -56,6 +58,7 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
controller = AuthRippleController(
+ statusBar,
context,
authController,
configurationController,
@@ -72,7 +75,7 @@
fun testFingerprintTrigger_Ripple() {
// GIVEN fp exists, keyguard is visible, user doesn't need strong auth
val fpsLocation = PointF(5f, 5f)
- `when`(authController.udfpsSensorLocation).thenReturn(fpsLocation)
+ `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
controller.onViewAttached()
`when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(true)
`when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
@@ -193,7 +196,7 @@
@Test
fun testNullFingerprintSensorLocationDoesNothing() {
- `when`(authController.udfpsSensorLocation).thenReturn(null)
+ `when`(authController.fingerprintSensorLocation).thenReturn(null)
controller.onViewAttached()
val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 3b56f22..67505c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -69,6 +69,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.time.Duration;
import java.util.Arrays;
@RunWith(AndroidTestingRunner.class)
@@ -185,6 +186,30 @@
}
@Test
+ public void testLastInteractionTime() {
+ long now = System.currentTimeMillis();
+ long fiveDaysAgo = now - Duration.ofDays(5).toMillis();
+ String lastInteractionString = PeopleTileViewHelper.getLastInteractionString(mContext,
+ fiveDaysAgo);
+ assertThat(lastInteractionString).isEqualTo("5 days ago");
+
+ long lessThanOneDayAgo = now - Duration.ofHours(20).toMillis();
+ lastInteractionString = PeopleTileViewHelper.getLastInteractionString(mContext,
+ lessThanOneDayAgo);
+ assertThat(lastInteractionString).isNull();
+
+ long overOneWeekAgo = now - Duration.ofDays(8).toMillis();
+ lastInteractionString = PeopleTileViewHelper.getLastInteractionString(mContext,
+ overOneWeekAgo);
+ assertThat(lastInteractionString).isEqualTo("Over 1 week ago");
+
+ long overTwoWeeksAgo = now - Duration.ofDays(15).toMillis();
+ lastInteractionString = PeopleTileViewHelper.getLastInteractionString(mContext,
+ overTwoWeeksAgo);
+ assertThat(lastInteractionString).isEqualTo("Over 2 weeks ago");
+ }
+
+ @Test
public void testCreateRemoteViewsWithLastInteractionTime() {
PeopleSpaceTile tileWithLastInteraction =
PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setLastInteractionTimestamp(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index be86af5..407afbe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -269,7 +269,7 @@
verify(mShadeController, atLeastOnce()).collapsePanel();
verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(any(),
- eq(false) /* animate */, any());
+ eq(false) /* animate */, any(), any());
verify(mAssistManager).hideAssist();
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 6e7771d..e336b6b 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -39,11 +39,9 @@
import libcore.io.IoUtils;
import java.io.DataInputStream;
-import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
@@ -377,11 +375,16 @@
try {
FileChannel channel = getBlockOutputChannel();
- ByteBuffer buf = ByteBuffer.allocate(DIGEST_SIZE_BYTES + HEADER_SIZE);
+ int header_size = DIGEST_SIZE_BYTES + HEADER_SIZE;
+ ByteBuffer buf = ByteBuffer.allocate(header_size);
buf.put(new byte[DIGEST_SIZE_BYTES]);
buf.putInt(PARTITION_TYPE_MARKER);
buf.putInt(0);
channel.write(buf);
+ // corrupt the payload explicitly
+ int payload_size = (int) getBlockDeviceSize() - header_size;
+ buf = ByteBuffer.allocate(payload_size);
+ channel.write(buf);
channel.force(true);
} catch (IOException e) {
Slog.e(TAG, "failed to format block", e);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 85e8315..6cb374a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -139,7 +139,7 @@
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
.replaceWith("?");
- private static final int MAX_LOW_POWER_STATS_SIZE = 8192;
+ private static final int MAX_LOW_POWER_STATS_SIZE = 16384;
private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
private static final String EMPTY = "Empty";
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index f32423f..b325ea3 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -159,6 +159,10 @@
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.MAGNIFIER_ASPECT_RATIO,
WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, float.class,
WidgetFlags.MAGNIFIER_ASPECT_RATIO_DEFAULT));
+ sDeviceConfigEntries.add(new DeviceConfigEntry<>(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ANALOG_CLOCK_SECONDS_HAND_FPS,
+ WidgetFlags.KEY_ANALOG_CLOCK_SECONDS_HAND_FPS, int.class,
+ WidgetFlags.ANALOG_CLOCK_SECONDS_HAND_FPS_DEFAULT));
// add other device configs here...
}
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index d29a0c7..0f97b90 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -26,9 +26,7 @@
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
-import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeInfo;
@@ -161,15 +159,17 @@
*
* @param packageName Package name to tentatively enable the change for.
* @param override The package override to be set
+ * @param allowedState Whether the override is allowed.
+ * @param versionCode The version code of the package.
*/
void addPackageOverride(String packageName, PackageOverride override,
- OverrideAllowedState allowedState, Context context) {
+ OverrideAllowedState allowedState, @Nullable Long versionCode) {
if (getLoggingOnly()) {
throw new IllegalArgumentException(
"Can't add overrides for a logging only change " + toString());
}
mRawOverrides.put(packageName, override);
- recheckOverride(packageName, allowedState, context);
+ recheckOverride(packageName, allowedState, versionCode);
}
/**
@@ -179,32 +179,24 @@
* overrides, check if they need to be demoted to deferred.</p>
*
* @param packageName Package name to apply deferred overrides for.
- * @param allowed Whether the override is allowed.
+ * @param allowedState Whether the override is allowed.
+ * @param versionCode The version code of the package.
*
* @return {@code true} if the recheck yielded a result that requires invalidating caches
* (a deferred override was consolidated or a regular override was removed).
*/
boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
- Context context) {
+ @Nullable Long versionCode) {
boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED);
- Long version = null;
- try {
- ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(
- packageName, 0);
- version = applicationInfo.longVersionCode;
- } catch (PackageManager.NameNotFoundException e) {
- // Do nothing
- }
-
// If the app is not installed or no longer has raw overrides, evaluate to false
- if (version == null || !hasRawOverride(packageName) || !allowed) {
+ if (versionCode == null || !hasRawOverride(packageName) || !allowed) {
removePackageOverrideInternal(packageName);
return false;
}
// Evaluate the override based on its version
- int overrideValue = mRawOverrides.get(packageName).evaluate(version);
+ int overrideValue = mRawOverrides.get(packageName).evaluate(versionCode);
switch (overrideValue) {
case VALUE_UNDEFINED:
removePackageOverrideInternal(packageName);
@@ -229,11 +221,13 @@
* <p>Note, this method is not thread safe so callers must ensure thread safety.
*
* @param pname Package name to reset to defaults for.
+ * @param allowedState Whether the override is allowed.
+ * @param versionCode The version code of the package.
*/
boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
- Context context) {
+ @Nullable Long versionCode) {
if (mRawOverrides.remove(pname) != null) {
- recheckOverride(pname, allowedState, context);
+ recheckOverride(pname, allowedState, versionCode);
return true;
}
return false;
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 9247568..909ed11 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -16,11 +16,13 @@
package com.android.server.compat;
+import android.annotation.Nullable;
import android.app.compat.ChangeIdStateCache;
import android.app.compat.PackageOverride;
import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Environment;
import android.text.TextUtils;
import android.util.LongArray;
@@ -51,7 +53,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -249,6 +250,7 @@
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
allowedState.enforce(changeId, packageName);
+ Long versionCode = getVersionCodeOrNull(packageName);
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c == null) {
@@ -256,7 +258,7 @@
c = new CompatChange(changeId);
addChange(c);
}
- c.addPackageOverride(packageName, overrides, allowedState, mContext);
+ c.addPackageOverride(packageName, overrides, allowedState, versionCode);
invalidateCache();
}
return alreadyKnown;
@@ -336,6 +338,7 @@
* It does not invalidate the cache nor save the overrides.
*/
private boolean removeOverrideUnsafe(long changeId, String packageName) {
+ Long versionCode = getVersionCodeOrNull(packageName);
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
if (c != null) {
@@ -343,7 +346,7 @@
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
if (c.hasPackageOverride(packageName)) {
allowedState.enforce(changeId, packageName);
- c.removePackageOverride(packageName, allowedState, mContext);
+ c.removePackageOverride(packageName, allowedState, versionCode);
invalidateCache();
return true;
}
@@ -653,26 +656,33 @@
* Rechecks all the existing overrides for a package.
*/
void recheckOverrides(String packageName) {
- // Local cache of compat changes. Holding a lock on mChanges for the whole duration of the
- // method will cause a deadlock.
- List<CompatChange> changes;
+ Long versionCode = getVersionCodeOrNull(packageName);
synchronized (mChanges) {
- changes = new ArrayList<>(mChanges.size());
+ boolean shouldInvalidateCache = false;
for (int idx = 0; idx < mChanges.size(); ++idx) {
- changes.add(mChanges.valueAt(idx));
+ CompatChange c = mChanges.valueAt(idx);
+ if (!c.hasPackageOverride(packageName)) {
+ continue;
+ }
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(),
+ packageName);
+ shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, versionCode);
+ }
+ if (shouldInvalidateCache) {
+ invalidateCache();
}
}
- boolean shouldInvalidateCache = false;
- for (CompatChange c: changes) {
- if (!c.hasPackageOverride(packageName)) {
- continue;
- }
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(), packageName);
- shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext);
- }
- if (shouldInvalidateCache) {
- invalidateCache();
+ }
+
+ @Nullable
+ private Long getVersionCodeOrNull(String packageName) {
+ try {
+ ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(
+ packageName, 0);
+ return applicationInfo.longVersionCode;
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 065dc6e..38966b9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -90,7 +89,6 @@
import static android.content.pm.ActivityInfo.SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
@@ -279,7 +277,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.permission.PermissionManager;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
@@ -7457,11 +7454,9 @@
@Override
public boolean providesMaxBounds() {
- // System and SystemUI should always be able to access the physical display bounds,
- // so do not provide it with the overridden maximum bounds.
- // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
- if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
- getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
+ // System should always be able to access the DisplayArea bounds, so do not provide it with
+ // compat max window bounds.
+ if (getUid() == SYSTEM_UID) {
return false;
}
// Do not sandbox to activity window bounds if the feature is disabled.
diff --git a/services/core/java/com/android/server/wm/BlurController.java b/services/core/java/com/android/server/wm/BlurController.java
index ff10168..0363944 100644
--- a/services/core/java/com/android/server/wm/BlurController.java
+++ b/services/core/java/com/android/server/wm/BlurController.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.provider.Settings;
import android.view.ICrossWindowBlurEnabledListener;
+import android.view.TunnelModeEnabledListener;
/**
* Keeps track of the different factors that determine whether cross-window blur is enabled
@@ -45,6 +46,16 @@
private volatile boolean mBlurEnabled;
private boolean mInPowerSaveMode;
private boolean mBlurDisabledSetting;
+ private boolean mTunnelModeEnabled = false;
+
+ private TunnelModeEnabledListener mTunnelModeListener =
+ new TunnelModeEnabledListener(Runnable::run) {
+ @Override
+ public void onTunnelModeEnabledChanged(boolean tunnelModeEnabled) {
+ mTunnelModeEnabled = tunnelModeEnabled;
+ updateBlurEnabled();
+ }
+ };
BlurController(Context context, PowerManager powerManager) {
mContext = context;
@@ -78,6 +89,8 @@
});
mBlurDisabledSetting = getBlurDisabledSetting();
+ TunnelModeEnabledListener.register(mTunnelModeListener);
+
updateBlurEnabled();
}
@@ -99,7 +112,7 @@
private void updateBlurEnabled() {
synchronized (mLock) {
final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurDisabledSetting
- && !mInPowerSaveMode;
+ && !mInPowerSaveMode && !mTunnelModeEnabled;
if (mBlurEnabled == newEnabled) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0251537..7df5744 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8105,11 +8105,10 @@
// This could prevent if there is no container animation, we still have to apply the
// pending transaction and exit waiting.
mAnimator.mNotifyWhenNoAnimation = true;
- WindowContainer animatingContainer = null;
- while (mAnimator.isAnimationScheduled() || timeoutRemaining > 0) {
- animatingContainer = mRoot.getAnimatingContainer(TRANSITION | CHILDREN,
- ANIMATION_TYPE_ALL);
- if (animatingContainer == null) {
+ while (timeoutRemaining > 0) {
+ boolean isAnimating = mAnimator.isAnimationScheduled()
+ || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL);
+ if (!isAnimating) {
break;
}
long startTime = System.currentTimeMillis();
@@ -8121,6 +8120,9 @@
}
mAnimator.mNotifyWhenNoAnimation = false;
+ WindowContainer animatingContainer;
+ animatingContainer = mRoot.getAnimatingContainer(TRANSITION | CHILDREN,
+ ANIMATION_TYPE_ALL);
if (mAnimator.isAnimationScheduled() || animatingContainer != null) {
Slog.w(TAG, "Timed out waiting for animations to complete,"
+ " animatingContainer=" + animatingContainer
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 8d43bbb..ce8f6df 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -1190,7 +1190,7 @@
pw.println(mEnrollmentSpecificId);
}
- pw.print("mAdminCanGrantSensorsPermissions");
+ pw.print("mAdminCanGrantSensorsPermissions=");
pw.println(mAdminCanGrantSensorsPermissions);
pw.print("mUsbDataSignaling=");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 891ed3d..bc130e2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8579,6 +8579,7 @@
final DevicePolicyData policyData = getUserData(userId);
policyData.mCurrentInputMethodSet = false;
saveSettingsLocked(userId);
+ mPolicyCache.onUserRemoved(userId);
final DevicePolicyData systemPolicyData = getUserData(UserHandle.USER_SYSTEM);
systemPolicyData.mLastSecurityLogRetrievalTime = -1;
systemPolicyData.mLastBugReportRequestTime = -1;
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 47505a3..e31be82 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -43,6 +43,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.EventLog;
import android.util.Log;
import com.android.internal.content.PackageMonitor;
@@ -736,13 +737,19 @@
@Override
public MidiDeviceInfo getServiceDeviceInfo(String packageName, String className) {
+ int uid = Binder.getCallingUid();
synchronized (mDevicesByInfo) {
for (Device device : mDevicesByInfo.values()) {
ServiceInfo serviceInfo = device.getServiceInfo();
if (serviceInfo != null &&
packageName.equals(serviceInfo.packageName) &&
className.equals(serviceInfo.name)) {
- return device.getDeviceInfo();
+ if (device.isUidAllowed(uid)) {
+ return device.getDeviceInfo();
+ } else {
+ EventLog.writeEvent(0x534e4554, "185796676", -1, "");
+ return null;
+ }
}
}
return null;
diff --git a/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
index c631026..49d5e50 100644
--- a/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
+++ b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java
@@ -20,26 +20,18 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.PendingIntent;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
import android.app.people.ConversationStatus;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
+import android.os.Binder;
import android.os.CancellationSignal;
-import android.os.SystemClock;
import com.android.server.LocalServices;
-import com.android.server.notification.NotificationRecord;
import com.android.server.people.PeopleServiceInternal;
-import java.util.concurrent.TimeUnit;
-
/**
* If a {@link ConversationStatus} is added to the system with an expiration time, remove that
* status at that time
@@ -53,18 +45,22 @@
void scheduleExpiration(Context context, @UserIdInt int userId, String pkg,
String conversationId, ConversationStatus status) {
-
- final PendingIntent pi = PendingIntent.getBroadcast(context,
- REQUEST_CODE,
- new Intent(ACTION)
- .setData(new Uri.Builder().scheme(SCHEME)
- .appendPath(getKey(userId, pkg, conversationId, status))
- .build())
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_USER_ID, userId),
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- context.getSystemService(AlarmManager.class).setExactAndAllowWhileIdle(
- AlarmManager.RTC_WAKEUP, status.getEndTimeMillis(), pi);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final PendingIntent pi = PendingIntent.getBroadcast(context,
+ REQUEST_CODE,
+ new Intent(ACTION)
+ .setData(new Uri.Builder().scheme(SCHEME)
+ .appendPath(getKey(userId, pkg, conversationId, status))
+ .build())
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .putExtra(EXTRA_USER_ID, userId),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ context.getSystemService(AlarmManager.class).setExactAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP, status.getEndTimeMillis(), pi);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
private static String getKey(@UserIdInt int userId, String pkg,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 9be1ac4..2206b0a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -22,6 +22,7 @@
import static android.app.ActivityManager.START_VOICE_NOT_ACTIVE_SESSION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -36,7 +37,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
@@ -414,11 +414,31 @@
Slog.w(TAG, "Hotword detection service name not found");
throw new IllegalStateException("Hotword detection service name not found");
}
- if (!isIsolatedProcessLocked(mHotwordDetectionComponentName)) {
+ ServiceInfo hotwordDetectionServiceInfo = getServiceInfoLocked(
+ mHotwordDetectionComponentName, mUser);
+ if (hotwordDetectionServiceInfo == null) {
+ Slog.w(TAG, "Hotword detection service info not found");
+ throw new IllegalStateException("Hotword detection service info not found");
+ }
+ if (!isIsolatedProcessLocked(hotwordDetectionServiceInfo)) {
Slog.w(TAG, "Hotword detection service not in isolated process");
throw new IllegalStateException("Hotword detection service not in isolated process");
}
- // TODO : Need to check related permissions for hotword detection service
+ if (!Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE.equals(
+ hotwordDetectionServiceInfo.permission)) {
+ Slog.w(TAG, "Hotword detection service does not require permission "
+ + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
+ throw new SecurityException("Hotword detection service does not require permission "
+ + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
+ }
+ if (mContext.getPackageManager().checkPermission(
+ Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE,
+ mInfo.getServiceInfo().packageName) == PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Voice interaction service should not hold permission "
+ + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
+ throw new SecurityException("Voice interaction service should not hold permission "
+ + Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE);
+ }
if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) {
Slog.w(TAG, "Can't set sharedMemory to be read-only");
@@ -522,23 +542,24 @@
mHotwordDetectionConnection);
}
- boolean isIsolatedProcessLocked(ComponentName componentName) {
- IPackageManager pm = AppGlobals.getPackageManager();
+ private static ServiceInfo getServiceInfoLocked(@NonNull ComponentName componentName,
+ int userHandle) {
try {
- ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
+ return AppGlobals.getPackageManager().getServiceInfo(componentName,
PackageManager.GET_META_DATA
| PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, mUser);
- if (serviceInfo != null) {
- return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
- && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
- }
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
} catch (RemoteException e) {
if (DEBUG) {
- Slog.w(TAG, "isIsolatedProcess RemoteException : " + e);
+ Slog.w(TAG, "getServiceInfoLocked RemoteException : " + e);
}
}
- return false;
+ return null;
+ }
+
+ boolean isIsolatedProcessLocked(@NonNull ServiceInfo serviceInfo) {
+ return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
+ && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
}
public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {