Merge "Add a flag to disable optional dimensions."
diff --git a/api/current.txt b/api/current.txt
index b793470b..0002a0c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -55869,11 +55869,16 @@
method public boolean executeKeyEvent(android.view.KeyEvent);
method public void fling(int);
method public boolean fullScroll(int);
+ method @ColorInt public int getLeftEdgeEffectColor();
method public int getMaxScrollAmount();
+ method @ColorInt public int getRightEdgeEffectColor();
method public boolean isFillViewport();
method public boolean isSmoothScrollingEnabled();
method public boolean pageScroll(int);
+ method public void setEdgeEffectColor(@ColorInt int);
method public void setFillViewport(boolean);
+ method public void setLeftEdgeEffectColor(@ColorInt int);
+ method public void setRightEdgeEffectColor(@ColorInt int);
method public void setSmoothScrollingEnabled(boolean);
method public final void smoothScrollBy(int, int);
method public final void smoothScrollTo(int, int);
@@ -56672,13 +56677,18 @@
method public boolean executeKeyEvent(android.view.KeyEvent);
method public void fling(int);
method public boolean fullScroll(int);
+ method @ColorInt public int getBottomEdgeEffectColor();
method public int getMaxScrollAmount();
+ method @ColorInt public int getTopEdgeEffectColor();
method public boolean isFillViewport();
method public boolean isSmoothScrollingEnabled();
method public boolean pageScroll(int);
method public void scrollToDescendant(android.view.View);
+ method public void setBottomEdgeEffectColor(@ColorInt int);
+ method public void setEdgeEffectColor(@ColorInt int);
method public void setFillViewport(boolean);
method public void setSmoothScrollingEnabled(boolean);
+ method public void setTopEdgeEffectColor(@ColorInt int);
method public final void smoothScrollBy(int, int);
method public final void smoothScrollTo(int, int);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index c9b8c38..9c4a482 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -205,6 +205,7 @@
}
public static final class R.attr {
+ field public static final int allowClearUserDataOnFailedRestore = 16844198; // 0x10105a6
field public static final int inheritShowWhenLocked = 16844194; // 0x10105a2
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -1150,7 +1151,7 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
method public int getUsageSource();
- method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
+ method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
method public void reportUsageStart(@NonNull android.app.Activity, @NonNull String);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 7ddb783..458e0f5 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -297,6 +297,7 @@
NumBiometricsEnrolled num_faces_enrolled = 10048;
RoleHolder role_holder = 10049;
DangerousPermissionState dangerous_permission_state = 10050;
+ TrainInfo train_info = 10051;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -5503,3 +5504,20 @@
// True if the package is privileged.
optional bool is_priv_app = 4;
}
+
+/**
+ * Potential experiment ids that goes with a train install.
+ */
+message TrainExperimentIds {
+ repeated int64 experiment_id = 1;
+}
+
+/**
+ * Pulls the ongoing mainline install train version code.
+ * Pulled from StatsCompanionService
+ */
+message TrainInfo {
+ optional int64 train_version_code = 1;
+
+ optional TrainExperimentIds train_experiment_id = 2;
+}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 2999b64..ea3f3b3 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -511,6 +511,7 @@
FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
+ FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
};
} // namespace statsd
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 6069516..92aa998 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -278,6 +278,39 @@
EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos());
}
+TEST(StatsdStatsTest, TestAtomMetricsStats) {
+ StatsdStats stats;
+ time_t now = time(nullptr);
+ // old event, we get it from the stats buffer. should be ignored.
+ stats.noteBucketDropped(1000L);
+
+ stats.noteBucketBoundaryDelayNs(1000L, -1L);
+ stats.noteBucketBoundaryDelayNs(1000L, -10L);
+ stats.noteBucketBoundaryDelayNs(1000L, 2L);
+
+ stats.noteBucketBoundaryDelayNs(1001L, 1L);
+
+ vector<uint8_t> output;
+ stats.dumpStats(&output, false);
+ StatsdStatsReport report;
+ bool good = report.ParseFromArray(&output[0], output.size());
+ EXPECT_TRUE(good);
+
+ EXPECT_EQ(2, report.atom_metric_stats().size());
+
+ auto atomStats = report.atom_metric_stats(0);
+ EXPECT_EQ(1000L, atomStats.metric_id());
+ EXPECT_EQ(1L, atomStats.bucket_dropped());
+ EXPECT_EQ(-10L, atomStats.min_bucket_boundary_delay_ns());
+ EXPECT_EQ(2L, atomStats.max_bucket_boundary_delay_ns());
+
+ auto atomStats2 = report.atom_metric_stats(1);
+ EXPECT_EQ(1001L, atomStats2.metric_id());
+ EXPECT_EQ(0L, atomStats2.bucket_dropped());
+ EXPECT_EQ(0L, atomStats2.min_bucket_boundary_delay_ns());
+ EXPECT_EQ(1L, atomStats2.max_bucket_boundary_delay_ns());
+}
+
TEST(StatsdStatsTest, TestAnomalyMonitor) {
StatsdStats stats;
stats.noteRegisteredAnomalyAlarmChanged();
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 51397a2..dc5bdc6 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -745,7 +745,11 @@
* Registering an {@code observerId} that was already registered will override the previous one.
* No more than 1000 unique {@code observerId} may be registered by a single uid
* at any one time.
- * A limit may be unregistered via {@link #unregisterAppUsageLimitObserver}
+ * A limit is not cleared when the usage time is exceeded. It needs to be unregistered via
+ * {@link #unregisterAppUsageLimitObserver}.
+ * <p>
+ * Note: usage limits are not persisted in the system and are cleared on reboots. Callers
+ * must reset any limits that they need on reboots.
* <p>
* This method is similar to {@link #registerAppUsageObserver}, but the usage limit set here
* will be visible to the launcher so that it can report the limit to the user and how much
@@ -757,12 +761,15 @@
* @param observedEntities The list of packages and token to observe for usage time. Cannot be
* null and must include at least one package or token.
* @param timeLimit The total time the set of apps can be in the foreground before the
- * callbackIntent is delivered. Must be at least one minute.
+ * callbackIntent is delivered. Must be at least one minute. Note: a limit of
+ * 0 can be set to indicate that the user has already exhausted the limit for
+ * a group, in which case, the given {@code callbackIntent} will be ignored.
* @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
- * @param callbackIntent The PendingIntent that will be dispatched when the usage limit is
+ * @param callbackIntent The PendingIntent that will be dispatched when the usage limit is
* exceeded by the group of apps. The delivered Intent will also contain
* the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
- * {@link #EXTRA_TIME_USED}. Cannot be null.
+ * {@link #EXTRA_TIME_USED}. Cannot be {@code null} unless the observer is
+ * being registered with a {@code timeLimit} of 0.
* @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
* permissions.
* @hide
@@ -772,7 +779,7 @@
android.Manifest.permission.SUSPEND_APPS,
android.Manifest.permission.OBSERVE_APP_USAGE})
public void registerAppUsageLimitObserver(int observerId, @NonNull String[] observedEntities,
- long timeLimit, @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
+ long timeLimit, @NonNull TimeUnit timeUnit, PendingIntent callbackIntent) {
try {
mService.registerAppUsageLimitObserver(observerId, observedEntities,
timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index b27c5dc..6c6fcb2 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -650,6 +650,18 @@
*/
public static final int PRIVATE_FLAG_USE_EMBEDDED_DEX = 1 << 25;
+ /**
+ * Value for {@link #privateFlags}: indicates whether this application's data will be cleared
+ * on a failed restore.
+ *
+ * <p>Comes from the
+ * android.R.styleable#AndroidManifestApplication_allowClearUserDataOnFailedRestore attribute
+ * of the <application> tag.
+ *
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE = 1 << 26;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -676,6 +688,7 @@
PRIVATE_FLAG_VENDOR,
PRIVATE_FLAG_VIRTUAL_PRELOAD,
PRIVATE_FLAG_HAS_FRAGILE_USER_DATA,
+ PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoPrivateFlags {}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 96b6eb52..0abd5ea 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3747,6 +3747,13 @@
ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable
+ .AndroidManifestApplication_allowClearUserDataOnFailedRestore,
+ true)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE;
+ }
+
ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0);
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index 27f0b04..f413d7c 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -365,6 +365,17 @@
}
/**
+ * Gets whether or not a non-default saturation level is currently applied to the display.
+ *
+ * @return {@code true} if the display is not at full saturation
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public boolean isSaturationActivated() {
+ return mManager.isSaturationActivated();
+ }
+
+ /**
* Set the level of color saturation to apply to a specific app.
*
* @param packageName the package name of the app whose windows should be desaturated
@@ -588,6 +599,14 @@
}
}
+ boolean isSaturationActivated() {
+ try {
+ return mCdm.isSaturationActivated();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
boolean setAppSaturationLevel(String packageName, int saturationLevel) {
try {
return mCdm.setAppSaturationLevel(packageName, saturationLevel);
diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
index 30e76cf..88b59a6 100644
--- a/core/java/android/hardware/display/IColorDisplayManager.aidl
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -24,6 +24,7 @@
boolean setSaturationLevel(int saturationLevel);
boolean setAppSaturationLevel(String packageName, int saturationLevel);
+ boolean isSaturationActivated();
int getTransformCapabilities();
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 629289b..0384faa 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1093,6 +1093,12 @@
return buildUniqueFileWithExtension(parent, parts[0], parts[1]);
}
+ /** {@hide} */
+ public static File buildNonUniqueFile(File parent, String mimeType, String displayName) {
+ final String[] parts = splitFileName(mimeType, displayName);
+ return buildFile(parent, parts[0], parts[1]);
+ }
+
/**
* Generates a unique file name under the given parent directory, keeping
* any extension intact.
diff --git a/core/java/android/service/textclassifier/TEST_MAPPING b/core/java/android/service/textclassifier/TEST_MAPPING
new file mode 100644
index 0000000..ccf2631
--- /dev/null
+++ b/core/java/android/service/textclassifier/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/base/core/java/android/view/textclassifier"
+ }
+ ]
+}
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 3f18d8b..39b6876 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -95,7 +95,12 @@
protected final Context mContext;
// these are optional, set by the caller
- @UnsupportedAppUsage
+ /**
+ * If any developer has desire to change this value, they should instead use
+ * {@link #cloneInContext(Context)} and set the new factory in thew newly-created
+ * LayoutInflater.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private boolean mFactorySet;
@UnsupportedAppUsage
private Factory mFactory;
diff --git a/core/java/android/view/textclassifier/TEST_MAPPING b/core/java/android/view/textclassifier/TEST_MAPPING
new file mode 100644
index 0000000..0d3c346
--- /dev/null
+++ b/core/java/android/view/textclassifier/TEST_MAPPING
@@ -0,0 +1,23 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.textclassifier"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsViewTestCases",
+ "options": [
+ {
+ "include-filter": "android.view.textclassifier.cts"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 9d7a482..af7210f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -657,7 +657,7 @@
private Runnable mClearScrollingCache;
Runnable mPositionScrollAfterLayout;
private int mMinimumVelocity;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124051740)
private int mMaximumVelocity;
private float mVelocityScale = 1.0f;
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 1c5f837..5a85012 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -80,10 +81,24 @@
private final Rect mTempRect = new Rect();
@UnsupportedAppUsage
private OverScroller mScroller;
- @UnsupportedAppUsage
- private EdgeEffect mEdgeGlowLeft;
- @UnsupportedAppUsage
- private EdgeEffect mEdgeGlowRight;
+ /**
+ * Tracks the state of the left edge glow.
+ *
+ * Even though this field is practically final, we cannot make it final because there are apps
+ * setting it via reflection and they need to keep working until they target Q.
+ */
+ @NonNull
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124053130)
+ private EdgeEffect mEdgeGlowLeft = new EdgeEffect(getContext());
+
+ /**
+ * Tracks the state of the bottom edge glow.
+ *
+ * Even though this field is practically final, we cannot make it final because there are apps
+ * setting it via reflection and they need to keep working until they target Q.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124052619)
+ private EdgeEffect mEdgeGlowRight = new EdgeEffect(getContext());
/**
* Position of the last motion event.
@@ -216,6 +231,74 @@
}
/**
+ * Sets the edge effect color for both left and right edge effects.
+ *
+ * @param color The color for the edge effects.
+ * @see #setLeftEdgeEffectColor(int)
+ * @see #setRightEdgeEffectColor(int)
+ * @see #getLeftEdgeEffectColor()
+ * @see #getRightEdgeEffectColor()
+ */
+ public void setEdgeEffectColor(@ColorInt int color) {
+ setLeftEdgeEffectColor(color);
+ setRightEdgeEffectColor(color);
+ }
+
+ /**
+ * Sets the right edge effect color.
+ *
+ * @param color The color for the right edge effect.
+ * @see #setLeftEdgeEffectColor(int)
+ * @see #setEdgeEffectColor(int)
+ * @see #getLeftEdgeEffectColor()
+ * @see #getRightEdgeEffectColor()
+ */
+ public void setRightEdgeEffectColor(@ColorInt int color) {
+ mEdgeGlowRight.setColor(color);
+ }
+
+ /**
+ * Sets the left edge effect color.
+ *
+ * @param color The color for the left edge effect.
+ * @see #setRightEdgeEffectColor(int)
+ * @see #setEdgeEffectColor(int)
+ * @see #getLeftEdgeEffectColor()
+ * @see #getRightEdgeEffectColor()
+ */
+ public void setLeftEdgeEffectColor(@ColorInt int color) {
+ mEdgeGlowLeft.setColor(color);
+ }
+
+ /**
+ * Returns the left edge effect color.
+ *
+ * @return The left edge effect color.
+ * @see #setEdgeEffectColor(int)
+ * @see #setLeftEdgeEffectColor(int)
+ * @see #setRightEdgeEffectColor(int)
+ * @see #getRightEdgeEffectColor()
+ */
+ @ColorInt
+ public int getLeftEdgeEffectColor() {
+ return mEdgeGlowLeft.getColor();
+ }
+
+ /**
+ * Returns the right edge effect color.
+ *
+ * @return The right edge effect color.
+ * @see #setEdgeEffectColor(int)
+ * @see #setLeftEdgeEffectColor(int)
+ * @see #setRightEdgeEffectColor(int)
+ * @see #getLeftEdgeEffectColor()
+ */
+ @ColorInt
+ public int getRightEdgeEffectColor() {
+ return mEdgeGlowRight.getColor();
+ }
+
+ /**
* @return The maximum amount this scroll view will scroll in response to
* an arrow event.
*/
@@ -665,7 +748,7 @@
mEdgeGlowLeft.onRelease();
}
}
- if (mEdgeGlowLeft != null
+ if (shouldDisplayEdgeEffects()
&& (!mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished())) {
postInvalidateOnAnimation();
}
@@ -693,7 +776,7 @@
mIsBeingDragged = false;
recycleVelocityTracker();
- if (mEdgeGlowLeft != null) {
+ if (shouldDisplayEdgeEffects()) {
mEdgeGlowLeft.onRelease();
mEdgeGlowRight.onRelease();
}
@@ -708,7 +791,7 @@
mIsBeingDragged = false;
recycleVelocityTracker();
- if (mEdgeGlowLeft != null) {
+ if (shouldDisplayEdgeEffects()) {
mEdgeGlowLeft.onRelease();
mEdgeGlowRight.onRelease();
}
@@ -1650,26 +1733,15 @@
}
}
- @Override
- public void setOverScrollMode(int mode) {
- if (mode != OVER_SCROLL_NEVER) {
- if (mEdgeGlowLeft == null) {
- Context context = getContext();
- mEdgeGlowLeft = new EdgeEffect(context);
- mEdgeGlowRight = new EdgeEffect(context);
- }
- } else {
- mEdgeGlowLeft = null;
- mEdgeGlowRight = null;
- }
- super.setOverScrollMode(mode);
+ private boolean shouldDisplayEdgeEffects() {
+ return getOverScrollMode() != OVER_SCROLL_NEVER;
}
@SuppressWarnings({"SuspiciousNameCombination"})
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
- if (mEdgeGlowLeft != null) {
+ if (shouldDisplayEdgeEffects()) {
final int scrollX = mScrollX;
if (!mEdgeGlowLeft.isFinished()) {
final int restoreCount = canvas.save();
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 30df5b5..2cc0d89 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -203,7 +203,7 @@
private int mDuration;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private boolean mIndeterminate;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(trackingBug = 124049927)
private boolean mOnlyIndeterminate;
private Transformation mTransformation;
private AlphaAnimation mAnimation;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index c360903..cd6e1a1 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -89,10 +90,25 @@
private final Rect mTempRect = new Rect();
@UnsupportedAppUsage
private OverScroller mScroller;
- @UnsupportedAppUsage
- private EdgeEffect mEdgeGlowTop;
- @UnsupportedAppUsage
- private EdgeEffect mEdgeGlowBottom;
+ /**
+ * Tracks the state of the top edge glow.
+ *
+ * Even though this field is practically final, we cannot make it final because there are apps
+ * setting it via reflection and they need to keep working until they target Q.
+ */
+ @NonNull
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768600)
+ private EdgeEffect mEdgeGlowTop = new EdgeEffect(getContext());
+
+ /**
+ * Tracks the state of the bottom edge glow.
+ *
+ * Even though this field is practically final, we cannot make it final because there are apps
+ * setting it via reflection and they need to keep working until they target Q.
+ */
+ @NonNull
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769386)
+ private EdgeEffect mEdgeGlowBottom = new EdgeEffect(getContext());
/**
* Position of the last motion event.
@@ -247,6 +263,74 @@
}
/**
+ * Sets the edge effect color for both top and bottom edge effects.
+ *
+ * @param color The color for the edge effects.
+ * @see #setTopEdgeEffectColor(int)
+ * @see #setBottomEdgeEffectColor(int)
+ * @see #getTopEdgeEffectColor()
+ * @see #getBottomEdgeEffectColor()
+ */
+ public void setEdgeEffectColor(@ColorInt int color) {
+ setTopEdgeEffectColor(color);
+ setBottomEdgeEffectColor(color);
+ }
+
+ /**
+ * Sets the bottom edge effect color.
+ *
+ * @param color The color for the bottom edge effect.
+ * @see #setTopEdgeEffectColor(int)
+ * @see #setEdgeEffectColor(int)
+ * @see #getTopEdgeEffectColor()
+ * @see #getBottomEdgeEffectColor()
+ */
+ public void setBottomEdgeEffectColor(@ColorInt int color) {
+ mEdgeGlowBottom.setColor(color);
+ }
+
+ /**
+ * Sets the top edge effect color.
+ *
+ * @param color The color for the top edge effect.
+ * @see #setBottomEdgeEffectColor(int)
+ * @see #setEdgeEffectColor(int)
+ * @see #getTopEdgeEffectColor()
+ * @see #getBottomEdgeEffectColor()
+ */
+ public void setTopEdgeEffectColor(@ColorInt int color) {
+ mEdgeGlowTop.setColor(color);
+ }
+
+ /**
+ * Returns the top edge effect color.
+ *
+ * @return The top edge effect color.
+ * @see #setEdgeEffectColor(int)
+ * @see #setTopEdgeEffectColor(int)
+ * @see #setBottomEdgeEffectColor(int)
+ * @see #getBottomEdgeEffectColor()
+ */
+ @ColorInt
+ public int getTopEdgeEffectColor() {
+ return mEdgeGlowTop.getColor();
+ }
+
+ /**
+ * Returns the bottom edge effect color.
+ *
+ * @return The bottom edge effect color.
+ * @see #setEdgeEffectColor(int)
+ * @see #setTopEdgeEffectColor(int)
+ * @see #setBottomEdgeEffectColor(int)
+ * @see #getTopEdgeEffectColor()
+ */
+ @ColorInt
+ public int getBottomEdgeEffectColor() {
+ return mEdgeGlowBottom.getColor();
+ }
+
+ /**
* @return The maximum amount this scroll view will scroll in response to
* an arrow event.
*/
@@ -624,6 +708,10 @@
return mIsBeingDragged;
}
+ private boolean shouldDisplayEdgeEffects() {
+ return getOverScrollMode() != OVER_SCROLL_NEVER;
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
initVelocityTrackerIfNotExists();
@@ -732,7 +820,7 @@
mEdgeGlowTop.onRelease();
}
}
- if (mEdgeGlowTop != null
+ if (shouldDisplayEdgeEffects()
&& (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
postInvalidateOnAnimation();
}
@@ -1670,7 +1758,7 @@
recycleVelocityTracker();
- if (mEdgeGlowTop != null) {
+ if (shouldDisplayEdgeEffects()) {
mEdgeGlowTop.onRelease();
mEdgeGlowBottom.onRelease();
}
@@ -1700,21 +1788,6 @@
}
@Override
- public void setOverScrollMode(int mode) {
- if (mode != OVER_SCROLL_NEVER) {
- if (mEdgeGlowTop == null) {
- Context context = getContext();
- mEdgeGlowTop = new EdgeEffect(context);
- mEdgeGlowBottom = new EdgeEffect(context);
- }
- } else {
- mEdgeGlowTop = null;
- mEdgeGlowBottom = null;
- }
- super.setOverScrollMode(mode);
- }
-
- @Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
return (nestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0;
}
@@ -1758,7 +1831,7 @@
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
- if (mEdgeGlowTop != null) {
+ if (shouldDisplayEdgeEffects()) {
final int scrollY = mScrollY;
final boolean clipToPadding = getClipToPadding();
if (!mEdgeGlowTop.isFinished()) {
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 7f462cb..106b909 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -610,8 +610,16 @@
resolver.registerContentObserver(uri, true,
mFormatChangeObserver, UserHandle.USER_ALL);
} else {
+ // UserHandle.myUserId() is needed. This class is supported by the
+ // remote views mechanism and as a part of that the remote views
+ // can be inflated by a context for another user without the app
+ // having interact users permission - just for loading resources.
+ // For example, when adding widgets from a managed profile to the
+ // home screen. Therefore, we register the ContentObserver with the user
+ // the app is running (e.g. the launcher) and not the user of the
+ // context (e.g. the widget's profile).
resolver.registerContentObserver(uri, true,
- mFormatChangeObserver);
+ mFormatChangeObserver, UserHandle.myUserId());
}
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d876001..51eaa12 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -664,7 +664,7 @@
@UnsupportedAppUsage
private CharWrapper mCharWrapper;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(trackingBug = 124050217)
private Marquee mMarquee;
@UnsupportedAppUsage
private boolean mRestartMarquee;
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 53cae63..1053184 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -118,7 +118,7 @@
<attr name="manageSpaceActivity" format="string" />
<!-- Option to let applications specify that user data can/cannot be
- cleared. This flag is turned on by default.
+ cleared by the user in Settings. This flag is turned on by default.
<em>This attribute is usable only by applications
included in the system image. Third-party apps cannot use it.</em> -->
<attr name="allowClearUserData" format="boolean" />
@@ -1661,7 +1661,12 @@
<!-- If {@code true} the user is prompted to keep the app's data on uninstall -->
<attr name="hasFragileUserData" />
- <attr name="zygotePreloadName" />
+ <attr name="zygotePreloadName" />
+
+ <!-- If {@code true} the system will clear app's data if a restore operation fails.
+ This flag is turned on by default. <em>This attribute is usable only by system apps.
+ </em> -->
+ <attr name="allowClearUserDataOnFailedRestore"/>
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d2c3b40..5e65605 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2939,6 +2939,8 @@
<public name="zygotePreloadName" />
<public name="useEmbeddedDex" />
<public name="forceUriPermissions" />
+ <!-- @hide @SystemApi -->
+ <public name="allowClearUserDataOnFailedRestore"/>
</public-group>
<public-group type="drawable" first-id="0x010800b4">
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index bdd0370..d2d03e56 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -382,7 +382,6 @@
assertThat(textLanguage, isTextLanguage("ja"));
}
- /* DISABLED: b/122467291
@Test
public void testSuggestConversationActions_textReplyOnly_maxThree() {
if (isTextClassifierDisabled()) return;
@@ -410,7 +409,7 @@
assertThat(conversationAction,
isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
}
- }*/
+ }
@Test
public void testSuggestConversationActions_textReplyOnly_noMax() {
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 7c9529b..56be05b 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -175,7 +175,12 @@
private int[] mSupportedAxes;
private static final int[] EMPTY_AXES = {};
- @UnsupportedAppUsage
+ /**
+ * Please use font in xml and also your application global theme to change the default Typeface.
+ * android:textViewStyle and its attribute android:textAppearance can be used in order to change
+ * typeface and other text related properties.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static void setDefault(Typeface t) {
sDefaultTypeface = t;
nativeSetDefault(t.native_instance);
diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl
index 5f6686a..c038f36 100644
--- a/media/java/android/media/IRingtonePlayer.aidl
+++ b/media/java/android/media/IRingtonePlayer.aidl
@@ -17,6 +17,7 @@
package android.media;
import android.media.AudioAttributes;
+import android.media.VolumeShaper;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
@@ -27,6 +28,8 @@
interface IRingtonePlayer {
/** Used for Ringtone.java playback */
oneway void play(IBinder token, in Uri uri, in AudioAttributes aa, float volume, boolean looping);
+ oneway void playWithVolumeShaping(IBinder token, in Uri uri, in AudioAttributes aa,
+ float volume, boolean looping, in @nullable VolumeShaper.Configuration volumeShaperConfig);
oneway void stop(IBinder token);
boolean isPlaying(IBinder token);
oneway void setPlaybackProperties(IBinder token, float volume, boolean looping);
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 73d3d88..eb680c8 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -381,7 +381,8 @@
volume = mVolume;
}
try {
- mRemotePlayer.play(mRemoteToken, canonicalUri, mAudioAttributes, volume, looping);
+ mRemotePlayer.playWithVolumeShaping(mRemoteToken, canonicalUri, mAudioAttributes,
+ volume, looping, mVolumeShaperConfig);
} catch (RemoteException e) {
if (!playFallbackRingtone()) {
Log.w(TAG, "Problem playing ringtone: " + e);
diff --git a/media/java/android/media/VolumeShaper.aidl b/media/java/android/media/VolumeShaper.aidl
new file mode 100644
index 0000000..e99c13f
--- /dev/null
+++ b/media/java/android/media/VolumeShaper.aidl
@@ -0,0 +1,19 @@
+/* Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable VolumeShaper;
+parcelable VolumeShaper.Configuration;
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index e08dab4..4906695 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -17,8 +17,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "SoundPool"
+#include <chrono>
#include <inttypes.h>
-
+#include <thread>
#include <utils/Log.h>
#define USE_SHARED_MEM_BUFFER
@@ -967,6 +968,12 @@
if (mState != IDLE) {
setVolume_l(0, 0);
ALOGV("stop");
+ // Since we're forcibly halting the previously playing content,
+ // we sleep here to ensure the volume is ramped down before we stop the track.
+ // Ideally the sleep time is the mixer period, or an approximation thereof
+ // (Fast vs Normal tracks are different).
+ // TODO: consider pausing instead of stop here.
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
mAudioTrack->stop();
mPrevSampleID = mSample->sampleID();
mSample.clear();
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 51afbc7..730c409 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -230,6 +230,7 @@
ASurfaceTransaction_reparent; # introduced=29
ASurfaceTransaction_setBuffer; # introduced=29
ASurfaceTransaction_setBufferAlpha; # introduced=29
+ ASurfaceTransaction_setBufferDataSpace; # introduced=29
ASurfaceTransaction_setBufferTransparency; # introduced=29
ASurfaceTransaction_setColor; # introduced=29
ASurfaceTransaction_setDamageRegion; # introduced=29
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 7d2934b..d07052b 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -440,6 +440,20 @@
transaction->setAlpha(surfaceControl, alpha);
}
+void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ ADataSpace aDataSpace) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ LOG_ALWAYS_FATAL_IF(!isDataSpaceValid(surfaceControl, aDataSpace), "invalid dataspace");
+
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ transaction->setDataspace(surfaceControl, static_cast<ui::Dataspace>(aDataSpace));
+}
+
void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* aSurfaceTransaction,
ASurfaceControl* aSurfaceControl,
struct AHdrMetadata_smpte2086* metadata) {
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index d502baa..91353d7 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -71,18 +71,38 @@
android:maxLines="1"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/pkg_divider" />
- <!-- 24 dp icon with 16 dp padding all around to mirror notification content margins -->
- <ImageButton
- android:id="@+id/info"
- android:layout_width="56dp"
- android:layout_height="56dp"
- android:layout_alignParentEnd="true"
+ <LinearLayout
+ android:id="@+id/info_and_settings"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_centerVertical="true"
- android:background="@drawable/ripple_drawable"
- android:contentDescription="@string/notification_more_settings"
- android:padding="16dp"
- android:src="@drawable/ic_info"
- android:tint="?android:attr/colorAccent" />
+ android:layout_alignParentEnd="true"
+ android:paddingHorizontal="16dp"
+ android:orientation="horizontal">
+ <!-- Optional link to app. Only appears if the channel is not disabled and the app
+asked for it -->
+ <ImageButton
+ android:id="@+id/app_settings"
+ android:layout_width="40dp"
+ android:layout_height="56dp"
+ android:layout_centerVertical="true"
+ android:paddingRight="16dp"
+ android:visibility="gone"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/notification_app_settings"
+ android:src="@drawable/ic_settings"
+ android:tint="?android:attr/colorAccent" />
+ <!-- 24 dp icon with 16 dp padding all around to mirror notification content margins -->
+ <ImageButton
+ android:id="@+id/info"
+ android:layout_width="24dp"
+ android:layout_height="56dp"
+ android:layout_centerVertical="true"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/notification_more_settings"
+ android:src="@drawable/ic_info"
+ android:tint="?android:attr/colorAccent" />
+ </LinearLayout>
</RelativeLayout>
<LinearLayout
@@ -143,50 +163,61 @@
</LinearLayout>
<!-- Settings and Done buttons -->
- <LinearLayout
+ <RelativeLayout
android:id="@+id/block_or_minimize"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_guts_button_spacing"
android:layout_marginStart="@dimen/notification_guts_button_side_margin"
android:layout_marginEnd="@dimen/notification_guts_button_side_margin"
- android:gravity="end"
- android:orientation="horizontal">
-
- <!-- Optional link to app. Only appears if the channel is not disabled and the app
- asked for it -->
+ android:clipChildren="false"
+ android:clipToPadding="false">
<TextView
- android:id="@+id/app_settings"
- android:text="@string/notification_app_settings"
+ android:id="@+id/done"
+ android:text="@string/inline_done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:visibility="gone"
- android:ellipsize="end"
- android:maxLines="1"
+ android:layout_centerVertical="true"
style="@style/TextAppearance.NotificationInfo.Button"/>
- <TextView
- android:id="@+id/block"
- android:text="@string/inline_stop_button"
+
+ <LinearLayout
+ android:id="@+id/block_buttons"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
- style="@style/TextAppearance.NotificationInfo.Button"/>
- <TextView
- android:id="@+id/minimize"
- android:text="@string/inline_minimize_button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
- style="@style/TextAppearance.NotificationInfo.Button" />
- <TextView
- android:id="@+id/keep"
- android:minWidth="48dp"
- android:text="@string/inline_keep_button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
- style="@style/TextAppearance.NotificationInfo.Button"/>
- </LinearLayout>
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_alignParentEnd="true"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/deliver_silently"
+ android:text="@string/inline_deliver_silently_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
+ android:paddingRight="24dp"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/block"
+ android:text="@string/inline_block_button"
+ android:minWidth="48dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ <TextView
+ android:id="@+id/minimize"
+ android:text="@string/inline_minimize_button"
+ android:minWidth="48dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
+ style="@style/TextAppearance.NotificationInfo.Button"/>
+ </LinearLayout>
+
+
+ </RelativeLayout>
<LinearLayout
android:id="@+id/interruptiveness_settings"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 89c6c8a..db92ed2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1573,12 +1573,18 @@
<string name="inline_blocking_helper">You usually dismiss these notifications.
\nKeep showing them?</string>
+ <!-- Notification Inline controls: button to dismiss the blocking helper [CHAR_LIMIT=25] -->
+ <string name="inline_done_button">Done</string>
+
<!-- Notification Inline controls: continue receiving notifications prompt, channel level -->
<string name="inline_keep_showing">Keep showing these notifications?</string>
<!-- Notification inline controls: block notifications button -->
<string name="inline_stop_button">Stop notifications</string>
+ <!-- Notification inline controls: button to deliver notifications silently from this channel [CHAR_LIMIT=35] -->
+ <string name="inline_deliver_silently_button">Deliver Silently</string>
+
<!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=35] -->
<string name="inline_block_button">Block</string>
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index ddd9cbf..aebadf9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -16,6 +16,7 @@
package com.android.systemui.media;
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -24,6 +25,7 @@
import android.media.IAudioService;
import android.media.IRingtonePlayer;
import android.media.Ringtone;
+import android.media.VolumeShaper;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
@@ -78,11 +80,16 @@
private final Ringtone mRingtone;
public Client(IBinder token, Uri uri, UserHandle user, AudioAttributes aa) {
+ this(token, uri, user, aa, null);
+ }
+
+ Client(IBinder token, Uri uri, UserHandle user, AudioAttributes aa,
+ @Nullable VolumeShaper.Configuration volumeShaperConfig) {
mToken = token;
mRingtone = new Ringtone(getContextForUser(user), false);
mRingtone.setAudioAttributes(aa);
- mRingtone.setUri(uri);
+ mRingtone.setUri(uri, volumeShaperConfig);
}
@Override
@@ -99,6 +106,12 @@
@Override
public void play(IBinder token, Uri uri, AudioAttributes aa, float volume, boolean looping)
throws RemoteException {
+ playWithVolumeShaping(token, uri, aa, volume, looping, null);
+ }
+ @Override
+ public void playWithVolumeShaping(IBinder token, Uri uri, AudioAttributes aa, float volume,
+ boolean looping, @Nullable VolumeShaper.Configuration volumeShaperConfig)
+ throws RemoteException {
if (LOGD) {
Log.d(TAG, "play(token=" + token + ", uri=" + uri + ", uid="
+ Binder.getCallingUid() + ")");
@@ -108,7 +121,7 @@
client = mClients.get(token);
if (client == null) {
final UserHandle user = Binder.getCallingUserHandle();
- client = new Client(token, uri, user, aa);
+ client = new Client(token, uri, user, aa, volumeShaperConfig);
token.linkToDeath(client, 0);
mClients.put(token, client);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
index 43b5503..a1fcbeb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java
@@ -30,6 +30,9 @@
/** Counter tag for when the user hits 'stop notifications' in the blocking helper. */
public static final String BLOCKING_HELPER_STOP_NOTIFICATIONS =
"blocking_helper_stop_notifications";
+ /** Counter tag for when the user hits 'deliver silently' in the blocking helper. */
+ public static final String BLOCKING_HELPER_DELIVER_SILENTLY =
+ "blocking_helper_deliver_silently";
/** Counter tag for when the user hits 'show silently' in the blocking helper. */
public static final String BLOCKING_HELPER_TOGGLE_SILENT =
"blocking_helper_toggle_silent";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index c7b2fab..3723731 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -84,6 +84,7 @@
public static final int ACTION_UNDO = 1;
public static final int ACTION_TOGGLE_SILENT = 2;
public static final int ACTION_BLOCK = 3;
+ public static final int ACTION_DELIVER_SILENTLY = 4;
private INotificationManager mINotificationManager;
private PackageManager mPm;
@@ -135,30 +136,26 @@
};
private OnClickListener mOnToggleSilent = v -> {
- Runnable saveImportance = () -> {
- swapContent(ACTION_TOGGLE_SILENT, true /* animate */);
- if (mIsForBlockingHelper) {
- mMetricsLogger.write(getLogMaker()
- .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
- .setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_ALERT_ME));
- }
- };
- if (mCheckSaveListener != null) {
- mCheckSaveListener.checkSave(saveImportance, mSbn);
- } else {
- saveImportance.run();
- }
+ handleSaveImportance(ACTION_TOGGLE_SILENT, MetricsEvent.BLOCKING_HELPER_CLICK_ALERT_ME);
+ };
+
+ private OnClickListener mOnDeliverSilently = v -> {
+ handleSaveImportance(
+ ACTION_DELIVER_SILENTLY, MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT);
};
private OnClickListener mOnStopOrMinimizeNotifications = v -> {
+ handleSaveImportance(ACTION_BLOCK, MetricsEvent.BLOCKING_HELPER_CLICK_BLOCKED);
+ };
+
+ private void handleSaveImportance(int action, int metricsSubtype) {
Runnable saveImportance = () -> {
- swapContent(ACTION_BLOCK, true /* animate */);
+ swapContent(action, true /* animate */);
if (mIsForBlockingHelper) {
mMetricsLogger.write(getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
.setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_BLOCKED));
+ .setSubtype(metricsSubtype));
}
};
if (mCheckSaveListener != null) {
@@ -166,7 +163,7 @@
} else {
saveImportance.run();
}
- };
+ }
private OnClickListener mOnUndo = v -> {
// Reset exit counter that we'll log and record an undo event separately (not an exit event)
@@ -283,8 +280,6 @@
mMetricsLogger.write(notificationControlsLogMaker());
}
-
-
private void bindHeader() throws RemoteException {
// Package name
Drawable pkgicon = null;
@@ -479,17 +474,21 @@
findViewById(R.id.block_or_minimize).setVisibility(VISIBLE);
findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
View block = findViewById(R.id.block);
- TextView keep = findViewById(R.id.keep);
+ TextView done = findViewById(R.id.done);
View minimize = findViewById(R.id.minimize);
+ View deliverSilently = findViewById(R.id.deliver_silently);
+
block.setOnClickListener(mOnStopOrMinimizeNotifications);
- keep.setOnClickListener(mOnKeepShowing);
+ done.setOnClickListener(mOnKeepShowing);
minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
+ deliverSilently.setOnClickListener(mOnDeliverSilently);
if (mIsNonblockable) {
- keep.setText(android.R.string.ok);
+ done.setText(android.R.string.ok);
block.setVisibility(GONE);
minimize.setVisibility(GONE);
+ deliverSilently.setVisibility(GONE);
} else if (mIsForeground) {
block.setVisibility(GONE);
minimize.setVisibility(VISIBLE);
@@ -499,7 +498,7 @@
}
// Set up app settings link (i.e. Customize)
- TextView settingsLinkView = findViewById(R.id.app_settings);
+ View settingsLinkView = findViewById(R.id.app_settings);
Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
mSingleNotificationChannel,
mSbn.getId(), mSbn.getTag());
@@ -507,7 +506,6 @@
&& settingsIntent != null
&& !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
settingsLinkView.setVisibility(VISIBLE);
- settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
settingsLinkView.setOnClickListener((View view) -> {
mAppSettingsClickListener.onClick(view, settingsIntent);
});
@@ -531,6 +529,11 @@
case ACTION_UNDO:
mChosenImportance = mStartingChannelImportance;
break;
+ case ACTION_DELIVER_SILENTLY:
+ mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
+ mChosenImportance = IMPORTANCE_LOW;
+ confirmationText.setText(R.string.notification_channel_silenced);
+ break;
case ACTION_TOGGLE_SILENT:
mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
if (mWasShownHighPriority) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 105bd9d..19a73f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -737,7 +737,7 @@
guts.setGutsContent(mNotificationInfo);
mNotificationInfo.setGutsParent(guts);
- mNotificationInfo.findViewById(R.id.keep).performClick();
+ mNotificationInfo.findViewById(R.id.done).performClick();
verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
mTestableLooper.processAllMessages();
@@ -765,7 +765,7 @@
guts.setGutsContent(mNotificationInfo);
mNotificationInfo.setGutsParent(guts);
- mNotificationInfo.findViewById(R.id.keep).performClick();
+ mNotificationInfo.findViewById(R.id.done).performClick();
verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
mTestableLooper.processAllMessages();
@@ -961,6 +961,41 @@
}
@Test
+ public void testSilentlyChangedCallsUpdateNotificationChannel_blockingHelper()
+ throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_LOW);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ 1 /* numChannels */,
+ mSbn,
+ null /* checkSaveListener */,
+ null /* onSettingsClick */,
+ null /* onAppSettingsClick */,
+ true /*provisioned */,
+ false /* isNonblockable */,
+ true /* isForBlockingHelper */,
+ true /* isUserSentimentNegative */,
+ IMPORTANCE_DEFAULT,
+ false);
+
+ mNotificationInfo.findViewById(R.id.deliver_silently).performClick();
+ waitForUndoButton();
+ mNotificationInfo.handleCloseControls(true, false);
+
+ mTestableLooper.processAllMessages();
+ ArgumentCaptor<NotificationChannel> updated =
+ ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+ anyString(), eq(TEST_UID), updated.capture());
+ assertTrue((updated.getValue().getUserLockedFields()
+ & USER_LOCKED_IMPORTANCE) != 0);
+ assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance());
+ }
+
+ @Test
public void testKeepUpdatesNotificationChannel() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
index 39d5c9d..86ad52d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerServiceShellCommand.java
@@ -77,15 +77,12 @@
pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
pw.println(" To reset, call with just the USER_ID argument.");
pw.println("");
- pw.println("");
pw.println(" set default-service-enabled USER_ID [true|false]");
pw.println(" Enable / disable the default service for the user.");
pw.println("");
- pw.println("");
pw.println(" get default-service-enabled USER_ID");
pw.println(" Checks whether the default service is enabled for the user.");
pw.println("");
- pw.println("");
pw.println(" list sessions [--user USER_ID]");
pw.println(" Lists all pending sessions.");
pw.println("");
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c981e68..7f6648a 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2072,7 +2072,8 @@
new TimingsTraceLog("SystemServerTiming", Trace.TRACE_TAG_SYSTEM_SERVER)
.logDuration("SystemUserUnlock", unlockTime);
} else {
- Slog.d(TAG, "Unlocking user " + id + " took " + unlockTime + " ms");
+ new TimingsTraceLog("SystemServerTiming", Trace.TRACE_TAG_SYSTEM_SERVER)
+ .logDuration("User" + id + "Unlock", unlockTime);
}
}
};
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 9cb6eee..8171ad6 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -387,8 +387,10 @@
Slog.d(TAG, "Setting saturation level: " + saturationLevel);
if (saturationLevel == 100) {
+ setActivated(false);
Matrix.setIdentityM(mMatrixGlobalSaturation, 0);
} else {
+ setActivated(true);
float saturation = saturationLevel * 0.1f;
float desaturation = 1.0f - saturation;
float[] luminance = {0.231f * desaturation, 0.715f * desaturation,
@@ -674,6 +676,10 @@
if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
mDisplayWhiteBalanceTintController.endAnimator();
}
+
+ if (mGlobalSaturationTintController.isAvailable(getContext())) {
+ mGlobalSaturationTintController.setActivated(null);
+ }
}
private void onNightDisplayAutoModeChanged(int autoMode) {
@@ -1671,6 +1677,20 @@
}
@Override
+ public boolean isSaturationActivated() {
+ getContext().enforceCallingPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to get display saturation level");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return !mGlobalSaturationTintController.isActivatedStateNotSet()
+ && mGlobalSaturationTintController.isActivated();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public boolean setAppSaturationLevel(String packageName, int level) {
getContext().enforceCallingPermission(
Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index cf84e22..1b23794 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -187,7 +187,7 @@
@Override
public boolean isDefaultServiceEnabled(int userId) {
synchronized (mLock) {
- return mDefaultServicesDisabled.get(userId);
+ return !mDefaultServicesDisabled.get(userId);
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 1f9b027..e7a71b9 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.os.RemoteException;
import android.security.Scrypt;
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
@@ -162,7 +163,7 @@
}
}
- private void syncKeys() {
+ private void syncKeys() throws RemoteException {
if (mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
// Application keys for the user will not be available for sync.
Log.w(TAG, "Credentials are not set for user " + mUserId);
@@ -195,7 +196,7 @@
&& mCredentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
}
- private void syncKeysForAgent(int recoveryAgentUid) throws IOException {
+ private void syncKeysForAgent(int recoveryAgentUid) throws IOException, RemoteException {
boolean shouldRecreateCurrentVersion = false;
if (!shouldCreateSnapshot(recoveryAgentUid)) {
shouldRecreateCurrentVersion =
@@ -412,7 +413,7 @@
private Map<String, Pair<SecretKey, byte[]>> getKeysToSync(int recoveryAgentUid)
throws InsecureUserException, KeyStoreException, UnrecoverableKeyException,
NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
- InvalidKeyException, InvalidAlgorithmParameterException, IOException {
+ InvalidKeyException, InvalidAlgorithmParameterException, IOException, RemoteException {
PlatformDecryptionKey decryptKey = mPlatformKeyManager.getDecryptKey(mUserId);;
Map<String, WrappedKey> wrappedKeys = mRecoverableKeyStoreDb.getAllKeys(
mUserId, recoveryAgentUid, decryptKey.getGenerationId());
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index 9ca052b..1c18771 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -18,15 +18,21 @@
import android.app.KeyguardManager;
import android.content.Context;
+import android.os.RemoteException;
+import android.security.GateKeeper;
import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.service.gatekeeper.IGateKeeperService;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@@ -34,9 +40,11 @@
import java.security.cert.CertificateException;
import java.util.Locale;
+import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
-import javax.security.auth.DestroyFailedException;
+import javax.crypto.spec.GCMParameterSpec;
/**
* Manages creating and checking the validity of the platform key.
@@ -67,6 +75,10 @@
private static final String ENCRYPT_KEY_ALIAS_SUFFIX = "encrypt";
private static final String DECRYPT_KEY_ALIAS_SUFFIX = "decrypt";
private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15;
+ private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+ private static final int GCM_TAG_LENGTH_BITS = 128;
+ // Only used for checking if a key is usable
+ private static final byte[] GCM_INSECURE_NONCE_BYTES = new byte[12];
private final Context mContext;
private final KeyStoreProxy mKeyStore;
@@ -158,12 +170,14 @@
* @throws KeyStoreException if there is an error in AndroidKeyStore.
* @throws InsecureUserException if the user does not have a lock screen set.
* @throws IOException if there was an issue with local database update.
+ * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*
* @hide
*/
@VisibleForTesting
void regenerate(int userId)
- throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException, IOException {
+ throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException, IOException,
+ RemoteException {
if (!isAvailable(userId)) {
throw new InsecureUserException(String.format(
Locale.US, "%d does not have a lock screen set.", userId));
@@ -190,11 +204,13 @@
* @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
* @throws InsecureUserException if the user does not have a lock screen set.
* @throws IOException if there was an issue with local database update.
+ * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*
* @hide
*/
- public PlatformEncryptionKey getEncryptKey(int userId) throws KeyStoreException,
- UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException, IOException {
+ public PlatformEncryptionKey getEncryptKey(int userId)
+ throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
+ InsecureUserException, IOException, RemoteException {
init(userId);
try {
// Try to see if the decryption key is still accessible before using the encryption key.
@@ -243,14 +259,18 @@
* @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
* @throws InsecureUserException if the user does not have a lock screen set.
* @throws IOException if there was an issue with local database update.
+ * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*
* @hide
*/
- public PlatformDecryptionKey getDecryptKey(int userId) throws KeyStoreException,
- UnrecoverableKeyException, NoSuchAlgorithmException, InsecureUserException, IOException {
+ public PlatformDecryptionKey getDecryptKey(int userId)
+ throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
+ InsecureUserException, IOException, RemoteException {
init(userId);
try {
- return getDecryptKeyInternal(userId);
+ PlatformDecryptionKey decryptionKey = getDecryptKeyInternal(userId);
+ ensureDecryptionKeyIsValid(userId, decryptionKey);
+ return decryptionKey;
} catch (UnrecoverableKeyException e) {
Log.i(TAG, String.format(Locale.US,
"Regenerating permanently invalid Platform key for user %d.",
@@ -284,6 +304,29 @@
}
/**
+ * Tries to use the decryption key to make sure it is not permanently invalidated. The exception
+ * {@code KeyPermanentlyInvalidatedException} is thrown only when the key is in use.
+ *
+ * <p>Note that we ignore all other InvalidKeyException exceptions, because such an exception
+ * may be thrown for auth-bound keys if there's no recent unlock event.
+ */
+ private void ensureDecryptionKeyIsValid(int userId, PlatformDecryptionKey decryptionKey)
+ throws UnrecoverableKeyException {
+ try {
+ Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM).init(Cipher.UNWRAP_MODE,
+ decryptionKey.getKey(),
+ new GCMParameterSpec(GCM_TAG_LENGTH_BITS, GCM_INSECURE_NONCE_BYTES));
+ } catch (KeyPermanentlyInvalidatedException e) {
+ Log.e(TAG, String.format(Locale.US, "The platform key for user %d became invalid.",
+ userId));
+ throw new UnrecoverableKeyException(e.getMessage());
+ } catch (NoSuchAlgorithmException | InvalidKeyException
+ | InvalidAlgorithmParameterException | NoSuchPaddingException e) {
+ // Ignore all other exceptions
+ }
+ }
+
+ /**
* Initializes the class. If there is no current platform key, and the user has a lock screen
* set, will create the platform key and set the generation ID.
*
@@ -291,11 +334,13 @@
* @throws KeyStoreException if there was an error in AndroidKeyStore.
* @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
* @throws IOException if there was an issue with local database update.
+ * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*
* @hide
*/
void init(int userId)
- throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException, IOException {
+ throws KeyStoreException, NoSuchAlgorithmException, InsecureUserException, IOException,
+ RemoteException {
if (!isAvailable(userId)) {
throw new InsecureUserException(String.format(
Locale.US, "%d does not have a lock screen set.", userId));
@@ -372,6 +417,11 @@
&& mKeyStore.containsAlias(getDecryptAlias(userId, generationId));
}
+ @VisibleForTesting
+ IGateKeeperService getGateKeeperService() {
+ return GateKeeper.getService();
+ }
+
/**
* Generates a new 256-bit AES key, and loads it into AndroidKeyStore with the given
* {@code generationId} determining its aliases.
@@ -380,15 +430,23 @@
* available since API version 1.
* @throws KeyStoreException if there was an issue loading the keys into AndroidKeyStore.
* @throws IOException if there was an issue with local database update.
+ * @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*/
private void generateAndLoadKey(int userId, int generationId)
- throws NoSuchAlgorithmException, KeyStoreException, IOException {
+ throws NoSuchAlgorithmException, KeyStoreException, IOException, RemoteException {
String encryptAlias = getEncryptAlias(userId, generationId);
String decryptAlias = getDecryptAlias(userId, generationId);
// SecretKey implementation doesn't provide reliable way to destroy the secret
// so it may live in memory for some time.
SecretKey secretKey = generateAesKey();
+ long secureUserId = getGateKeeperService().getSecureUserId(userId);
+ // TODO(b/124095438): Propagate this failure instead of silently failing.
+ if (secureUserId == GateKeeper.INVALID_SECURE_USER_ID) {
+ Log.e(TAG, "No SID available for user " + userId);
+ return;
+ }
+
// Store decryption key first since it is more likely to fail.
mKeyStore.setEntry(
decryptAlias,
@@ -399,7 +457,7 @@
USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .setBoundToSpecificSecureUserId(userId)
+ .setBoundToSpecificSecureUserId(secureUserId)
.build());
mKeyStore.setEntry(
encryptAlias,
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 18e292f..7e4365d 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -119,6 +119,7 @@
UserManager.DISALLOW_OEM_UNLOCK,
UserManager.DISALLOW_UNMUTE_DEVICE,
UserManager.DISALLOW_AUTOFILL,
+ UserManager.DISALLOW_CONTENT_CAPTURE,
UserManager.DISALLOW_USER_SWITCH,
UserManager.DISALLOW_UNIFIED_PASSWORD,
UserManager.DISALLOW_CONFIG_LOCATION,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6c3e1f4..00105be 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -188,6 +188,7 @@
import android.util.TimeUtils;
import android.util.TypedValue;
import android.util.proto.ProtoOutputStream;
+import android.view.Choreographer;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
@@ -843,7 +844,7 @@
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
synchronized (mGlobalLock) {
SurfaceControl.closeTransaction();
- traceStateLocked(where);
+ mWindowTracing.logState(where);
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -967,7 +968,8 @@
mWindowPlacerLocked = new WindowSurfacePlacer(this);
mTaskSnapshotController = new TaskSnapshotController(this);
- mWindowTracing = WindowTracing.createDefaultAndStartLooper(context);
+ mWindowTracing = WindowTracing.createDefaultAndStartLooper(this,
+ Choreographer.getInstance());
LocalServices.addService(WindowManagerPolicy.class, mPolicy);
@@ -5765,17 +5767,6 @@
proto.write(LAST_ORIENTATION, defaultDisplayContent.getLastOrientation());
}
- void traceStateLocked(String where) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
- try {
- mWindowTracing.traceStateLocked(where, this);
- } catch (Exception e) {
- Log.wtf(TAG, "Exception while tracing state", e);
- } finally {
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- }
-
private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
ArrayList<WindowState> windows) {
pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)");
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 8b1ffa8..abc474d 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -24,12 +24,14 @@
import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
import android.annotation.Nullable;
-import android.content.Context;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.Trace;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import android.view.Choreographer;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.io.File;
import java.io.IOException;
@@ -48,6 +50,10 @@
private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024;
private static final String TAG = "WindowTracing";
+ private final WindowManagerService mService;
+ private final Choreographer mChoreographer;
+ private final WindowManagerGlobalLock mGlobalLock;
+
private final Object mLock = new Object();
private final WindowTraceBuffer.Builder mBufferBuilder;
@@ -57,11 +63,24 @@
private boolean mContinuousMode;
private boolean mEnabled;
private volatile boolean mEnabledLockFree;
+ private boolean mScheduled;
+ private Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
+ log("onFrame" /* where */);
- WindowTracing(File file) {
+ private WindowTracing(File file, WindowManagerService service, Choreographer choreographer) {
+ this(file, service, choreographer, service.mGlobalLock);
+ }
+
+ @VisibleForTesting
+ WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
+ WindowManagerGlobalLock globalLock) {
mBufferBuilder = new WindowTraceBuffer.Builder()
.setTraceFile(file)
.setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE);
+
+ mChoreographer = choreographer;
+ mService = service;
+ mGlobalLock = globalLock;
}
void startTrace(@Nullable PrintWriter pw) throws IOException {
@@ -111,7 +130,8 @@
}
}
- private void setContinuousMode(boolean continuous, PrintWriter pw) {
+ @VisibleForTesting
+ void setContinuousMode(boolean continuous, PrintWriter pw) {
logAndPrintln(pw, "Setting window tracing continuous mode to " + continuous);
if (mEnabled) {
@@ -123,21 +143,14 @@
WindowTraceLogLevel.TRIM;
}
- private void appendTraceEntry(ProtoOutputStream proto) {
- if (!mEnabledLockFree) {
- return;
- }
-
- mTraceBuffer.add(proto);
- }
-
boolean isEnabled() {
return mEnabledLockFree;
}
- static WindowTracing createDefaultAndStartLooper(Context context) {
+ static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
+ Choreographer choreographer) {
File file = new File("/data/misc/wmtrace/wm_trace.pb");
- return new WindowTracing(file);
+ return new WindowTracing(file, service, choreographer);
}
int onShellCommand(ShellCommand shell) {
@@ -164,28 +177,65 @@
}
}
- void traceStateLocked(String where, WindowManagerService service) {
+ /**
+ * If tracing is enabled, log the current state or schedule the next frame to be logged,
+ * according to {@link #mContinuousMode}.
+ *
+ * @param where Logging point descriptor
+ */
+ void logState(String where) {
if (!isEnabled()) {
return;
}
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToBufferLocked");
- try {
- ProtoOutputStream os = new ProtoOutputStream();
- long tokenOuter = os.start(ENTRY);
- os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
- os.write(WHERE, where);
+ if (mContinuousMode) {
+ schedule();
+ } else {
+ log(where);
+ }
+ }
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked");
- try {
- long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
- service.writeToProtoLocked(os, mWindowTraceLogLevel);
- os.end(tokenInner);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ /**
+ * Schedule the log to trace the next frame
+ */
+ private void schedule() {
+ if (mScheduled) {
+ return;
+ }
+
+ mScheduled = true;
+ mChoreographer.postFrameCallback(mFrameCallback);
+ }
+
+ /**
+ * Write the current frame to the buffer
+ *
+ * @param where Logging point descriptor
+ */
+ private void log(String where) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
+ try {
+ synchronized (mGlobalLock) {
+ ProtoOutputStream os = new ProtoOutputStream();
+ long tokenOuter = os.start(ENTRY);
+ os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+ os.write(WHERE, where);
+
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked");
+ try {
+ long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+ mService.writeToProtoLocked(os, mWindowTraceLogLevel);
+ os.end(tokenInner);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ os.end(tokenOuter);
+ mTraceBuffer.add(os);
+
+ mScheduled = false;
}
- os.end(tokenOuter);
- appendTraceEntry(os);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Exception while tracing state", e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index 13436e7..821d97a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -24,14 +24,21 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.expectThrows;
import android.app.KeyguardManager;
import android.content.Context;
+import android.os.RemoteException;
+import android.security.GateKeeper;
+import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.service.gatekeeper.IGateKeeperService;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -53,6 +60,8 @@
import java.security.UnrecoverableKeyException;
import java.util.List;
+import javax.crypto.KeyGenerator;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PlatformKeyManagerTest {
@@ -60,10 +69,15 @@
private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
private static final int USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS = 15;
private static final int USER_ID_FIXTURE = 42;
+ private static final long USER_SID = 4200L;
+ private static final String KEY_ALGORITHM = "AES";
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+ private static final String TESTING_KEYSTORE_KEY_ALIAS = "testing-key-store-key-alias";
@Mock private Context mContext;
@Mock private KeyStoreProxy mKeyStoreProxy;
@Mock private KeyguardManager mKeyguardManager;
+ @Mock private IGateKeeperService mGateKeeperService;
@Captor private ArgumentCaptor<KeyStore.ProtectionParameter> mProtectionParameterCaptor;
@Captor private ArgumentCaptor<KeyStore.Entry> mEntryArgumentCaptor;
@@ -74,18 +88,19 @@
private PlatformKeyManager mPlatformKeyManager;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getTargetContext();
mDatabaseFile = context.getDatabasePath(DATABASE_FILE_NAME);
mRecoverableKeyStoreDb = RecoverableKeyStoreDb.newInstance(context);
- mPlatformKeyManager = new PlatformKeyManager(
- mContext, mKeyStoreProxy, mRecoverableKeyStoreDb);
+ mPlatformKeyManager = new PlatformKeyManagerTestable(
+ mContext, mKeyStoreProxy, mRecoverableKeyStoreDb, mGateKeeperService);
when(mContext.getSystemService(anyString())).thenReturn(mKeyguardManager);
when(mContext.getSystemServiceName(any())).thenReturn("test");
when(mKeyguardManager.isDeviceSecure(USER_ID_FIXTURE)).thenReturn(true);
+ when(mGateKeeperService.getSecureUserId(USER_ID_FIXTURE)).thenReturn(USER_SID);
}
@After
@@ -192,11 +207,36 @@
mPlatformKeyManager.init(USER_ID_FIXTURE);
assertEquals(
- USER_ID_FIXTURE,
+ USER_SID,
getDecryptKeyProtection().getBoundToSpecificSecureUserId());
}
@Test
+ public void init_doesNotCreateDecryptKeyIfNoSid() throws Exception {
+ when(mGateKeeperService.getSecureUserId(USER_ID_FIXTURE))
+ .thenReturn(GateKeeper.INVALID_SECURE_USER_ID);
+
+ mPlatformKeyManager.init(USER_ID_FIXTURE);
+
+ verify(mKeyStoreProxy, never()).setEntry(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ any(),
+ any());
+ }
+
+ @Test
+ public void init_doesNotCreateDecryptKeyOnGateKeeperException() throws Exception {
+ when(mGateKeeperService.getSecureUserId(USER_ID_FIXTURE)).thenThrow(new RemoteException());
+
+ expectThrows(RemoteException.class, () -> mPlatformKeyManager.init(USER_ID_FIXTURE));
+
+ verify(mKeyStoreProxy, never()).setEntry(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ any(),
+ any());
+ }
+
+ @Test
public void init_createsBothKeysWithSameMaterial() throws Exception {
mPlatformKeyManager.init(USER_ID_FIXTURE);
@@ -259,6 +299,9 @@
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ "platform/42/1/encrypt")).thenReturn(true);
+ when(mKeyStoreProxy.getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
+ any())).thenReturn(generateAndroidKeyStoreKey());
mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
@@ -281,6 +324,9 @@
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
+ "platform/42/2/decrypt")).thenReturn(true);
+ when(mKeyStoreProxy.getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ any())).thenReturn(generateAndroidKeyStoreKey());
mPlatformKeyManager.getDecryptKey(USER_ID_FIXTURE);
@@ -352,6 +398,9 @@
doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
eq("com.android.server.locksettings.recoverablekeystore/platform/42/1/decrypt"),
any());
+ when(mKeyStoreProxy.getKey(
+ eq("com.android.server.locksettings.recoverablekeystore/platform/42/2/decrypt"),
+ any())).thenReturn(generateAndroidKeyStoreKey());
when(mKeyStoreProxy
.containsAlias("com.android.server.locksettings.recoverablekeystore/"
@@ -536,4 +585,34 @@
mProtectionParameterCaptor.capture());
return (KeyProtection) mProtectionParameterCaptor.getValue();
}
+
+ private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KEY_ALGORITHM,
+ ANDROID_KEY_STORE_PROVIDER);
+ keyGenerator.init(new KeyGenParameterSpec.Builder(TESTING_KEYSTORE_KEY_ALIAS,
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ }
+
+ class PlatformKeyManagerTestable extends PlatformKeyManager {
+ private IGateKeeperService mGateKeeperService;
+
+ PlatformKeyManagerTestable(
+ Context context,
+ KeyStoreProxy keyStoreProxy,
+ RecoverableKeyStoreDb database,
+ IGateKeeperService gateKeeperService) {
+ super(context, keyStoreProxy, database);
+ mGateKeeperService = gateKeeperService;
+ }
+
+ @Override
+ IGateKeeperService getGateKeeperService() {
+ return mGateKeeperService;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 4e43d00..8caa39d 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -1035,6 +1035,15 @@
0L, group.getUsageRemaining());
}
+ /** Verify that a limit of 0 is allowed for the special case of re-registering an observer. */
+ @Test
+ public void testAppUsageLimitObserver_ZeroTimeLimitIsAllowed() {
+ addAppUsageLimitObserver(OBS_ID1, GROUP1, 0);
+ AppTimeLimitController.AppUsageLimitGroup group = getAppUsageLimitObserver(UID, OBS_ID1);
+ assertNotNull("Observer wasn't added", group);
+ assertEquals("Usage remaining was not 0.", 0, group.getUsageRemaining());
+ }
+
private void startUsage(String packageName) {
mController.noteUsageStart(packageName, USER_ID);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
index b6b9a86..2970c21 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -34,8 +34,8 @@
import android.platform.test.annotations.Presubmit;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.proto.ProtoOutputStream;
+import android.view.Choreographer;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.util.Preconditions;
@@ -74,6 +74,8 @@
@Mock
private WindowManagerService mWmMock;
+ @Mock
+ private Choreographer mChoreographer;
private WindowTracing mWindowTracing;
private File mFile;
@@ -85,7 +87,9 @@
mFile = testContext.getFileStreamPath("tracing_test.dat");
mFile.delete();
- mWindowTracing = new WindowTracing(mFile);
+ mWindowTracing = new WindowTracing(mFile, mWmMock, mChoreographer,
+ new WindowManagerGlobalLock());
+ mWindowTracing.setContinuousMode(false /* continuous */, null /* pw */);
}
@After
@@ -113,15 +117,14 @@
@Test
public void trace_discared_whenNotTracing() {
- mWindowTracing.traceStateLocked("where", mWmMock);
+ mWindowTracing.logState("where");
verifyZeroInteractions(mWmMock);
}
@Test
public void trace_dumpsWindowManagerState_whenTracing() throws Exception {
mWindowTracing.startTrace(mock(PrintWriter.class));
- mWindowTracing.traceStateLocked("where", mWmMock);
-
+ mWindowTracing.logState("where");
verify(mWmMock).writeToProtoLocked(any(), eq(WindowTraceLogLevel.TRIM));
}
@@ -147,7 +150,7 @@
WindowManagerTraceProto.WHERE, "TEST_WM_PROTO");
return null;
}).when(mWmMock).writeToProtoLocked(any(), any());
- mWindowTracing.traceStateLocked("TEST_WHERE", mWmMock);
+ mWindowTracing.logState("TEST_WHERE");
mWindowTracing.stopTrace(mock(PrintWriter.class));
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 873ada0..731cbf4 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -840,7 +840,8 @@
*/
public void addAppUsageLimitObserver(int requestingUid, int observerId, String[] observed,
long timeLimit, PendingIntent callbackIntent, @UserIdInt int userId) {
- if (timeLimit < getMinTimeLimit()) {
+ // Allow the special case of the limit being 0, but with no callback.
+ if (timeLimit != 0L && timeLimit < getMinTimeLimit()) {
throw new IllegalArgumentException("Time limit must be >= " + getMinTimeLimit());
}
synchronized (mLock) {
@@ -858,7 +859,7 @@
"Too many app usage observers added by uid " + requestingUid);
}
group = new AppUsageLimitGroup(user, observerApp, observerId, observed, timeLimit,
- callbackIntent);
+ timeLimit == 0L ? null : callbackIntent);
observerApp.appUsageLimitGroups.append(observerId, group);
if (DEBUG) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index df2f455..af5278f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1377,7 +1377,7 @@
if (packages == null || packages.length == 0) {
throw new IllegalArgumentException("Must specify at least one package");
}
- if (callbackIntent == null) {
+ if (callbackIntent == null && timeLimitMs != 0L) {
throw new NullPointerException("callbackIntent can't be null");
}
final int callingUid = Binder.getCallingUid();