Merge "Default isUserSwitcherEnabled showEvenIfNotActionable" into rvc-dev
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 095dd1e..e7b32c5 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -529,7 +529,9 @@
VLOG("StatsdConfig valid");
} else {
// If there is any error in the config, don't use it.
+ // Remove any existing config with the same key.
ALOGE("StatsdConfig NOT valid");
+ mMetricsManagers.erase(key);
}
}
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 7090bd4..23f2584 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -284,6 +284,7 @@
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
+ FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 8587e145..7cac026 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -660,6 +660,8 @@
FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
+
+ FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index e8c575a..7e825ef 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -361,8 +361,12 @@
protoOutput->end(token);
}
- mLastReportTimeNs = dumpTimeStampNs;
- mLastReportWallClockNs = getWallClockNs();
+ // Do not update the timestamps when data is not cleared to avoid timestamps from being
+ // misaligned.
+ if (erase_data) {
+ mLastReportTimeNs = dumpTimeStampNs;
+ mLastReportWallClockNs = getWallClockNs();
+ }
VLOG("=========================Metric Reports End==========================");
}
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 72decf2..acdffd3 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -198,6 +198,9 @@
optional int64 condition = 3;
repeated MetricConditionLink links = 4;
+
+ reserved 100;
+ reserved 101;
}
message CountMetric {
@@ -218,6 +221,9 @@
repeated MetricStateLink state_link = 9;
optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message DurationMetric {
@@ -245,6 +251,9 @@
optional TimeUnit bucket = 7;
optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message GaugeMetric {
@@ -281,6 +290,9 @@
optional int32 max_pull_delay_sec = 13 [default = 30];
optional bool split_bucket_for_app_upgrade = 14 [default = true];
+
+ reserved 100;
+ reserved 101;
}
message ValueMetric {
@@ -333,6 +345,9 @@
optional bool split_bucket_for_app_upgrade = 17 [default = true];
optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message Alert {
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 076f327..13d977f 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -324,6 +324,41 @@
EXPECT_EQ(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end());
}
+TEST(StatsLogProcessorTest, InvalidConfigRemoved) {
+ // Setup simple config key corresponding to empty config.
+ StatsdStats::getInstance().reset();
+ sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ m->updateMap(1, {1, 2}, {1, 2}, {String16("v1"), String16("v2")},
+ {String16("p1"), String16("p2")}, {String16(""), String16("")});
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
+ StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [](const ConfigKey& key) { return true; },
+ [](const int&, const vector<int64_t>&) {return true;});
+ ConfigKey key(3, 4);
+ StatsdConfig config = MakeConfig(true);
+ p.OnConfigUpdated(0, key, config);
+ EXPECT_EQ(1, p.mMetricsManagers.size());
+ EXPECT_NE(p.mMetricsManagers.find(key), p.mMetricsManagers.end());
+ // Cannot assert the size of mConfigStats since it is static and does not get cleared on reset.
+ EXPECT_NE(StatsdStats::getInstance().mConfigStats.end(),
+ StatsdStats::getInstance().mConfigStats.find(key));
+ EXPECT_EQ(0, StatsdStats::getInstance().mIceBox.size());
+
+ StatsdConfig invalidConfig = MakeConfig(true);
+ invalidConfig.clear_allowed_log_source();
+ p.OnConfigUpdated(0, key, invalidConfig);
+ EXPECT_EQ(0, p.mMetricsManagers.size());
+ // The current configs should not contain the invalid config.
+ EXPECT_EQ(StatsdStats::getInstance().mConfigStats.end(),
+ StatsdStats::getInstance().mConfigStats.find(key));
+ // Both "config" and "invalidConfig" should be in the icebox.
+ EXPECT_EQ(2, StatsdStats::getInstance().mIceBox.size());
+
+}
+
+
TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) {
int uid = 1111;
@@ -1796,6 +1831,48 @@
EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value);
}
+TEST(StatsLogProcessorTest, TestDumpReportWithoutErasingDataDoesNotUpdateTimestamp) {
+ int hostUid = 20;
+ int isolatedUid = 30;
+ sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+ ConfigKey key(3, 4);
+ StatsdConfig config = MakeConfig(false);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(1, 1, config, key, nullptr, 0, mockUidMap);
+ vector<uint8_t> bytes;
+
+ int64_t dumpTime1Ns = 1 * NS_PER_SEC;
+ processor->onDumpReport(key, dumpTime1Ns, false /* include_current_bucket */,
+ true /* erase_data */, ADB_DUMP, FAST, &bytes);
+
+ ConfigMetricsReportList output;
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime1Ns);
+
+ int64_t dumpTime2Ns = 5 * NS_PER_SEC;
+ processor->onDumpReport(key, dumpTime2Ns, false /* include_current_bucket */,
+ false /* erase_data */, ADB_DUMP, FAST, &bytes);
+
+ // Check that the dump report without clearing data is successful.
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime2Ns);
+ EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns);
+
+ int64_t dumpTime3Ns = 10 * NS_PER_SEC;
+ processor->onDumpReport(key, dumpTime3Ns, false /* include_current_bucket */,
+ true /* erase_data */, ADB_DUMP, FAST, &bytes);
+
+ // Check that the previous dump report that didn't clear data did not overwrite the first dump's
+ // timestamps.
+ output.ParseFromArray(bytes.data(), bytes.size());
+ EXPECT_EQ(output.reports_size(), 1);
+ EXPECT_EQ(output.reports(0).current_report_elapsed_nanos(), dumpTime3Ns);
+ EXPECT_EQ(output.reports(0).last_report_elapsed_nanos(), dumpTime1Ns);
+
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f881616..0e3f35e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7495,6 +7495,7 @@
mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION);
mUnreadMessageCount = extras.getInt(EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT);
+ mShortcutIcon = extras.getParcelable(EXTRA_CONVERSATION_ICON);
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c650643..1f90e40 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5868,12 +5868,22 @@
* returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be
* the profile owner of an organization-owned managed profile.
* <p>
- * If the caller is device owner or called on the parent instance, then the
- * restriction will be applied to all users.
+ * If the caller is device owner, then the restriction will be applied to all users. If
+ * called on the parent instance, then the restriction will be applied on the personal profile.
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} to be able to call this method; if it has
* not, a security exception will be thrown.
+ * <p>
+ * <b>Note</b>, this policy type is deprecated for legacy device admins since
+ * {@link android.os.Build.VERSION_CODES#Q}. On Android
+ * {@link android.os.Build.VERSION_CODES#Q} devices, legacy device admins targeting SDK
+ * version {@link android.os.Build.VERSION_CODES#P} or below can still call this API to
+ * disable camera, while legacy device admins targeting SDK version
+ * {@link android.os.Build.VERSION_CODES#Q} will receive a SecurityException. Starting
+ * from Android {@link android.os.Build.VERSION_CODES#R}, requests to disable camera from
+ * legacy device admins targeting SDK version {@link android.os.Build.VERSION_CODES#P} or
+ * below will be silently ignored.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param disabled Whether or not the camera should be disabled.
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 0f999ad..3522b1b 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -133,6 +133,7 @@
/**
* An event type denoting that a component was in the foreground when the stats
* rolled-over. This is effectively treated as a {@link #ACTIVITY_PAUSED}.
+ * This event has a non-null packageName, and a null className.
* {@hide}
*/
public static final int END_OF_DAY = 3;
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index bd1ee27..1a694b3 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -1243,14 +1243,7 @@
private ParcelFileDescriptor getUriShortcutIconFd(@NonNull String packageName,
@NonNull String shortcutId, int userId) {
- String uri = null;
- try {
- uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId,
- userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
+ String uri = getShortcutIconUri(packageName, shortcutId, userId);
if (uri == null) {
return null;
}
@@ -1262,6 +1255,18 @@
}
}
+ private String getShortcutIconUri(@NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ String uri = null;
+ try {
+ uri = mService.getShortcutIconUri(mContext.getPackageName(), packageName, shortcutId,
+ userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return uri;
+ }
+
/**
* Returns the icon for this shortcut, without any badging for the profile.
*
@@ -1357,6 +1362,17 @@
} catch (IOException ignore) {
}
}
+ } else if (shortcut.hasIconUri()) {
+ String uri = getShortcutIconUri(shortcut.getPackage(), shortcut.getId(),
+ shortcut.getUserId());
+ if (uri == null) {
+ return null;
+ }
+ if (shortcut.hasAdaptiveBitmap()) {
+ return Icon.createWithAdaptiveBitmapContentUri(uri);
+ } else {
+ return Icon.createWithContentUri(uri);
+ }
} else if (shortcut.hasIconResource()) {
return Icon.createWithResource(shortcut.getPackage(), shortcut.getIconResourceId());
} else {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 36ffe50..ba34899 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2246,26 +2246,6 @@
.getPackageNameForUid(context, uid), true /* throwException */);
}
- /** {@hide} */
- public static final void enforceTetherChangePermission(Context context, String callingPkg) {
- Preconditions.checkNotNull(context, "Context cannot be null");
- Preconditions.checkNotNull(callingPkg, "callingPkg cannot be null");
-
- if (context.getResources().getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app).length == 2) {
- // Have a provisioning app - must only let system apps (which check this app)
- // turn on tethering
- context.enforceCallingOrSelfPermission(
- android.Manifest.permission.TETHER_PRIVILEGED, "ConnectivityService");
- } else {
- int uid = Binder.getCallingUid();
- // If callingPkg's uid is not same as Binder.getCallingUid(),
- // AppOpsService throws SecurityException.
- Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPkg,
- true /* throwException */);
- }
- }
-
/**
* @deprecated - use getSystemService. This is a kludge to support static access in certain
* situations where a Context pointer is unavailable.
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index fd7cdda..0625457 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -221,15 +221,16 @@
*
* Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
* each pair, the value in the amplitude array determines the strength of the vibration and the
- * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no
- * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored.
+ * value in the timing array determines how long it vibrates for, in milliseconds. Amplitude
+ * values must be between 0 and 255, and an amplitude of 0 implies no vibration (i.e. off). Any
+ * pairs with a timing value of 0 will be ignored.
* </p><p>
* To cause the pattern to repeat, pass the index into the timings array at which to start the
* repetition, or -1 to disable repeating.
* </p>
*
- * @param timings The timing values of the timing / amplitude pairs. Timing values of 0
- * will cause the pair to be ignored.
+ * @param timings The timing values, in milliseconds, of the timing / amplitude pairs. Timing
+ * values of 0 will cause the pair to be ignored.
* @param amplitudes The amplitude values of the timing / amplitude pairs. Amplitude values
* must be between 0 and 255, or equal to {@link #DEFAULT_AMPLITUDE}. An
* amplitude value of 0 implies the motor is off.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index e4d53c6..9a9396c 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -575,21 +575,23 @@
@VisibleForTesting
public boolean onStateChanged(InsetsState state) {
- boolean localStateChanged = !mState.equals(state, true /* excludingCaptionInsets */)
+ boolean stateChanged = !mState.equals(state, true /* excludingCaptionInsets */,
+ false /* excludeInvisibleIme */)
|| !captionInsetsUnchanged();
- if (!localStateChanged && mLastDispatchedState.equals(state)) {
+ if (!stateChanged && mLastDispatchedState.equals(state)) {
return false;
}
if (DEBUG) Log.d(TAG, "onStateChanged: " + state);
updateState(state);
+
+ boolean localStateChanged = !mState.equals(mLastDispatchedState,
+ true /* excludingCaptionInsets */, true /* excludeInvisibleIme */);
mLastDispatchedState.set(state, true /* copySources */);
+
applyLocalVisibilityOverride();
if (localStateChanged) {
- if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
+ if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged, send state to WM: " + mState);
mHost.notifyInsetsChanged();
- }
- if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */)) {
- if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState);
updateRequestedState();
}
return true;
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index b015846..15b9a93 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -191,6 +191,14 @@
@Override
public boolean equals(Object o) {
+ return equals(o, false);
+ }
+
+ /**
+ * @param excludeInvisibleImeFrames If {@link InsetsState#ITYPE_IME} frames should be ignored
+ * when IME is not visible.
+ */
+ public boolean equals(Object o, boolean excludeInvisibleImeFrames) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -198,6 +206,7 @@
if (mType != that.mType) return false;
if (mVisible != that.mVisible) return false;
+ if (excludeInvisibleImeFrames && !mVisible && mType == ITYPE_IME) return true;
if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false;
return mFrame.equals(that.mFrame);
}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index ae70a49..3aa2464 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -18,8 +18,8 @@
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.AnimationType;
-import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsController.DEBUG;
+import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsState.toPublicType;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -221,9 +221,10 @@
final boolean hasControl = mSourceControl != null;
// We still need to let the legacy app know the visibility change even if we don't have the
- // control.
+ // control. If we don't have the source, we don't change the requested visibility for making
+ // the callback behavior compatible.
mController.updateCompatSysUiVisibility(
- mType, hasControl ? mRequestedVisible : isVisible, hasControl);
+ mType, (hasControl || source == null) ? mRequestedVisible : isVisible, hasControl);
// If we don't have control, we are not able to change the visibility.
if (!hasControl) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index f0bca26..397b04e 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -555,7 +555,7 @@
@Override
public boolean equals(Object o) {
- return equals(o, false);
+ return equals(o, false, false);
}
/**
@@ -564,10 +564,13 @@
* excluded.
* @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but
* ignore the caption insets source value.
+ * @param excludeInvisibleImeFrames If {@link #ITYPE_IME} frames should be ignored when IME is
+ * not visible.
* @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
*/
@VisibleForTesting
- public boolean equals(Object o, boolean excludingCaptionInsets) {
+ public boolean equals(Object o, boolean excludingCaptionInsets,
+ boolean excludeInvisibleImeFrames) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
@@ -598,7 +601,7 @@
if (otherSource == null) {
return false;
}
- if (!otherSource.equals(source)) {
+ if (!otherSource.equals(source, excludeInvisibleImeFrames)) {
return false;
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 57f91ed..c098fae 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -893,12 +893,15 @@
}
return;
}
- ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
- if (DEBUG) {
- Log.d(TAG, System.identityHashCode(this)
- + " updateSurface: no valid surface");
- }
+ final ViewRootImpl viewRoot = getViewRootImpl();
+
+ if (viewRoot == null) {
+ return;
+ }
+
+ if (viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
+ notifySurfaceDestroyed();
+ releaseSurfaces();
return;
}
@@ -1109,28 +1112,7 @@
final boolean surfaceChanged = creating;
if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
mSurfaceCreated = false;
- if (mSurface.isValid()) {
- if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
- + "visibleChanged -- surfaceDestroyed");
- callbacks = getSurfaceCallbacks();
- for (SurfaceHolder.Callback c : callbacks) {
- c.surfaceDestroyed(mSurfaceHolder);
- }
- // Since Android N the same surface may be reused and given to us
- // again by the system server at a later point. However
- // as we didn't do this in previous releases, clients weren't
- // necessarily required to clean up properly in
- // surfaceDestroyed. This leads to problems for example when
- // clients don't destroy their EGL context, and try
- // and create a new one on the same surface following reuse.
- // Since there is no valid use of the surface in-between
- // surfaceDestroyed and surfaceCreated, we force a disconnect,
- // so the next connect will always work if we end up reusing
- // the surface.
- if (mSurface.isValid()) {
- mSurface.forceScopedDisconnect();
- }
- }
+ notifySurfaceDestroyed();
}
if (creating) {
@@ -1786,6 +1768,31 @@
}
}
+ private void notifySurfaceDestroyed() {
+ if (mSurface.isValid()) {
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "surfaceDestroyed");
+ SurfaceHolder.Callback[] callbacks = getSurfaceCallbacks();
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceDestroyed(mSurfaceHolder);
+ }
+ // Since Android N the same surface may be reused and given to us
+ // again by the system server at a later point. However
+ // as we didn't do this in previous releases, clients weren't
+ // necessarily required to clean up properly in
+ // surfaceDestroyed. This leads to problems for example when
+ // clients don't destroy their EGL context, and try
+ // and create a new one on the same surface following reuse.
+ // Since there is no valid use of the surface in-between
+ // surfaceDestroyed and surfaceCreated, we force a disconnect,
+ // so the next connect will always work if we end up reusing
+ // the surface.
+ if (mSurface.isValid()) {
+ mSurface.forceScopedDisconnect();
+ }
+ }
+ }
+
/**
* Wrapper of accessibility embedded connection for embedded view hierarchy.
*/
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 301ce9f..3f5ef5a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -39,8 +39,8 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.SecureRandom;
import java.util.ArrayList;
-import java.util.Random;
/**
* Session used when the Android a system-provided content capture service
@@ -50,7 +50,9 @@
private static final String TAG = ContentCaptureSession.class.getSimpleName();
- private static final Random sIdGenerator = new Random();
+ // TODO(b/158778794): to make the session ids truly globally unique across
+ // processes, we may need to explore other options.
+ private static final SecureRandom ID_GENERATOR = new SecureRandom();
/**
* Initial state, when there is no session.
@@ -622,7 +624,7 @@
private static int getRandomSessionId() {
int id;
do {
- id = sIdGenerator.nextInt();
+ id = ID_GENERATOR.nextInt();
} while (id == NO_SESSION_ID);
return id;
}
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index e07181a..843700c 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -971,7 +971,8 @@
return new TextClassifierEvent.LanguageDetectionEvent.Builder(eventType)
.setEventContext(classificationContext)
.setResultId(classification.getId())
- .setEntityTypes(language)
+ // b/158481016: Disable language logging.
+ //.setEntityTypes(language)
.setScores(score)
.setActionIndices(classification.getActions().indexOf(translateAction))
.setModelName(model)
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 67c97ae..0a1e3a0 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2816,8 +2816,7 @@
}
// no need to query direct share for work profile when its turned off
- UserManager userManager = getSystemService(UserManager.class);
- if (userManager.isQuietModeEnabled(chooserListAdapter.getUserHandle())) {
+ if (isQuietModeEnabled(chooserListAdapter.getUserHandle())) {
getChooserActivityLogger().logSharesheetAppLoadComplete();
return;
}
@@ -2841,6 +2840,12 @@
getChooserActivityLogger().logSharesheetAppLoadComplete();
}
+ @VisibleForTesting
+ protected boolean isQuietModeEnabled(UserHandle userHandle) {
+ UserManager userManager = getSystemService(UserManager.class);
+ return userManager.isQuietModeEnabled(userHandle);
+ }
+
private void setupScrollListener() {
if (mResolverDrawerLayout == null) {
return;
@@ -3095,7 +3100,7 @@
setVerticalScrollEnabled(false);
}
} else if (state == ViewPager.SCROLL_STATE_IDLE) {
- if (mScrollStatus == SCROLL_STATUS_SCROLLING_VERTICAL) {
+ if (mScrollStatus == SCROLL_STATUS_SCROLLING_HORIZONTAL) {
mScrollStatus = SCROLL_STATUS_IDLE;
setVerticalScrollEnabled(true);
}
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 157e0a7..2a7eae6 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,29 +17,32 @@
package com.android.internal.app;
import android.animation.ObjectAnimator;
-import android.animation.TimeAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActionBar;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
-import android.graphics.Matrix;
+import android.graphics.LinearGradient;
import android.graphics.Paint;
-import android.graphics.Path;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.provider.Settings;
+import android.util.AttributeSet;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.animation.PathInterpolator;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.internal.R;
@@ -50,23 +53,12 @@
* @hide
*/
public class PlatLogoActivity extends Activity {
- ImageView mZeroView, mOneView;
- BackslashDrawable mBackslash;
- int mClicks;
+ private static final boolean WRITE_SETTINGS = true;
- static final Paint sPaint = new Paint();
- static {
- sPaint.setStyle(Paint.Style.STROKE);
- sPaint.setStrokeWidth(4f);
- sPaint.setStrokeCap(Paint.Cap.SQUARE);
- }
+ BigDialView mDialView;
@Override
protected void onPause() {
- if (mBackslash != null) {
- mBackslash.stopAnimating();
- }
- mClicks = 0;
super.onPause();
}
@@ -80,101 +72,33 @@
getWindow().setNavigationBarColor(0);
getWindow().setStatusBarColor(0);
- getActionBar().hide();
+ final ActionBar ab = getActionBar();
+ if (ab != null) ab.hide();
- setContentView(R.layout.platlogo_layout);
-
- mBackslash = new BackslashDrawable((int) (50 * dp));
-
- mOneView = findViewById(R.id.one);
- mOneView.setImageDrawable(new OneDrawable());
- mZeroView = findViewById(R.id.zero);
- mZeroView.setImageDrawable(new ZeroDrawable());
-
- final ViewGroup root = (ViewGroup) mOneView.getParent();
- root.setClipChildren(false);
- root.setBackground(mBackslash);
- root.getBackground().setAlpha(0x20);
-
- View.OnTouchListener tl = new View.OnTouchListener() {
- float mOffsetX, mOffsetY;
- long mClickTime;
- ObjectAnimator mRotAnim;
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- measureTouchPressure(event);
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- v.animate().scaleX(1.1f).scaleY(1.1f);
- v.getParent().bringChildToFront(v);
- mOffsetX = event.getRawX() - v.getX();
- mOffsetY = event.getRawY() - v.getY();
- long now = System.currentTimeMillis();
- if (now - mClickTime < 350) {
- mRotAnim = ObjectAnimator.ofFloat(v, View.ROTATION,
- v.getRotation(), v.getRotation() + 3600);
- mRotAnim.setDuration(10000);
- mRotAnim.start();
- mClickTime = 0;
- } else {
- mClickTime = now;
- }
- break;
- case MotionEvent.ACTION_MOVE:
- v.setX(event.getRawX() - mOffsetX);
- v.setY(event.getRawY() - mOffsetY);
- v.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE);
- break;
- case MotionEvent.ACTION_UP:
- v.performClick();
- // fall through
- case MotionEvent.ACTION_CANCEL:
- v.animate().scaleX(1f).scaleY(1f);
- if (mRotAnim != null) mRotAnim.cancel();
- testOverlap();
- break;
- }
- return true;
- }
- };
-
- findViewById(R.id.one).setOnTouchListener(tl);
- findViewById(R.id.zero).setOnTouchListener(tl);
- findViewById(R.id.text).setOnTouchListener(tl);
- }
-
- private void testOverlap() {
- final float width = mZeroView.getWidth();
- final float targetX = mZeroView.getX() + width * .2f;
- final float targetY = mZeroView.getY() + width * .3f;
- if (Math.hypot(targetX - mOneView.getX(), targetY - mOneView.getY()) < width * .2f
- && Math.abs(mOneView.getRotation() % 360 - 315) < 15) {
- mOneView.animate().x(mZeroView.getX() + width * .2f);
- mOneView.animate().y(mZeroView.getY() + width * .3f);
- mOneView.setRotation(mOneView.getRotation() % 360);
- mOneView.animate().rotation(315);
- mOneView.performHapticFeedback(HapticFeedbackConstants.CONFIRM);
-
- mBackslash.startAnimating();
-
- mClicks++;
- if (mClicks >= 7) {
- launchNextStage();
- }
- } else {
- mBackslash.stopAnimating();
+ mDialView = new BigDialView(this, null);
+ if (Settings.System.getLong(getContentResolver(),
+ "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) {
+ mDialView.setUnlockTries(3);
}
+
+ final FrameLayout layout = new FrameLayout(this);
+ layout.setBackgroundColor(0xFFFF0000);
+ layout.addView(mDialView, FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT);
+ setContentView(layout);
}
- private void launchNextStage() {
+ private void launchNextStage(boolean locked) {
final ContentResolver cr = getContentResolver();
if (Settings.System.getLong(cr, "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) {
// For posterity: the moment this user unlocked the easter egg
try {
- Settings.System.putLong(cr,
- "egg_mode", // Settings.System.EGG_MODE,
- System.currentTimeMillis());
+ if (WRITE_SETTINGS) {
+ Settings.System.putLong(cr,
+ "egg_mode", // Settings.System.EGG_MODE,
+ locked ? 0 : System.currentTimeMillis());
+ }
} catch (RuntimeException e) {
Log.e("com.android.internal.app.PlatLogoActivity", "Can't write settings", e);
}
@@ -182,12 +106,12 @@
try {
startActivity(new Intent(Intent.ACTION_MAIN)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK)
.addCategory("com.android.internal.category.PLATLOGO"));
} catch (ActivityNotFoundException ex) {
Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs.");
}
- finish();
+ //finish(); // no longer finish upon unlock; it's fun to frob the dial
}
static final String TOUCH_STATS = "touch.stats";
@@ -223,7 +147,10 @@
if (mPressureMax >= 0) {
touchData.put("min", mPressureMin);
touchData.put("max", mPressureMax);
- Settings.System.putString(getContentResolver(), TOUCH_STATS, touchData.toString());
+ if (WRITE_SETTINGS) {
+ Settings.System.putString(getContentResolver(), TOUCH_STATS,
+ touchData.toString());
+ }
}
} catch (Exception e) {
Log.e("com.android.internal.app.PlatLogoActivity", "Can't write touch settings", e);
@@ -242,149 +169,272 @@
super.onStop();
}
- static class ZeroDrawable extends Drawable {
- int mTintColor;
+ class BigDialView extends ImageView {
+ private static final int COLOR_GREEN = 0xff3ddc84;
+ private static final int COLOR_BLUE = 0xff4285f4;
+ private static final int COLOR_NAVY = 0xff073042;
+ private static final int COLOR_ORANGE = 0xfff86734;
+ private static final int COLOR_CHARTREUSE = 0xffeff7cf;
+ private static final int COLOR_LIGHTBLUE = 0xffd7effe;
- @Override
- public void draw(Canvas canvas) {
- sPaint.setColor(mTintColor | 0xFF000000);
+ private static final int STEPS = 11;
+ private static final float VALUE_CHANGE_MAX = 1f / STEPS;
- canvas.save();
- canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f);
+ private BigDialDrawable mDialDrawable;
+ private boolean mWasLocked;
- canvas.drawCircle(12f, 12f, 10f, sPaint);
- canvas.restore();
+ BigDialView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ BigDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ BigDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ private void init() {
+ mDialDrawable = new BigDialDrawable();
+ setImageDrawable(mDialDrawable);
}
@Override
- public void setAlpha(int alpha) { }
+ public void onDraw(Canvas c) {
+ super.onDraw(c);
+ }
- @Override
- public void setColorFilter(ColorFilter colorFilter) { }
-
- @Override
- public void setTintList(ColorStateList tint) {
- mTintColor = tint.getDefaultColor();
+ double toPositiveDegrees(double rad) {
+ return (Math.toDegrees(rad) + 360 - 90) % 360;
}
@Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
- }
-
- static class OneDrawable extends Drawable {
- int mTintColor;
-
- @Override
- public void draw(Canvas canvas) {
- sPaint.setColor(mTintColor | 0xFF000000);
-
- canvas.save();
- canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f);
-
- final Path p = new Path();
- p.moveTo(12f, 21.83f);
- p.rLineTo(0f, -19.67f);
- p.rLineTo(-5f, 0f);
- canvas.drawPath(p, sPaint);
- canvas.restore();
- }
-
- @Override
- public void setAlpha(int alpha) { }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) { }
-
- @Override
- public void setTintList(ColorStateList tint) {
- mTintColor = tint.getDefaultColor();
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
- }
-
- private static class BackslashDrawable extends Drawable implements TimeAnimator.TimeListener {
- Bitmap mTile;
- Paint mPaint = new Paint();
- BitmapShader mShader;
- TimeAnimator mAnimator = new TimeAnimator();
- Matrix mMatrix = new Matrix();
-
- public void draw(Canvas canvas) {
- canvas.drawPaint(mPaint);
- }
-
- BackslashDrawable(int width) {
- mTile = Bitmap.createBitmap(width, width, Bitmap.Config.ALPHA_8);
- mAnimator.setTimeListener(this);
-
- final Canvas tileCanvas = new Canvas(mTile);
- final float w = tileCanvas.getWidth();
- final float h = tileCanvas.getHeight();
-
- final Path path = new Path();
- path.moveTo(0, 0);
- path.lineTo(w / 2, 0);
- path.lineTo(w, h / 2);
- path.lineTo(w, h);
- path.close();
-
- path.moveTo(0, h / 2);
- path.lineTo(w / 2, h);
- path.lineTo(0, h);
- path.close();
-
- final Paint slashPaint = new Paint();
- slashPaint.setAntiAlias(true);
- slashPaint.setStyle(Paint.Style.FILL);
- slashPaint.setColor(0xFF000000);
- tileCanvas.drawPath(path, slashPaint);
-
- //mPaint.setColor(0xFF0000FF);
- mShader = new BitmapShader(mTile, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
- mPaint.setShader(mShader);
- }
-
- public void startAnimating() {
- if (!mAnimator.isStarted()) {
- mAnimator.start();
+ public boolean onTouchEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mWasLocked = mDialDrawable.isLocked();
+ // pass through
+ case MotionEvent.ACTION_MOVE:
+ float x = ev.getX();
+ float y = ev.getY();
+ float cx = (getLeft() + getRight()) / 2f;
+ float cy = (getTop() + getBottom()) / 2f;
+ float angle = (float) toPositiveDegrees(Math.atan2(x - cx, y - cy));
+ final int oldLevel = mDialDrawable.getUserLevel();
+ mDialDrawable.touchAngle(angle);
+ final int newLevel = mDialDrawable.getUserLevel();
+ if (oldLevel != newLevel) {
+ performHapticFeedback(newLevel == STEPS
+ ? HapticFeedbackConstants.CONFIRM
+ : HapticFeedbackConstants.CLOCK_TICK);
+ }
+ return true;
+ case MotionEvent.ACTION_UP:
+ if (mWasLocked && !mDialDrawable.isLocked()) {
+ launchNextStage(false);
+ }
+ return true;
}
+ return false;
}
- public void stopAnimating() {
- if (mAnimator.isStarted()) {
- mAnimator.cancel();
+ @Override
+ public boolean performClick() {
+ if (mDialDrawable.getUserLevel() < STEPS - 1) {
+ mDialDrawable.setUserLevel(mDialDrawable.getUserLevel() + 1);
+ performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
}
+ return true;
}
- @Override
- public void setAlpha(int alpha) {
- mPaint.setAlpha(alpha);
+ void setUnlockTries(int tries) {
+ mDialDrawable.setUnlockTries(tries);
}
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- mPaint.setColorFilter(colorFilter);
- }
+ private class BigDialDrawable extends Drawable {
+ public final int STEPS = 10;
+ private int mUnlockTries = 0;
+ final Paint mPaint = new Paint();
+ final Drawable mEleven;
+ private boolean mNightMode;
+ private float mValue = 0f;
+ float mElevenAnim = 0f;
+ ObjectAnimator mElevenShowAnimator = ObjectAnimator.ofFloat(this, "elevenAnim", 0f,
+ 1f).setDuration(300);
+ ObjectAnimator mElevenHideAnimator = ObjectAnimator.ofFloat(this, "elevenAnim", 1f,
+ 0f).setDuration(500);
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
+ BigDialDrawable() {
+ mNightMode = getContext().getResources().getConfiguration().isNightModeActive();
+ mEleven = getContext().getDrawable(R.drawable.ic_number11);
+ mElevenShowAnimator.setInterpolator(new PathInterpolator(0.4f, 0f, 0.2f, 1f));
+ mElevenHideAnimator.setInterpolator(new PathInterpolator(0.8f, 0.2f, 0.6f, 1f));
+ }
- @Override
- public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
- if (mShader != null) {
- mMatrix.postTranslate(deltaTime / 4f, 0);
- mShader.setLocalMatrix(mMatrix);
+ public void setUnlockTries(int count) {
+ if (mUnlockTries != count) {
+ mUnlockTries = count;
+ setValue(getValue());
+ invalidateSelf();
+ }
+ }
+
+ boolean isLocked() {
+ return mUnlockTries > 0;
+ }
+
+ public void setValue(float v) {
+ // until the dial is "unlocked", you can't turn it all the way to 11
+ final float max = isLocked() ? 1f - 1f / STEPS : 1f;
+ mValue = v < 0f ? 0f : v > max ? max : v;
invalidateSelf();
}
+
+ public float getValue() {
+ return mValue;
+ }
+
+ public int getUserLevel() {
+ return Math.round(getValue() * STEPS - 0.25f);
+ }
+
+ public void setUserLevel(int i) {
+ setValue(getValue() + ((float) i) / STEPS);
+ }
+
+ public float getElevenAnim() {
+ return mElevenAnim;
+ }
+
+ public void setElevenAnim(float f) {
+ if (mElevenAnim != f) {
+ mElevenAnim = f;
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ final Rect bounds = getBounds();
+ final int w = bounds.width();
+ final int h = bounds.height();
+ final float w2 = w / 2f;
+ final float h2 = h / 2f;
+ final float radius = w / 4f;
+
+ canvas.drawColor(mNightMode ? COLOR_NAVY : COLOR_LIGHTBLUE);
+
+ canvas.save();
+ canvas.rotate(45, w2, h2);
+ canvas.clipRect(w2, h2 - radius, Math.min(w, h), h2 + radius);
+ final int gradientColor = mNightMode ? 0x60000020 : (0x10FFFFFF & COLOR_NAVY);
+ mPaint.setShader(
+ new LinearGradient(w2, h2, Math.min(w, h), h2, gradientColor,
+ 0x00FFFFFF & gradientColor, Shader.TileMode.CLAMP));
+ mPaint.setColor(Color.BLACK);
+ canvas.drawPaint(mPaint);
+ mPaint.setShader(null);
+ canvas.restore();
+
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(COLOR_GREEN);
+
+ canvas.drawCircle(w2, h2, radius, mPaint);
+
+ mPaint.setColor(mNightMode ? COLOR_LIGHTBLUE : COLOR_NAVY);
+ final float cx = w * 0.85f;
+ for (int i = 0; i < STEPS; i++) {
+ final float f = (float) i / STEPS;
+ canvas.save();
+ final float angle = valueToAngle(f);
+ canvas.rotate(-angle, w2, h2);
+ canvas.drawCircle(cx, h2, (i <= getUserLevel()) ? 20 : 5, mPaint);
+ canvas.restore();
+ }
+
+ if (mElevenAnim > 0f) {
+ final int color = COLOR_ORANGE;
+ final int size2 = (int) ((0.5 + 0.5f * mElevenAnim) * w / 14);
+ final float cx11 = cx + size2 / 4f;
+ mEleven.setBounds((int) cx11 - size2, (int) h2 - size2,
+ (int) cx11 + size2, (int) h2 + size2);
+ final int alpha = 0xFFFFFF | ((int) clamp(0xFF * 2 * mElevenAnim, 0, 0xFF)
+ << 24);
+ mEleven.setTint(alpha & color);
+ mEleven.draw(canvas);
+ }
+
+ // don't want to use the rounded value here since the quantization will be visible
+ final float angle = valueToAngle(mValue);
+
+ // it's easier to draw at far-right and rotate backwards
+ canvas.rotate(-angle, w2, h2);
+ mPaint.setColor(Color.WHITE);
+ final float dimple = w2 / 12f;
+ canvas.drawCircle(w - radius - dimple * 2, h2, dimple, mPaint);
+ }
+
+ float clamp(float x, float a, float b) {
+ return x < a ? a : x > b ? b : x;
+ }
+
+ float angleToValue(float a) {
+ return 1f - clamp(a / (360 - 45), 0f, 1f);
+ }
+
+ // rotation: min is at 4:30, max is at 3:00
+ float valueToAngle(float v) {
+ return (1f - v) * (360 - 45);
+ }
+
+ public void touchAngle(float a) {
+ final int oldUserLevel = getUserLevel();
+ final float newValue = angleToValue(a);
+ // this is how we prevent the knob from snapping from max back to min, or from
+ // jumping around wherever the user presses. The new value must be pretty close
+ // to the
+ // previous one.
+ if (Math.abs(newValue - getValue()) < VALUE_CHANGE_MAX) {
+ setValue(newValue);
+
+ if (isLocked() && oldUserLevel != STEPS - 1 && getUserLevel() == STEPS - 1) {
+ mUnlockTries--;
+ }
+
+ if (!isLocked()) {
+ if (getUserLevel() == STEPS && mElevenAnim != 1f
+ && !mElevenShowAnimator.isRunning()) {
+ mElevenHideAnimator.cancel();
+ mElevenShowAnimator.start();
+ } else if (getUserLevel() != STEPS && mElevenAnim == 1f
+ && !mElevenHideAnimator.isRunning()) {
+ mElevenShowAnimator.cancel();
+ mElevenHideAnimator.start();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setAlpha(int i) {
+ }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
}
}
}
+
+
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 0751596..b64923f 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -72,6 +72,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.regex.Pattern;
@@ -784,8 +785,8 @@
}
@RemotableViewMethod
- public void setShortcutIcon(Icon conversationIcon) {
- mConversationIcon = conversationIcon;
+ public void setShortcutIcon(Icon shortcutIcon) {
+ mShortcutIcon = shortcutIcon;
}
/**
@@ -795,7 +796,8 @@
*/
@RemotableViewMethod
public void setConversationTitle(CharSequence conversationTitle) {
- mConversationTitle = conversationTitle;
+ // Remove formatting from the title.
+ mConversationTitle = conversationTitle != null ? conversationTitle.toString() : null;
}
public CharSequence getConversationTitle() {
@@ -1052,6 +1054,9 @@
groups.add(currentGroup);
if (sender == null) {
sender = mUser;
+ } else {
+ // Remove all formatting from the sender name
+ sender = sender.toBuilder().setName(Objects.toString(sender.getName())).build();
}
senders.add(sender);
currentSenderKey = key;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 03a7b3d..93690cd 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1430,6 +1430,32 @@
== StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
}
+ private static class WrappedCallback extends ICheckCredentialProgressCallback.Stub {
+
+ private Handler mHandler;
+ private CheckCredentialProgressCallback mCallback;
+
+ WrappedCallback(Handler handler, CheckCredentialProgressCallback callback) {
+ mHandler = handler;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onCredentialVerified() throws RemoteException {
+ if (mHandler == null) {
+ Log.e(TAG, "Handler is null during callback");
+ }
+ // Kill reference immediately to allow early GC at client side independent of
+ // when system_server decides to lose its reference to the
+ // ICheckCredentialProgressCallback binder object.
+ mHandler.post(() -> {
+ mCallback.onEarlyMatched();
+ mCallback = null;
+ });
+ mHandler = null;
+ }
+ }
+
private ICheckCredentialProgressCallback wrapCallback(
final CheckCredentialProgressCallback callback) {
if (callback == null) {
@@ -1439,13 +1465,7 @@
throw new IllegalStateException("Must construct LockPatternUtils on a looper thread"
+ " to use progress callbacks.");
}
- return new ICheckCredentialProgressCallback.Stub() {
-
- @Override
- public void onCredentialVerified() throws RemoteException {
- mHandler.post(callback::onEarlyMatched);
- }
- };
+ return new WrappedCallback(mHandler, callback);
}
}
diff --git a/core/res/res/drawable-nodpi/ic_number11.xml b/core/res/res/drawable-nodpi/ic_number11.xml
new file mode 100644
index 0000000..daad611
--- /dev/null
+++ b/core/res/res/drawable-nodpi/ic_number11.xml
@@ -0,0 +1,12 @@
+<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="M5.14,5H1.59a0.88,0.88 0,0 1,-0.88 -0.89V0.88A0.87,0.87 0,0 1,1.59 0H9.36a0.87,0.87 0,0 1,0.88 0.88V23.12a0.88,0.88 0,0 1,-0.88 0.88H6a0.88,0.88 0,0 1,-0.88 -0.88Z"
+ android:fillColor="#000000"/>
+ <path
+ android:pathData="M18.19,5H14.64a0.89,0.89 0,0 1,-0.88 -0.89V0.88A0.88,0.88 0,0 1,14.64 0h7.78a0.87,0.87 0,0 1,0.87 0.88V23.12a0.88,0.88 0,0 1,-0.87 0.88H19.08a0.89,0.89 0,0 1,-0.89 -0.88Z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/core/res/res/layout-car/car_alert_dialog.xml b/core/res/res/layout-car/car_alert_dialog.xml
new file mode 100644
index 0000000..569e594
--- /dev/null
+++ b/core/res/res/layout-car/car_alert_dialog.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.internal.widget.AlertDialogLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start|top"
+ android:orientation="vertical">
+
+ <include layout="@layout/car_alert_dialog_title" />
+
+ <FrameLayout
+ android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp">
+
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Space
+ android:id="@+id/textSpacerNoTitle"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_no_title_padding_top" />
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/text_view_start_margin"
+ android:layout_marginEnd="@dimen/text_view_end_margin"
+ style="@style/CarBody2"/>
+
+ <!-- we don't need this spacer, but the id needs to be here for compatibility -->
+ <Space
+ android:id="@+id/textSpacerNoButtons"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+ </LinearLayout>
+ </ScrollView>
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp">
+
+ <FrameLayout
+ android:id="@+id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/car_alert_dialog_button_bar" />
+</com.android.internal.widget.AlertDialogLayout>
diff --git a/core/res/res/layout-car/car_alert_dialog_button_bar.xml b/core/res/res/layout-car/car_alert_dialog_button_bar.xml
new file mode 100644
index 0000000..277b0dc
--- /dev/null
+++ b/core/res/res/layout-car/car_alert_dialog_button_bar.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ android:scrollIndicators="top|bottom"
+ android:fillViewport="true"
+ style="?attr/buttonBarStyle">
+ <com.android.internal.widget.ButtonBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/button_bar_layout_start_padding"
+ android:paddingEnd="@dimen/button_bar_layout_end_padding"
+ android:paddingTop="@dimen/button_bar_layout_top_padding"
+ android:layoutDirection="locale"
+ android:orientation="horizontal"
+ android:gravity="left|center_vertical">
+
+ <Button
+ android:id="@+id/button3"
+ style="@style/CarAction1"
+ android:background="@drawable/car_dialog_button_background"
+ android:layout_marginRight="@dimen/button_end_margin"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/button_layout_height" />
+
+ <Button
+ android:id="@+id/button2"
+ style="@style/CarAction1"
+ android:background="@drawable/car_dialog_button_background"
+ android:layout_marginRight="@dimen/button_end_margin"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/button_layout_height" />
+
+ <Button
+ android:id="@+id/button1"
+ style="@style/CarAction1"
+ android:background="@drawable/car_dialog_button_background"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/button_layout_height" />
+ <Space
+ android:id="@+id/spacer"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="invisible" />
+ </com.android.internal.widget.ButtonBarLayout>
+</ScrollView>
diff --git a/core/res/res/layout-car/car_alert_dialog_title.xml b/core/res/res/layout-car/car_alert_dialog_title.xml
new file mode 100644
index 0000000..ba735a6
--- /dev/null
+++ b/core/res/res/layout-car/car_alert_dialog_title.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/topPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <!-- If the client uses a customTitle, it will be added here. -->
+
+ <RelativeLayout
+ android:id="@+id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/car_card_header_height"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/image_size"
+ android:layout_height="@dimen/image_size"
+ android:layout_marginStart="@dimen/image_margin_start"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:scaleType="fitCenter"
+ android:src="@null" />
+
+ <com.android.internal.widget.DialogTitle
+ android:id="@+id/alertTitle"
+ android:maxLines="1"
+ android:ellipsize="none"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@+id/icon"
+ android:textAlignment="viewStart"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="@dimen/text_view_start_margin"
+ android:layout_marginEnd="@dimen/text_view_end_margin"
+ style="?attr/windowTitleStyle" />
+ </RelativeLayout>
+
+ <Space
+ android:id="@+id/titleDividerNoCustom"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="0dp" />
+</LinearLayout>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c56c78a..8e1dc89 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3830,6 +3830,7 @@
<java-symbol type="drawable" name="android_logotype" />
<java-symbol type="layout" name="platlogo_layout" />
+ <java-symbol type="drawable" name="ic_number11" />
<java-symbol type="integer" name="config_notificationWarnRemoteViewSizeBytes" />
<java-symbol type="integer" name="config_notificationStripRemoteViewSizeBytes" />
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index d4c2569..964ae21 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -678,7 +678,8 @@
final InsetsState currentState = new InsetsState(mController.getState());
// The caption bar source should be synced with the info in mAttachInfo.
assertEquals(captionFrame, currentState.peekSource(ITYPE_CAPTION_BAR).getFrame());
- assertTrue(currentState.equals(state, true /* excludingCaptionInsets*/));
+ assertTrue(currentState.equals(state, true /* excludingCaptionInsets*/,
+ true /* excludeInvisibleIme */));
mController.setCaptionInsetsHeight(0);
mController.onStateChanged(state);
// The caption bar source should not be there at all, because we don't add empty
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index cd93eeb..7115acf 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -276,6 +276,15 @@
}
@Test
+ public void testEquals_excludeInvisibleIme() {
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_IME).setVisible(false);
+ mState2.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 200));
+ mState2.getSource(ITYPE_IME).setVisible(false);
+ assertTrue(mState2.equals(mState, true, true /* excludeInvisibleIme */));
+ }
+
+ @Test
public void testParcelUnparcel() {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 100));
mState.getSource(ITYPE_IME).setVisibleFrame(new Rect(0, 0, 50, 10));
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 44a5263..0f6b51f 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -222,6 +222,11 @@
super.queryTargetServices(adapter);
}
+ @Override
+ protected boolean isQuietModeEnabled(UserHandle userHandle) {
+ return sOverrides.isQuietModeEnabled;
+ }
+
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index a112bdd..7d15bbd 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2583,9 +2583,15 @@
}
public void cancel() {
+ remove();
+ }
+
+ private Consumer<Location> remove() {
+ Consumer<Location> consumer;
ICancellationSignal cancellationSignal;
synchronized (this) {
mExecutor = null;
+ consumer = mConsumer;
mConsumer = null;
if (mAlarmManager != null) {
@@ -2605,6 +2611,8 @@
// ignore
}
}
+
+ return consumer;
}
public void fail() {
@@ -2663,16 +2671,10 @@
}
private void acceptResult(Location location) {
- Consumer<Location> consumer;
- synchronized (this) {
- if (mConsumer == null) {
- return;
- }
- consumer = mConsumer;
- cancel();
+ Consumer<Location> consumer = remove();
+ if (consumer != null) {
+ consumer.accept(location);
}
-
- consumer.accept(location);
}
}
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index e5ad569..54c0bc9 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -317,9 +317,10 @@
@ConnectionState
final int mConnectionState;
final String mClientPackageName;
- final int mVolume;
- final int mVolumeMax;
final int mVolumeHandling;
+ final int mVolumeMax;
+ final int mVolume;
+ final String mAddress;
final Bundle mExtras;
final String mProviderId;
@@ -336,6 +337,7 @@
mVolumeHandling = builder.mVolumeHandling;
mVolumeMax = builder.mVolumeMax;
mVolume = builder.mVolume;
+ mAddress = builder.mAddress;
mExtras = builder.mExtras;
mProviderId = builder.mProviderId;
}
@@ -353,6 +355,7 @@
mVolumeHandling = in.readInt();
mVolumeMax = in.readInt();
mVolume = in.readInt();
+ mAddress = in.readString();
mExtras = in.readBundle();
mProviderId = in.readString();
}
@@ -483,6 +486,15 @@
return mVolume;
}
+ /**
+ * Gets the hardware address of the route if available.
+ * @hide
+ */
+ @Nullable
+ public String getAddress() {
+ return mAddress;
+ }
+
@Nullable
public Bundle getExtras() {
return mExtras == null ? null : new Bundle(mExtras);
@@ -564,6 +576,7 @@
&& (mVolumeHandling == other.mVolumeHandling)
&& (mVolumeMax == other.mVolumeMax)
&& (mVolume == other.mVolume)
+ && Objects.equals(mAddress, other.mAddress)
&& Objects.equals(mProviderId, other.mProviderId);
}
@@ -572,7 +585,7 @@
// Note: mExtras is not included.
return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
- mProviderId);
+ mAddress, mProviderId);
}
@Override
@@ -614,6 +627,7 @@
dest.writeInt(mVolumeHandling);
dest.writeInt(mVolumeMax);
dest.writeInt(mVolume);
+ dest.writeString(mAddress);
dest.writeBundle(mExtras);
dest.writeString(mProviderId);
}
@@ -637,6 +651,7 @@
int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
int mVolumeMax;
int mVolume;
+ String mAddress;
Bundle mExtras;
String mProviderId;
@@ -669,24 +684,7 @@
* @param routeInfo the existing instance to copy data from.
*/
public Builder(@NonNull MediaRoute2Info routeInfo) {
- Objects.requireNonNull(routeInfo, "routeInfo must not be null");
-
- mId = routeInfo.mId;
- mName = routeInfo.mName;
- mFeatures = new ArrayList<>(routeInfo.mFeatures);
- mType = routeInfo.mType;
- mIsSystem = routeInfo.mIsSystem;
- mIconUri = routeInfo.mIconUri;
- mDescription = routeInfo.mDescription;
- mConnectionState = routeInfo.mConnectionState;
- mClientPackageName = routeInfo.mClientPackageName;
- mVolumeHandling = routeInfo.mVolumeHandling;
- mVolumeMax = routeInfo.mVolumeMax;
- mVolume = routeInfo.mVolume;
- if (routeInfo.mExtras != null) {
- mExtras = new Bundle(routeInfo.mExtras);
- }
- mProviderId = routeInfo.mProviderId;
+ this(routeInfo.mId, routeInfo);
}
/**
@@ -715,6 +713,7 @@
mVolumeHandling = routeInfo.mVolumeHandling;
mVolumeMax = routeInfo.mVolumeMax;
mVolume = routeInfo.mVolume;
+ mAddress = routeInfo.mAddress;
if (routeInfo.mExtras != null) {
mExtras = new Bundle(routeInfo.mExtras);
}
@@ -865,6 +864,16 @@
}
/**
+ * Sets the hardware address of the route.
+ * @hide
+ */
+ @NonNull
+ public Builder setAddress(String address) {
+ mAddress = address;
+ return this;
+ }
+
+ /**
* Sets a bundle of extras for the route.
* <p>
* Note: The extras will not affect the result of {@link MediaRoute2Info#equals(Object)}.
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 05c6e3a..908fd82 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -40,8 +40,10 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Deque;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -132,15 +134,21 @@
@Retention(RetentionPolicy.SOURCE)
public @interface Reason {}
+ private static final int MAX_REQUEST_IDS_SIZE = 500;
+
private final Handler mHandler;
private final Object mSessionLock = new Object();
+ private final Object mRequestIdsLock = new Object();
private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false);
private MediaRoute2ProviderServiceStub mStub;
private IMediaRoute2ProviderServiceCallback mRemoteCallback;
private volatile MediaRoute2ProviderInfo mProviderInfo;
+ @GuardedBy("mRequestIdsLock")
+ private final Deque<Long> mRequestIds = new ArrayDeque<>(MAX_REQUEST_IDS_SIZE);
+
@GuardedBy("mSessionLock")
- private ArrayMap<String, RoutingSessionInfo> mSessionInfo = new ArrayMap<>();
+ private final ArrayMap<String, RoutingSessionInfo> mSessionInfo = new ArrayMap<>();
public MediaRoute2ProviderService() {
mHandler = new Handler(Looper.getMainLooper());
@@ -230,6 +238,11 @@
@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+ if (requestId != REQUEST_ID_NONE && !removeRequestId(requestId)) {
+ Log.w(TAG, "notifySessionCreated: The requestId doesn't exist. requestId=" + requestId);
+ return;
+ }
+
String sessionId = sessionInfo.getId();
synchronized (mSessionLock) {
if (mSessionInfo.containsKey(sessionId)) {
@@ -322,6 +335,13 @@
if (mRemoteCallback == null) {
return;
}
+
+ if (!removeRequestId(requestId)) {
+ Log.w(TAG, "notifyRequestFailed: The requestId doesn't exist. requestId="
+ + requestId);
+ return;
+ }
+
try {
mRemoteCallback.notifyRequestFailed(requestId, reason);
} catch (RemoteException ex) {
@@ -469,6 +489,36 @@
}
}
+ /**
+ * Adds a requestId in the request ID list whose max size is {@link #MAX_REQUEST_IDS_SIZE}.
+ * When the max size is reached, the first element is removed (FIFO).
+ */
+ private void addRequestId(long requestId) {
+ synchronized (mRequestIdsLock) {
+ if (mRequestIds.size() >= MAX_REQUEST_IDS_SIZE) {
+ mRequestIds.removeFirst();
+ }
+ mRequestIds.addLast(requestId);
+ }
+ }
+
+ /**
+ * Removes the given {@code requestId} from received request ID list.
+ * <p>
+ * Returns whether the list contains the {@code requestId}. These are the cases when the list
+ * doesn't contain the given {@code requestId}:
+ * <ul>
+ * <li>This service has never received a request with the requestId. </li>
+ * <li>{@link #notifyRequestFailed} or {@link #notifySessionCreated} already has been called
+ * for the requestId. </li>
+ * </ul>
+ */
+ private boolean removeRequestId(long requestId) {
+ synchronized (mRequestIdsLock) {
+ return mRequestIds.removeFirstOccurrence(requestId);
+ }
+ }
+
final class MediaRoute2ProviderServiceStub extends IMediaRoute2ProviderService.Stub {
MediaRoute2ProviderServiceStub() { }
@@ -529,6 +579,7 @@
if (!checkRouteIdIsValid(routeId, "setRouteVolume")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetRouteVolume,
MediaRoute2ProviderService.this, requestId, routeId, volume));
}
@@ -542,6 +593,7 @@
if (!checkRouteIdIsValid(routeId, "requestCreateSession")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
MediaRoute2ProviderService.this, requestId, packageName, routeId,
requestCreateSession));
@@ -556,6 +608,7 @@
|| !checkRouteIdIsValid(routeId, "selectRoute")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@@ -569,6 +622,7 @@
|| !checkRouteIdIsValid(routeId, "deselectRoute")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onDeselectRoute,
MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@@ -582,6 +636,7 @@
|| !checkRouteIdIsValid(routeId, "transferToRoute")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferToRoute,
MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@@ -594,6 +649,7 @@
if (!checkSessionIdIsValid(sessionId, "setSessionVolume")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetSessionVolume,
MediaRoute2ProviderService.this, requestId, sessionId, volume));
}
@@ -606,6 +662,7 @@
if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
return;
}
+ addRequestId(requestId);
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
MediaRoute2ProviderService.this, requestId, sessionId));
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 9575581..638a842 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -593,11 +593,16 @@
final int failureReason = REASON_REJECTED;
final CountDownLatch onRequestFailedLatch = new CountDownLatch(1);
+ final CountDownLatch onRequestFailedSecondCallLatch = new CountDownLatch(1);
addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
public void onRequestFailed(int reason) {
if (reason == failureReason) {
- onRequestFailedLatch.countDown();
+ if (onRequestFailedLatch.getCount() > 0) {
+ onRequestFailedLatch.countDown();
+ } else {
+ onRequestFailedSecondCallLatch.countDown();
+ }
}
}
});
@@ -609,6 +614,11 @@
final long validRequestId = requestIds.get(0);
instance.notifyRequestFailed(validRequestId, failureReason);
assertTrue(onRequestFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ // Test calling notifyRequestFailed() multiple times with the same valid requestId.
+ // onRequestFailed() shouldn't be called since the requestId has been already handled.
+ instance.notifyRequestFailed(validRequestId, failureReason);
+ assertFalse(onRequestFailedSecondCallLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@Test
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index d20ab49..ab94265 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -15,7 +15,6 @@
~ limitations under the License
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <color name="nav_bar_ripple_background_color">#40ffffff</color>
<!-- colors for user switcher -->
<color name="car_user_switcher_background_color">#000000</color>
<color name="car_user_switcher_name_text_color">@*android:color/car_body1_light</color>
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 7fc69e6..e76373d 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -37,13 +37,9 @@
<item name="android:textColor">@*android:color/car_grey_50</item>
</style>
- <style name="CarNavigationBarButtonTheme">
- <item name="android:colorControlHighlight">@color/nav_bar_ripple_background_color</item>
- </style>
-
<style name="NavigationBarButton">
<item name="android:layout_height">96dp</item>
<item name="android:layout_width">96dp</item>
- <item name="android:background">@*android:drawable/item_background_material</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
</style>
</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java
index 6d140ca..7d353f5a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/BottomNotificationPanelViewMediator.java
@@ -16,6 +16,7 @@
package com.android.systemui.car.notification;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayPanelViewController;
@@ -37,6 +38,7 @@
NotificationPanelViewController notificationPanelViewController,
PowerManagerHelper powerManagerHelper,
+ BroadcastDispatcher broadcastDispatcher,
CarDeviceProvisionedController carDeviceProvisionedController,
ConfigurationController configurationController
@@ -44,6 +46,7 @@
super(carNavigationBarController,
notificationPanelViewController,
powerManagerHelper,
+ broadcastDispatcher,
carDeviceProvisionedController,
configurationController);
notificationPanelViewController.setOverlayDirection(
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
index 41349b2..0c185ba 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
@@ -17,10 +17,17 @@
package com.android.systemui.car.notification;
import android.car.hardware.power.CarPowerManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
+import android.os.UserHandle;
+import android.util.Log;
import androidx.annotation.CallSuper;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayViewMediator;
@@ -37,18 +44,36 @@
public class NotificationPanelViewMediator implements OverlayViewMediator,
ConfigurationController.ConfigurationListener {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "NotificationPanelVM";
+
private final CarNavigationBarController mCarNavigationBarController;
private final NotificationPanelViewController mNotificationPanelViewController;
private final PowerManagerHelper mPowerManagerHelper;
+ private final BroadcastDispatcher mBroadcastDispatcher;
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final ConfigurationController mConfigurationController;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) Log.v(TAG, "onReceive: " + intent);
+ String action = intent.getAction();
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ if (mNotificationPanelViewController.isPanelExpanded()) {
+ mNotificationPanelViewController.toggle();
+ }
+ }
+ }
+ };
+
@Inject
public NotificationPanelViewMediator(
CarNavigationBarController carNavigationBarController,
NotificationPanelViewController notificationPanelViewController,
PowerManagerHelper powerManagerHelper,
+ BroadcastDispatcher broadcastDispatcher,
CarDeviceProvisionedController carDeviceProvisionedController,
ConfigurationController configurationController
@@ -56,6 +81,7 @@
mCarNavigationBarController = carNavigationBarController;
mNotificationPanelViewController = notificationPanelViewController;
mPowerManagerHelper = powerManagerHelper;
+ mBroadcastDispatcher = broadcastDispatcher;
mCarDeviceProvisionedController = carDeviceProvisionedController;
mConfigurationController = configurationController;
}
@@ -84,6 +110,9 @@
return mNotificationPanelViewController.isPanelExpanded();
}
});
+
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), null, UserHandle.ALL);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
index 8d3eb4c..89c9931 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
@@ -16,6 +16,7 @@
package com.android.systemui.car.notification;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayPanelViewController;
@@ -37,6 +38,7 @@
NotificationPanelViewController notificationPanelViewController,
PowerManagerHelper powerManagerHelper,
+ BroadcastDispatcher broadcastDispatcher,
CarDeviceProvisionedController carDeviceProvisionedController,
ConfigurationController configurationController
@@ -44,6 +46,7 @@
super(carNavigationBarController,
notificationPanelViewController,
powerManagerHelper,
+ broadcastDispatcher,
carDeviceProvisionedController,
configurationController);
notificationPanelViewController.setOverlayDirection(
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 132922a..f42bf19 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -95,7 +95,8 @@
public String docId;
public File visiblePath;
public File path;
- public boolean reportAvailableBytes = true;
+ // TODO (b/157033915): Make getFreeBytes() faster
+ public boolean reportAvailableBytes = false;
}
private static final String ROOT_ID_PRIMARY_EMULATED =
diff --git a/packages/OsuLogin/Android.bp b/packages/OsuLogin/Android.bp
index d7e36b1..445c81b 100644
--- a/packages/OsuLogin/Android.bp
+++ b/packages/OsuLogin/Android.bp
@@ -1,5 +1,6 @@
android_app {
name: "OsuLogin",
+ defaults: ["wifi-module-sdk-version-defaults"],
static_libs: ["androidx.legacy_legacy-support-v4"],
resource_dirs: ["res"],
srcs: ["src/**/*.java"],
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index b83a9c4..6c7e03f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -56,7 +56,7 @@
public class InfoMediaManager extends MediaManager {
private static final String TAG = "InfoMediaManager";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@VisibleForTesting
final RouterManagerCallback mMediaRouterCallback = new RouterManagerCallback();
@VisibleForTesting
@@ -430,7 +430,7 @@
case TYPE_HEARING_AID:
case TYPE_BLUETOOTH_A2DP:
final BluetoothDevice device =
- BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getOriginalId());
+ BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getAddress());
final CachedBluetoothDevice cachedDevice =
mBluetoothManager.getCachedDeviceManager().findDevice(device);
if (cachedDevice != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index f381cde..9d06c84 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -20,7 +20,6 @@
import android.app.Notification;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.RoutingSessionInfo;
import android.text.TextUtils;
@@ -409,7 +408,8 @@
synchronized (mMediaDevicesLock) {
for (MediaDevice device : mMediaDevices) {
if (device instanceof BluetoothMediaDevice) {
- if (isActiveDevice(((BluetoothMediaDevice) device).getCachedDevice())) {
+ if (isActiveDevice(((BluetoothMediaDevice) device).getCachedDevice())
+ && device.isConnected()) {
return device;
}
} else if (device instanceof PhoneMediaDevice) {
@@ -422,8 +422,22 @@
}
private boolean isActiveDevice(CachedBluetoothDevice device) {
- return device.isActiveDevice(BluetoothProfile.A2DP)
- || device.isActiveDevice(BluetoothProfile.HEARING_AID);
+ boolean isActiveDeviceA2dp = false;
+ boolean isActiveDeviceHearingAid = false;
+ final A2dpProfile a2dpProfile = mLocalBluetoothManager.getProfileManager().getA2dpProfile();
+ if (a2dpProfile != null) {
+ isActiveDeviceA2dp = device.getDevice().equals(a2dpProfile.getActiveDevice());
+ }
+ if (!isActiveDeviceA2dp) {
+ final HearingAidProfile hearingAidProfile = mLocalBluetoothManager.getProfileManager()
+ .getHearingAidProfile();
+ if (hearingAidProfile != null) {
+ isActiveDeviceHearingAid =
+ hearingAidProfile.getActiveDevices().contains(device.getDevice());
+ }
+ }
+
+ return isActiveDeviceA2dp || isActiveDeviceHearingAid;
}
private Collection<DeviceCallback> getCallbacks() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 248eb5b..94d95f0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -681,7 +681,7 @@
assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
- when(route2Info.getOriginalId()).thenReturn("00:00:00:00:00:00");
+ when(route2Info.getAddress()).thenReturn("00:00:00:00:00:00");
when(mLocalBluetoothManager.getCachedDeviceManager())
.thenReturn(cachedBluetoothDeviceManager);
when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
@@ -703,7 +703,7 @@
mock(CachedBluetoothDeviceManager.class);
when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
- when(route2Info.getOriginalId()).thenReturn("00:00:00:00:00:00");
+ when(route2Info.getAddress()).thenReturn("00:00:00:00:00:00");
when(mLocalBluetoothManager.getCachedDeviceManager())
.thenReturn(cachedBluetoothDeviceManager);
when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 368245f..a654fd4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -578,6 +578,9 @@
when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(cachedDevice.isConnected()).thenReturn(false);
when(cachedDevice.getConnectableProfiles()).thenReturn(profiles);
+ when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(bluetoothDevice);
+ when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>());
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
@@ -734,11 +737,19 @@
final PhoneMediaDevice phoneDevice = mock(PhoneMediaDevice.class);
final CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class);
final CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
+ final BluetoothDevice bluetoothDevice1 = mock(BluetoothDevice.class);
+ final BluetoothDevice bluetoothDevice2 = mock(BluetoothDevice.class);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(bluetoothDevice2);
+ when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>());
when(device1.getCachedDevice()).thenReturn(cachedDevice1);
when(device2.getCachedDevice()).thenReturn(cachedDevice2);
+ when(cachedDevice1.getDevice()).thenReturn(bluetoothDevice1);
+ when(cachedDevice2.getDevice()).thenReturn(bluetoothDevice2);
when(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
when(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
+ when(device1.isConnected()).thenReturn(true);
+ when(device2.isConnected()).thenReturn(true);
mLocalMediaManager.mMediaDevices.add(device1);
mLocalMediaManager.mMediaDevices.add(phoneDevice);
@@ -754,11 +765,19 @@
final PhoneMediaDevice phoneDevice = mock(PhoneMediaDevice.class);
final CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class);
final CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
+ final BluetoothDevice bluetoothDevice1 = mock(BluetoothDevice.class);
+ final BluetoothDevice bluetoothDevice2 = mock(BluetoothDevice.class);
+ when(mA2dpProfile.getActiveDevice()).thenReturn(null);
+ when(mHapProfile.getActiveDevices()).thenReturn(new ArrayList<>());
when(device1.getCachedDevice()).thenReturn(cachedDevice1);
when(device2.getCachedDevice()).thenReturn(cachedDevice2);
+ when(cachedDevice1.getDevice()).thenReturn(bluetoothDevice1);
+ when(cachedDevice2.getDevice()).thenReturn(bluetoothDevice2);
when(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
when(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(false);
+ when(device1.isConnected()).thenReturn(true);
+ when(device2.isConnected()).thenReturn(true);
mLocalMediaManager.mMediaDevices.add(device1);
mLocalMediaManager.mMediaDevices.add(phoneDevice);
diff --git a/packages/SystemUI/docs/broadcasts.md b/packages/SystemUI/docs/broadcasts.md
index 6c8488b..8ec20f5 100644
--- a/packages/SystemUI/docs/broadcasts.md
+++ b/packages/SystemUI/docs/broadcasts.md
@@ -62,7 +62,7 @@
* @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
* executor in the main thread (default).
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the current user.
+ * By default, it is the user of the context (system user in SystemUI).
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 6a23521..ef7325e 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -48,4 +48,18 @@
android:visibility="gone"
android:pointerIcon="crosshair"/>
<include layout="@layout/global_screenshot_static"/>
+ <FrameLayout
+ android:id="@+id/global_screenshot_dismiss_button"
+ android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
+ android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
+ android:elevation="7dp"
+ android:visibility="gone"
+ android:contentDescription="@string/screenshot_dismiss_ui_description">
+ <ImageView
+ android:id="@+id/global_screenshot_dismiss_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/screenshot_dismiss_button_margin"
+ android:src="@drawable/screenshot_cancel"/>
+ </FrameLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml
index da5277c..9ec2f20 100644
--- a/packages/SystemUI/res/layout/global_screenshot_static.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_static.xml
@@ -54,22 +54,4 @@
android:layout_height="wrap_content"/>
</HorizontalScrollView>
<include layout="@layout/global_screenshot_preview"/>
- <FrameLayout
- android:id="@+id/global_screenshot_dismiss_button"
- android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
- android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
- android:elevation="7dp"
- android:visibility="gone"
- android:contentDescription="@string/screenshot_dismiss_ui_description"
- app:layout_constraintStart_toEndOf="@+id/global_screenshot_preview"
- app:layout_constraintEnd_toEndOf="@+id/global_screenshot_preview"
- app:layout_constraintTop_toTopOf="@+id/global_screenshot_preview"
- app:layout_constraintBottom_toTopOf="@+id/global_screenshot_preview">
- <ImageView
- android:id="@+id/global_screenshot_dismiss_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_margin="@dimen/screenshot_dismiss_button_margin"
- android:src="@drawable/screenshot_cancel"/>
- </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
index 21a671c..214c44a 100644
--- a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
@@ -20,24 +20,26 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|start"
+ android:paddingTop="1dp"
+ android:paddingBottom="1dp"
android:paddingEnd="12dp">
<FrameLayout
android:layout_width="@*android:dimen/conversation_content_start"
- android:layout_height="36dp"
+ android:layout_height="25dp"
>
<ImageView
android:id="@*android:id/conversation_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="20dp"
+ android:layout_height="20dp"
android:layout_gravity="center"
/>
<ViewStub
android:id="@*android:id/conversation_face_pile"
android:layout="@*android:layout/conversation_face_pile_layout"
- android:layout_width="36dp"
- android:layout_height="36dp"
+ android:layout_width="25dp"
+ android:layout_height="25dp"
android:layout_gravity="center"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index abc560b..44d3b02 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -631,10 +631,10 @@
<dimen name="notification_section_divider_height">@dimen/notification_side_paddings</dimen>
<!-- Size of the face pile shown on one-line (children of a group) conversation notifications -->
- <dimen name="conversation_single_line_face_pile_size">36dp</dimen>
+ <dimen name="conversation_single_line_face_pile_size">25dp</dimen>
<!-- Size of an avatar shown on one-line (children of a group) conversation notifications -->
- <dimen name="conversation_single_line_avatar_size">24dp</dimen>
+ <dimen name="conversation_single_line_avatar_size">20dp</dimen>
<!-- Border width for avatars in the face pile shown on one-line (children of a group) conversation notifications -->
<dimen name="conversation_single_line_face_pile_protection_width">1dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index ebed1fc..27e4c85 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -63,8 +63,9 @@
*/
private final class InputEventReceiver extends BatchedInputEventReceiver {
- public InputEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper, Choreographer.getInstance());
+ InputEventReceiver(InputChannel inputChannel, Looper looper,
+ Choreographer choreographer) {
+ super(inputChannel, looper, choreographer);
}
@Override
@@ -143,6 +144,14 @@
* Registers the input consumer.
*/
public void registerInputConsumer() {
+ registerInputConsumer(false);
+ }
+
+ /**
+ * Registers the input consumer.
+ * @param withSfVsync the flag set using sf vsync signal or no
+ */
+ public void registerInputConsumer(boolean withSfVsync) {
if (mInputEventReceiver == null) {
final InputChannel inputChannel = new InputChannel();
try {
@@ -152,7 +161,8 @@
} catch (RemoteException e) {
Log.e(TAG, "Failed to create input consumer", e);
}
- mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper());
+ mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper(),
+ withSfVsync ? Choreographer.getSfInstance() : Choreographer.getInstance());
if (mRegistrationListener != null) {
mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ee31706..34c8587 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1903,6 +1903,12 @@
private boolean shouldListenForFingerprint() {
final boolean allowedOnBouncer =
!(mFingerprintLockedOut && mBouncer && mCredentialAttempted);
+ final int user = getCurrentUser();
+ final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
+ final boolean isLockDown =
+ containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
+ || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -1911,7 +1917,7 @@
shouldListenForFingerprintAssistant() || (mKeyguardOccluded && mIsDreaming))
&& !mSwitchingUser && !isFingerprintDisabled(getCurrentUser())
&& (!mKeyguardGoingAway || !mDeviceInteractive) && mIsPrimaryUser
- && allowedOnBouncer;
+ && allowedOnBouncer && !isLockDown && !isEncrypted;
return shouldListen;
}
@@ -1928,9 +1934,10 @@
final boolean isLockDown =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- final boolean isEncryptedOrTimedOut =
- containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
- || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+ final boolean isEncrypted =
+ containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ final boolean isTimedOut =
+ containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
@@ -1939,10 +1946,9 @@
// TrustAgents or biometrics are keeping the device unlocked.
boolean becauseCannotSkipBouncer = !getUserCanSkipBouncer(user) || canBypass;
- // Scan even when encrypted or timeout to show a preemptive bouncer when bypassing.
+ // Scan even when timeout to show a preemptive bouncer when bypassing.
// Lock-down mode shouldn't scan, since it is more explicit.
- boolean strongAuthAllowsScanning = (!isEncryptedOrTimedOut || canBypass && !mBouncer)
- && !isLockDown;
+ boolean strongAuthAllowsScanning = (!isTimedOut || canBypass && !mBouncer);
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -1952,7 +1958,7 @@
&& !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer
&& !mKeyguardGoingAway && mFaceSettingEnabledForUser.get(user) && !mLockIconPressed
&& strongAuthAllowsScanning && mIsPrimaryUser
- && !mSecureCameraLaunched;
+ && !mSecureCameraLaunched && !isLockDown && !isEncrypted;
// Aggregate relevant fields for debug logging.
if (DEBUG_FACE || DEBUG_SPEW) {
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index 4269605..67c0c62 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -16,8 +16,10 @@
package com.android.systemui.broadcast
+import android.app.ActivityManager
import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.HandlerExecutor
@@ -28,15 +30,11 @@
import android.util.SparseArray
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import java.io.FileDescriptor
import java.io.PrintWriter
-import java.lang.IllegalStateException
import java.util.concurrent.Executor
-import javax.inject.Inject
-import javax.inject.Singleton
data class ReceiverData(
val receiver: BroadcastReceiver,
@@ -48,6 +46,8 @@
private const val MSG_ADD_RECEIVER = 0
private const val MSG_REMOVE_RECEIVER = 1
private const val MSG_REMOVE_RECEIVER_FOR_USER = 2
+private const val MSG_USER_SWITCH = 3
+private const val MSG_SET_STARTING_USER = 99
private const val TAG = "BroadcastDispatcher"
private const val DEBUG = true
@@ -62,20 +62,27 @@
* permissions, schemes, data types, data authorities or priority different than 0.
* Cannot be used for getting sticky broadcasts (either as return of registering or as re-delivery).
*/
-@Singleton
-open class BroadcastDispatcher @Inject constructor (
+open class BroadcastDispatcher constructor (
private val context: Context,
- @Main private val mainHandler: Handler,
- @Background private val bgLooper: Looper,
- dumpManager: DumpManager
-) : Dumpable {
+ private val bgLooper: Looper,
+ private val dumpManager: DumpManager,
+ private val logger: BroadcastDispatcherLogger
+) : Dumpable, BroadcastReceiver() {
// Only modify in BG thread
private val receiversByUser = SparseArray<UserBroadcastDispatcher>(20)
- init {
- // TODO: Don't do this in the constructor
+ fun initialize() {
dumpManager.registerDumpable(javaClass.name, this)
+ handler.sendEmptyMessage(MSG_SET_STARTING_USER)
+ registerReceiver(this, IntentFilter(Intent.ACTION_USER_SWITCHED), null, UserHandle.ALL)
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == Intent.ACTION_USER_SWITCHED) {
+ val user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)
+ handler.obtainMessage(MSG_USER_SWITCH, user, 0).sendToTarget()
+ }
}
/**
@@ -87,7 +94,7 @@
* have at least one action.
* @param handler A handler to dispatch [BroadcastReceiver.onReceive].
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the current user.
+ * By default, it is the user of the context (system user in SystemUI).
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
@@ -113,7 +120,7 @@
* @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
* executor in the main thread (default).
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
- * By default, it is the current user.
+ * By default, it is the user of the context (system user in SystemUI).
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
@@ -156,7 +163,7 @@
/**
* Unregister receiver for a particular user.
*
- * @param receiver The receiver to unregister. It will be unregistered for all users.
+ * @param receiver The receiver to unregister.
* @param user The user associated to the registered [receiver]. It can be [UserHandle.ALL].
*/
open fun unregisterReceiverForUser(receiver: BroadcastReceiver, user: UserHandle) {
@@ -166,10 +173,11 @@
@VisibleForTesting
protected open fun createUBRForUser(userId: Int) =
- UserBroadcastDispatcher(context, userId, bgLooper)
+ UserBroadcastDispatcher(context, userId, bgLooper, logger)
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.println("Broadcast dispatcher:")
+ pw.println(" Current user: ${handler.currentUser}")
for (index in 0 until receiversByUser.size()) {
pw.println(" User ${receiversByUser.keyAt(index)}")
receiversByUser.valueAt(index).dump(fd, pw, args)
@@ -177,6 +185,8 @@
}
private val handler = object : Handler(bgLooper) {
+ var currentUser = UserHandle.USER_SYSTEM
+
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_ADD_RECEIVER -> {
@@ -184,7 +194,7 @@
// If the receiver asked to be registered under the current user, we register
// under the actual current user.
val userId = if (data.user.identifier == UserHandle.USER_CURRENT) {
- context.userId
+ currentUser
} else {
data.user.identifier
}
@@ -207,6 +217,13 @@
receiversByUser.get(msg.arg1)?.unregisterReceiver(msg.obj as BroadcastReceiver)
}
+ MSG_USER_SWITCH -> {
+ currentUser = msg.arg1
+ }
+ MSG_SET_STARTING_USER -> {
+ currentUser = ActivityManager.getCurrentUser()
+ }
+
else -> super.handleMessage(msg)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
index 3272fb7..96f5a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
@@ -30,6 +30,7 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.util.Preconditions
import com.android.systemui.Dumpable
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import java.io.FileDescriptor
import java.io.PrintWriter
import java.lang.IllegalArgumentException
@@ -54,7 +55,8 @@
class UserBroadcastDispatcher(
private val context: Context,
private val userId: Int,
- private val bgLooper: Looper
+ private val bgLooper: Looper,
+ private val logger: BroadcastDispatcherLogger
) : BroadcastReceiver(), Dumpable {
companion object {
@@ -109,10 +111,12 @@
}
override fun onReceive(context: Context, intent: Intent) {
- val id = if (DEBUG) index.getAndIncrement() else 0
+ val id = index.getAndIncrement()
if (DEBUG) Log.w(TAG, "[$id] Received $intent")
+ logger.logBroadcastReceived(id, userId, intent)
bgHandler.post(
- HandleBroadcastRunnable(actionsToReceivers, context, intent, pendingResult, id))
+ HandleBroadcastRunnable(
+ actionsToReceivers, context, intent, pendingResult, id, logger))
}
/**
@@ -143,6 +147,7 @@
ArraySet()
}.add(receiverData)
}
+ logger.logReceiverRegistered(userId, receiverData.receiver)
if (changed) {
createFilterAndRegisterReceiverBG()
}
@@ -163,6 +168,7 @@
actionsToReceivers.remove(action)
}
}
+ logger.logReceiverUnregistered(userId, receiver)
if (changed) {
createFilterAndRegisterReceiverBG()
}
@@ -187,7 +193,8 @@
val context: Context,
val intent: Intent,
val pendingResult: PendingResult,
- val index: Int
+ val index: Int,
+ val logger: BroadcastDispatcherLogger
) : Runnable {
override fun run() {
if (DEBUG) Log.w(TAG, "[$index] Dispatching $intent")
@@ -199,6 +206,7 @@
it.executor.execute {
if (DEBUG) Log.w(TAG,
"[$index] Dispatching ${intent.action} to ${it.receiver}")
+ logger.logBroadcastDispatched(index, intent.action, it.receiver)
it.receiver.pendingResult = pendingResult
it.receiver.onReceive(context, intent)
}
@@ -215,6 +223,7 @@
if (registered.get()) {
try {
context.unregisterReceiver(this@UserBroadcastDispatcher)
+ logger.logContextReceiverUnregistered(userId)
} catch (e: IllegalArgumentException) {
Log.e(TAG, "Trying to unregister unregistered receiver for user $userId",
IllegalStateException(e))
@@ -230,6 +239,7 @@
null,
bgHandler)
registered.set(true)
+ logger.logContextReceiverRegistered(userId, intentFilter)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
new file mode 100644
index 0000000..123a8ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.broadcast.logging
+
+import android.content.BroadcastReceiver
+import android.content.Intent
+import android.content.IntentFilter
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.LogMessage
+import com.android.systemui.log.dagger.BroadcastDispatcherLog
+import javax.inject.Inject
+
+private const val TAG = "BroadcastDispatcherLog"
+
+class BroadcastDispatcherLogger @Inject constructor(
+ @BroadcastDispatcherLog private val buffer: LogBuffer
+) {
+
+ fun logBroadcastReceived(broadcastId: Int, user: Int, intent: Intent) {
+ val intentString = intent.toString()
+ log(INFO, {
+ int1 = broadcastId
+ int2 = user
+ str1 = intentString
+ }, {
+ "[$int1] Broadcast received for user $int2: $str1"
+ })
+ }
+
+ fun logBroadcastDispatched(broadcastId: Int, action: String?, receiver: BroadcastReceiver) {
+ val receiverString = receiver.toString()
+ log(DEBUG, {
+ int1 = broadcastId
+ str1 = action
+ str2 = receiverString
+ }, {
+ "Broadcast $int1 ($str1) dispatched to $str2"
+ })
+ }
+
+ fun logReceiverRegistered(user: Int, receiver: BroadcastReceiver) {
+ val receiverString = receiver.toString()
+ log(INFO, {
+ int1 = user
+ str1 = receiverString
+ }, {
+ "Receiver $str1 registered for user $int1"
+ })
+ }
+
+ fun logReceiverUnregistered(user: Int, receiver: BroadcastReceiver) {
+ val receiverString = receiver.toString()
+ log(INFO, {
+ int1 = user
+ str1 = receiverString
+ }, {
+ "Receiver $str1 unregistered for user $int1"
+ })
+ }
+
+ fun logContextReceiverRegistered(user: Int, filter: IntentFilter) {
+ val actions = filter.actionsIterator().asSequence()
+ .joinToString(separator = ",", prefix = "Actions(", postfix = ")")
+ val categories = if (filter.countCategories() != 0) {
+ filter.categoriesIterator().asSequence()
+ .joinToString(separator = ",", prefix = "Categories(", postfix = ")")
+ } else {
+ ""
+ }
+ log(INFO, {
+ int1 = user
+ str1 = if (categories != "") {
+ "${actions}\n$categories"
+ } else {
+ actions
+ }
+ }, {
+ """
+ Receiver registered with Context for user $int1.
+ $str1
+ """.trimIndent()
+ })
+ }
+
+ fun logContextReceiverUnregistered(user: Int) {
+ log(INFO, {
+ int1 = user
+ }, {
+ "Receiver unregistered with Context for user $int1."
+ })
+ }
+
+ private inline fun log(
+ logLevel: LogLevel,
+ initializer: LogMessage.() -> Unit,
+ noinline printer: LogMessage.() -> String
+ ) {
+ buffer.log(TAG, logLevel, initializer, printer)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 2affe06..c4c5da4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -55,7 +55,6 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.os.Binder;
import android.os.Handler;
import android.os.RemoteException;
@@ -180,6 +179,9 @@
// Callback that updates BubbleOverflowActivity on data change.
@Nullable private Runnable mOverflowCallback = null;
+ // Only load overflow data from disk once
+ private boolean mOverflowDataLoaded = false;
+
private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private IStatusBarService mBarService;
private WindowManager mWindowManager;
@@ -193,9 +195,6 @@
/** Whether or not the BubbleStackView has been added to the WindowManager. */
private boolean mAddedToWindowManager = false;
- // Used for determining view rect for touch interaction
- private Rect mTempRect = new Rect();
-
// Listens to user switch so bubbles can be saved and restored.
private final NotificationLockscreenUserManager mNotifUserManager;
@@ -715,6 +714,9 @@
* the new params if the stack has been added.
*/
private void updateWmFlags() {
+ if (mStackView == null) {
+ return;
+ }
if (isStackExpanded() && !mImeVisible) {
// If we're expanded, and the IME isn't visible, we want to be focusable. This ensures
// that any taps within Bubbles (including on the ActivityView) results in Bubbles
@@ -726,7 +728,7 @@
mWmLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
}
- if (mStackView != null && mAddedToWindowManager) {
+ if (mAddedToWindowManager) {
try {
mWindowManager.updateViewLayout(mStackView, mWmLayoutParams);
} catch (IllegalArgumentException e) {
@@ -911,7 +913,7 @@
mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
bubble.setInflateSynchronously(mInflateSynchronously);
bubble.setShouldAutoExpand(true);
- bubble.markUpdatedAt(System.currentTimeMillis());
+ bubble.markAsAccessedAt(System.currentTimeMillis());
setIsBubble(bubble, true /* isBubble */);
}
@@ -962,13 +964,14 @@
* Fills the overflow bubbles by loading them from disk.
*/
void loadOverflowBubblesFromDisk() {
- if (!mBubbleData.getOverflowBubbles().isEmpty()) {
+ if (!mBubbleData.getOverflowBubbles().isEmpty() || mOverflowDataLoaded) {
// we don't need to load overflow bubbles from disk if it is already in memory
return;
}
+ mOverflowDataLoaded = true;
mDataRepository.loadBubbles((bubbles) -> {
bubbles.forEach(bubble -> {
- if (mBubbleData.getBubbles().contains(bubble)) {
+ if (mBubbleData.hasAnyBubbleWithKey(bubble.getKey())) {
// if the bubble is already active, there's no need to push it to overflow
return;
}
@@ -1245,24 +1248,23 @@
}
mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);
- if (update.addedBubble != null) {
+ if (update.addedBubble != null && mStackView != null) {
mDataRepository.addBubble(mCurrentUserId, update.addedBubble);
mStackView.addBubble(update.addedBubble);
-
}
- if (update.updatedBubble != null) {
+ if (update.updatedBubble != null && mStackView != null) {
mStackView.updateBubble(update.updatedBubble);
}
// At this point, the correct bubbles are inflated in the stack.
// Make sure the order in bubble data is reflected in bubble row.
- if (update.orderChanged) {
+ if (update.orderChanged && mStackView != null) {
mDataRepository.addBubbles(mCurrentUserId, update.bubbles);
mStackView.updateBubbleOrder(update.bubbles);
}
- if (update.selectionChanged) {
+ if (update.selectionChanged && mStackView != null) {
mStackView.setSelectedBubble(update.selectedBubble);
if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) {
mNotificationGroupManager.updateSuppression(
@@ -1272,7 +1274,9 @@
// Expanding? Apply this last.
if (update.expandedChanged && update.expanded) {
- mStackView.setExpanded(true);
+ if (mStackView != null) {
+ mStackView.setExpanded(true);
+ }
}
for (NotifCallback cb : mCallbacks) {
@@ -1378,7 +1382,6 @@
}
/**
- * Lets any listeners know if bubble state has changed.
* Updates the visibility of the bubbles based on current state.
* Does not un-bubble, just hides or un-hides.
* Updates stack description for TalkBack focus.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 24d44d5..20a9a8c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -417,7 +417,8 @@
if (mBubbles.size() == 1) {
// Going to become empty, handle specially.
setExpandedInternal(false);
- setSelectedBubbleInternal(null);
+ // Don't use setSelectedBubbleInternal because we don't want to trigger an applyUpdate
+ mSelectedBubble = null;
}
if (indexToRemove < mBubbles.size() - 1) {
// Removing anything but the last bubble means positions will change.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index ec875f8..47e6645 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -350,7 +350,10 @@
// ActivityView's vertical bounds. These events are part of a back gesture, and so they
// should not collapse the stack (which all other touches on areas around the AV would
// do).
- if (motionEvent.getRawY() >= avBounds.top && motionEvent.getRawY() <= avBounds.bottom) {
+ if (motionEvent.getRawY() >= avBounds.top
+ && motionEvent.getRawY() <= avBounds.bottom
+ && (motionEvent.getRawX() < avBounds.left
+ || motionEvent.getRawX() > avBounds.right)) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 1437501..088747b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -93,7 +93,6 @@
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.RelativeTouchListener;
@@ -1515,7 +1514,9 @@
// expanded view becomes visible on the screen. See b/126856255
mExpandedViewContainer.setAlpha(0.0f);
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
- previouslySelected.setContentVisibility(false);
+ if (previouslySelected != null) {
+ previouslySelected.setContentVisibility(false);
+ }
updateExpandedBubble();
requestUpdate();
@@ -1886,9 +1887,11 @@
.withEndActions(this::releaseAnimatingOutBubbleBuffer)
.start();
+ boolean isOverflow = mExpandedBubble != null
+ && mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
float expandingFromBubbleDestinationX =
- mExpandedAnimationController.getBubbleLeft(
- mBubbleData.getBubbles().indexOf(mExpandedBubble));
+ mExpandedAnimationController.getBubbleLeft(isOverflow ? getBubbleCount()
+ : mBubbleData.getBubbles().indexOf(mExpandedBubble));
mExpandedViewContainer.setAlpha(1f);
mExpandedViewContainer.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 8368b2c..1f30305 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -25,6 +25,7 @@
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Looper;
import android.os.ServiceManager;
import android.util.DisplayMetrics;
import android.view.Choreographer;
@@ -39,9 +40,12 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Prefs;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.PluginInitializerImpl;
import com.android.systemui.shared.plugins.PluginManager;
@@ -178,6 +182,21 @@
return ActivityManagerWrapper.getInstance();
}
+ /** Provides and initializes the {#link BroadcastDispatcher} for SystemUI */
+ @Singleton
+ @Provides
+ public BroadcastDispatcher providesBroadcastDispatcher(
+ Context context,
+ @Background Looper backgroundLooper,
+ DumpManager dumpManager,
+ BroadcastDispatcherLogger logger
+ ) {
+ BroadcastDispatcher bD =
+ new BroadcastDispatcher(context, backgroundLooper, dumpManager, logger);
+ bD.initialize();
+ return bD;
+ }
+
@Singleton
@Provides
public DevicePolicyManagerWrapper provideDevicePolicyManagerWrapper() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b26dc5f..53251ed43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -654,7 +654,7 @@
@Override
public void onBouncerVisiblityChanged(boolean shown) {
synchronized (KeyguardViewMediator.this) {
- adjustStatusBarLocked(shown);
+ adjustStatusBarLocked(shown, false);
}
}
@@ -2003,10 +2003,12 @@
}
private void adjustStatusBarLocked() {
- adjustStatusBarLocked(false /* forceHideHomeRecentsButtons */);
+ adjustStatusBarLocked(false /* forceHideHomeRecentsButtons */,
+ false /* forceClearFlags */);
}
- private void adjustStatusBarLocked(boolean forceHideHomeRecentsButtons) {
+ private void adjustStatusBarLocked(boolean forceHideHomeRecentsButtons,
+ boolean forceClearFlags) {
if (mStatusBarManager == null) {
mStatusBarManager = (StatusBarManager)
mContext.getSystemService(Context.STATUS_BAR_SERVICE);
@@ -2018,6 +2020,13 @@
// Disable aspects of the system/status/navigation bars that must not be re-enabled by
// windows that appear on top, ever
int flags = StatusBarManager.DISABLE_NONE;
+
+ // TODO (b/155663717) After restart, status bar will not properly hide home button
+ // unless disable is called to show un-hide it once first
+ if (forceClearFlags) {
+ mStatusBarManager.disable(flags);
+ }
+
if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
if (!mShowHomeOverLockscreen || !mInGestureNavigationMode) {
flags |= StatusBarManager.DISABLE_HOME;
@@ -2141,6 +2150,7 @@
public void onBootCompleted() {
synchronized (this) {
mBootCompleted = true;
+ adjustStatusBarLocked(false, true);
if (mBootSendUserPresent) {
sendUserPresentBroadcast();
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java
new file mode 100644
index 0000000..7d1f1c2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BroadcastDispatcherLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for BroadcastDispatcher-related messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface BroadcastDispatcherLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 9c89fee..a2086e8 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -97,6 +97,18 @@
return buffer;
}
+ /** Provides a logging buffer for {@link com.android.systemui.broadcast.BroadcastDispatcher} */
+ @Provides
+ @Singleton
+ @BroadcastDispatcherLog
+ public static LogBuffer provideBroadcastDispatcherLogBuffer(
+ LogcatEchoTracker bufferFilter,
+ DumpManager dumpManager) {
+ LogBuffer buffer = new LogBuffer("BroadcastDispatcherLog", 500, 10, bufferFilter);
+ buffer.attach(dumpManager);
+ return buffer;
+ }
+
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
@Provides
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index 7f7e108..2980f11 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -16,6 +16,7 @@
package com.android.systemui.pip;
+import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -26,6 +27,7 @@
import android.view.animation.Interpolator;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -79,6 +81,13 @@
private PipTransitionAnimator mCurrentAnimator;
+ private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
+ ThreadLocal.withInitial(() -> {
+ AnimationHandler handler = new AnimationHandler();
+ handler.setProvider(new SfVsyncFrameCallbackProvider());
+ return handler;
+ });
+
@Inject
PipAnimationController(Context context, PipSurfaceTransactionHelper helper) {
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -135,6 +144,7 @@
animator.setSurfaceTransactionHelper(mSurfaceTransactionHelper);
animator.setInterpolator(mFastOutSlowInInterpolator);
animator.setFloatValues(FRACTION_START, FRACTION_END);
+ animator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
return animator;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index b714bff..e38bfb4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -269,7 +269,7 @@
if (stackInfo != null) {
// If SystemUI restart, and it already existed a pinned stack,
// register the pip input consumer to ensure touch can send to it.
- mInputConsumerController.registerInputConsumer();
+ mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
}
} catch (RemoteException | UnsupportedOperationException e) {
e.printStackTrace();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 8b4d932..31d292f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -197,7 +197,7 @@
}
public void onActivityPinned() {
- mInputConsumerController.registerInputConsumer();
+ mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
}
public void onActivityUnpinned() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 2e75bab..d077666 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -23,9 +23,11 @@
import android.graphics.Rect;
import android.os.Debug;
import android.util.Log;
+import android.view.Choreographer;
import androidx.dynamicanimation.animation.SpringForce;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.util.FloatingContentCoordinator;
@@ -68,6 +70,9 @@
/** The region that all of PIP must stay within. */
private final Rect mFloatingAllowedArea = new Rect();
+ private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider =
+ new SfVsyncFrameCallbackProvider();
+
/**
* Bounds that are animated using the physics animator.
*/
@@ -79,6 +84,10 @@
/** Coordinator instance for resolving conflicts with other floating content. */
private FloatingContentCoordinator mFloatingContentCoordinator;
+ /** Callback that re-sizes PIP to the animated bounds. */
+ private final Choreographer.FrameCallback mResizePipVsyncCallback =
+ l -> resizePipUnchecked(mAnimatedBounds);
+
/**
* PhysicsAnimator instance for animating {@link #mAnimatedBounds} using physics animations.
*/
@@ -89,7 +98,7 @@
* Update listener that resizes the PIP to {@link #mAnimatedBounds}.
*/
final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
- (target, values) -> resizePipUnchecked(mAnimatedBounds);
+ (target, values) -> mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback);
/** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
private PhysicsAnimator.FlingConfig mFlingConfigX;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index bafbd21..f6b212c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -32,6 +32,8 @@
import android.os.Handler;
import android.os.Looper;
import android.provider.DeviceConfig;
+import android.view.BatchedInputEventReceiver;
+import android.view.Choreographer;
import android.view.InputChannel;
import android.view.InputEvent;
import android.view.InputEventReceiver;
@@ -323,9 +325,9 @@
mMinSize.set(minX, minY);
}
- class SysUiInputEventReceiver extends InputEventReceiver {
+ class SysUiInputEventReceiver extends BatchedInputEventReceiver {
SysUiInputEventReceiver(InputChannel channel, Looper looper) {
- super(channel, looper);
+ super(channel, looper, Choreographer.getSfInstance());
}
public void onInputEvent(InputEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 57436bc..7babe2f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -120,7 +120,6 @@
public Consumer<Uri> finisher;
public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
public int errorMsgResId;
- public boolean createDeleteAction;
void clearImage() {
image = null;
@@ -174,6 +173,8 @@
private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
+ // delay before starting to fade in dismiss button
+ private static final long SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS = 200;
private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
@@ -271,7 +272,8 @@
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
PixelFormat.TRANSLUCENT);
mWindowLayoutParams.setTitle("ScreenshotAnimation");
mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -437,7 +439,6 @@
data.image = mScreenBitmap;
data.finisher = finisher;
data.mActionsReadyListener = actionsReadyListener;
- data.createDeleteAction = false;
if (mSaveInBgTask != null) {
// just log success/failure for the pre-existing screenshot
@@ -773,6 +774,9 @@
mScreenshotAnimatedView.setScaleX(currentScale);
mScreenshotAnimatedView.setScaleY(currentScale);
+ mDismissButton.setAlpha(0);
+ mDismissButton.setVisibility(View.VISIBLE);
+
AnimatorSet dropInAnimation = new AnimatorSet();
ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
@@ -794,6 +798,8 @@
toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
float xPositionPct =
SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+ float dismissPct =
+ SCREENSHOT_TO_CORNER_DISMISS_DELAY_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
float scalePct =
SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
toCorner.addUpdateListener(animation -> {
@@ -821,6 +827,19 @@
float yCenter = MathUtils.lerp(
startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t));
mScreenshotAnimatedView.setY(yCenter - bounds.height() * currentScaleY / 2f);
+
+ if (t >= dismissPct) {
+ mDismissButton.setAlpha((t - dismissPct) / (1 - dismissPct));
+ float currentX = mScreenshotAnimatedView.getX();
+ float currentY = mScreenshotAnimatedView.getY();
+ mDismissButton.setY(currentY - mDismissButton.getHeight() / 2f);
+ if (mDirectionLTR) {
+ mDismissButton.setX(currentX
+ + bounds.width() * currentScaleX - mDismissButton.getWidth() / 2f);
+ } else {
+ mDismissButton.setX(currentX - mDismissButton.getWidth() / 2f);
+ }
+ }
});
toCorner.addListener(new AnimatorListenerAdapter() {
@@ -845,13 +864,20 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
+ mDismissButton.setAlpha(1);
+ float dismissOffset = mDismissButton.getWidth() / 2f;
+ float finalDismissX = mDirectionLTR
+ ? finalPos.x - dismissOffset + bounds.width() * cornerScale / 2f
+ : finalPos.x - dismissOffset - bounds.width() * cornerScale / 2f;
+ mDismissButton.setX(finalDismissX);
+ mDismissButton.setY(
+ finalPos.y - dismissOffset - bounds.height() * cornerScale / 2f);
mScreenshotAnimatedView.setScaleX(1);
mScreenshotAnimatedView.setScaleY(1);
mScreenshotAnimatedView.setX(finalPos.x - bounds.width() * cornerScale / 2f);
mScreenshotAnimatedView.setY(finalPos.y - bounds.height() * cornerScale / 2f);
mScreenshotAnimatedView.setVisibility(View.GONE);
mScreenshotPreview.setVisibility(View.VISIBLE);
- mDismissButton.setVisibility(View.VISIBLE);
mScreenshotLayout.forceLayout();
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
deleted file mode 100644
index 0017b1f..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Insets;
-import android.graphics.PixelFormat;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.media.MediaActionSound;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.PowerManager;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-import android.widget.ImageView;
-import android.widget.Toast;
-
-import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Main;
-
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * Class for handling device screen shots
- *
- * @deprecated will be removed when corner flow is complete and tested
- */
-@Singleton
-@Deprecated
-public class GlobalScreenshotLegacy {
-
- // These strings are used for communicating the action invoked to
- // ScreenshotNotificationSmartActionsProvider.
- static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
- static final String EXTRA_ID = "android:screenshot_id";
- static final String ACTION_TYPE_DELETE = "Delete";
- static final String ACTION_TYPE_SHARE = "Share";
- static final String ACTION_TYPE_EDIT = "Edit";
- static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
- static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
-
- static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
- static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
- static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
-
- private static final String TAG = "GlobalScreenshot";
-
- private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
- private static final int SCREENSHOT_DROP_IN_DURATION = 430;
- private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
- private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
- private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
- private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320;
- private static final float BACKGROUND_ALPHA = 0.5f;
- private static final float SCREENSHOT_SCALE = 1f;
- private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f;
- private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f;
- private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f;
- private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f;
-
- private final ScreenshotNotificationsController mNotificationsController;
-
- private Context mContext;
- private WindowManager mWindowManager;
- private WindowManager.LayoutParams mWindowLayoutParams;
- private Display mDisplay;
- private DisplayMetrics mDisplayMetrics;
-
- private Bitmap mScreenBitmap;
- private View mScreenshotLayout;
- private ScreenshotSelectorView mScreenshotSelectorView;
- private ImageView mBackgroundView;
- private ImageView mScreenshotView;
- private ImageView mScreenshotFlash;
-
- private AnimatorSet mScreenshotAnimation;
-
- private float mBgPadding;
- private float mBgPaddingScale;
-
- private AsyncTask<Void, Void, Void> mSaveInBgTask;
-
- private MediaActionSound mCameraSound;
-
- /**
- * @param context everything needs a context :(
- */
- @Inject
- public GlobalScreenshotLegacy(
- Context context, @Main Resources resources, LayoutInflater layoutInflater,
- ScreenshotNotificationsController screenshotNotificationsController) {
- mContext = context;
- mNotificationsController = screenshotNotificationsController;
-
- // Inflate the screenshot layout
- mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot_legacy, null);
- mBackgroundView = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy_background);
- mScreenshotView = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy);
-
- mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_legacy_flash);
- mScreenshotSelectorView = mScreenshotLayout.findViewById(
- R.id.global_screenshot_legacy_selector);
- mScreenshotLayout.setFocusable(true);
- mScreenshotSelectorView.setFocusable(true);
- mScreenshotSelectorView.setFocusableInTouchMode(true);
- mScreenshotLayout.setOnTouchListener((v, event) -> {
- // Intercept and ignore all touch events
- return true;
- });
-
- // Setup the window that we are going to use
- mWindowLayoutParams = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
- WindowManager.LayoutParams.TYPE_SCREENSHOT,
- WindowManager.LayoutParams.FLAG_FULLSCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
- PixelFormat.TRANSLUCENT);
- mWindowLayoutParams.setTitle("ScreenshotAnimation");
- mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mWindowLayoutParams.setFitInsetsTypes(0 /* types */);
- mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- mDisplay = mWindowManager.getDefaultDisplay();
- mDisplayMetrics = new DisplayMetrics();
- mDisplay.getRealMetrics(mDisplayMetrics);
-
- // Scale has to account for both sides of the bg
- mBgPadding = (float) resources.getDimensionPixelSize(
- R.dimen.global_screenshot_legacy_bg_padding);
- mBgPaddingScale = mBgPadding / mDisplayMetrics.widthPixels;
-
-
- // Setup the Camera shutter sound
- mCameraSound = new MediaActionSound();
- mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
- }
-
- /**
- * Creates a new worker thread and saves the screenshot to the media store.
- */
- private void saveScreenshotInWorkerThread(
- Consumer<Uri> finisher,
- @Nullable GlobalScreenshot.ActionsReadyListener actionsReadyListener) {
- GlobalScreenshot.SaveImageInBackgroundData data =
- new GlobalScreenshot.SaveImageInBackgroundData();
- data.image = mScreenBitmap;
- data.finisher = finisher;
- data.mActionsReadyListener = actionsReadyListener;
- data.createDeleteAction = true;
- if (mSaveInBgTask != null) {
- mSaveInBgTask.cancel(false);
- }
-
- mNotificationsController.reset();
- mNotificationsController.setImage(mScreenBitmap);
- mNotificationsController.showSavingScreenshotNotification();
-
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute();
- }
-
- /**
- * Takes a screenshot of the current display and shows an animation.
- */
- private void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible,
- boolean navBarVisible, Rect crop) {
- int rot = mDisplay.getRotation();
- int width = crop.width();
- int height = crop.height();
-
- takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher,
- statusBarVisible, navBarVisible, null);
- }
-
- private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, boolean statusBarVisible,
- boolean navBarVisible, Rect screenboundsOfBitmap) {
- mScreenBitmap = screenshot;
- if (mScreenBitmap == null) {
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- finisher.accept(null);
- return;
- }
-
- // Optimizations
- mScreenBitmap.setHasAlpha(false);
- mScreenBitmap.prepareToDraw();
-
- // Start the post-screenshot animation
- startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
- statusBarVisible, navBarVisible, screenboundsOfBitmap);
- }
-
- void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) {
- mDisplay.getRealMetrics(mDisplayMetrics);
- takeScreenshot(finisher, statusBarVisible, navBarVisible,
- new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
- }
-
- void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
- Consumer<Uri> finisher) {
- // TODO: use task Id, userId, topComponent for smart handler
- // TODO: use visibleInsets for animation
- takeScreenshot(screenshot, finisher, false, false, screenshotScreenBounds);
- }
-
- /**
- * Displays a screenshot selector
- */
- void takeScreenshotPartial(final Consumer<Uri> finisher, final boolean statusBarVisible,
- final boolean navBarVisible) {
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- ScreenshotSelectorView view = (ScreenshotSelectorView) v;
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- view.startSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_MOVE:
- view.updateSelection((int) event.getX(), (int) event.getY());
- return true;
- case MotionEvent.ACTION_UP:
- view.setVisibility(View.GONE);
- mWindowManager.removeView(mScreenshotLayout);
- final Rect rect = view.getSelectionRect();
- if (rect != null) {
- if (rect.width() != 0 && rect.height() != 0) {
- // Need mScreenshotLayout to handle it after the view disappears
- mScreenshotLayout.post(() -> takeScreenshot(
- finisher, statusBarVisible, navBarVisible, rect));
- }
- }
-
- view.stopSelection();
- return true;
- }
-
- return false;
- }
- });
- mScreenshotLayout.post(() -> {
- mScreenshotSelectorView.setVisibility(View.VISIBLE);
- mScreenshotSelectorView.requestFocus();
- });
- }
-
- /**
- * Cancels screenshot request
- */
- void stopScreenshot() {
- // If the selector layer still presents on screen, we remove it and resets its state.
- if (mScreenshotSelectorView.getSelectionRect() != null) {
- mWindowManager.removeView(mScreenshotLayout);
- mScreenshotSelectorView.stopSelection();
- }
- }
-
- /**
- * Clears current screenshot
- */
- private void clearScreenshot() {
- if (mScreenshotLayout.isAttachedToWindow()) {
- mWindowManager.removeView(mScreenshotLayout);
- }
-
- // Clear any references to the bitmap
- mScreenBitmap = null;
- mScreenshotView.setImageBitmap(null);
- mBackgroundView.setVisibility(View.GONE);
- mScreenshotView.setVisibility(View.GONE);
- mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
-
- /**
- * Starts the animation after taking the screenshot
- */
- private void startAnimation(final Consumer<Uri> finisher, int w, int h,
- boolean statusBarVisible, boolean navBarVisible, @Nullable Rect screenBoundsOfBitmap) {
- // If power save is on, show a toast so there is some visual indication that a screenshot
- // has been taken.
- PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- if (powerManager.isPowerSaveMode()) {
- Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
- }
-
- // Add the view for the animation
- mScreenshotView.setImageBitmap(mScreenBitmap);
- mScreenshotLayout.requestFocus();
-
- // Setup the animation with the screenshot just taken
- if (mScreenshotAnimation != null) {
- if (mScreenshotAnimation.isStarted()) {
- mScreenshotAnimation.end();
- }
- mScreenshotAnimation.removeAllListeners();
- }
-
- mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
- ValueAnimator screenshotDropInAnim = screenBoundsOfBitmap != null
- ? createRectAnimation(screenBoundsOfBitmap) : createScreenshotDropInAnimation();
- ValueAnimator screenshotFadeOutAnim =
- createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible);
- mScreenshotAnimation = new AnimatorSet();
- mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
- mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Save the screenshot once we have a bit of time now
- saveScreenshotInWorkerThread(finisher, new GlobalScreenshot.ActionsReadyListener() {
- @Override
- void onActionsReady(GlobalScreenshot.SavedImageData actionData) {
- if (actionData.uri == null) {
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- } else {
- mNotificationsController
- .showScreenshotActionsNotification(actionData);
- }
- }
- });
- clearScreenshot();
- }
- });
- mScreenshotLayout.post(() -> {
- // Play the shutter sound to notify that we've taken a screenshot
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
-
- mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mScreenshotView.buildLayer();
- mScreenshotAnimation.start();
- });
- }
-
- private ValueAnimator createScreenshotDropInAnimation() {
- final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
- / SCREENSHOT_DROP_IN_DURATION);
- final float flashDurationPct = 2f * flashPeakDurationPct;
- final Interpolator flashAlphaInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- // Flash the flash view in and out quickly
- if (x <= flashDurationPct) {
- return (float) Math.sin(Math.PI * (x / flashDurationPct));
- }
- return 0;
- }
- };
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- // We start scaling when the flash is at it's peak
- if (x < flashPeakDurationPct) {
- return 0;
- }
- return (x - flashDurationPct) / (1f - flashDurationPct);
- }
- };
-
- Resources r = mContext.getResources();
- if ((r.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES) {
- mScreenshotView.getBackground().setTint(Color.BLACK);
- } else {
- mScreenshotView.getBackground().setTintList(null);
- }
-
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mBackgroundView.setAlpha(0f);
- mBackgroundView.setVisibility(View.VISIBLE);
- mScreenshotView.setAlpha(0f);
- mScreenshotView.setTranslationX(0f);
- mScreenshotView.setTranslationY(0f);
- mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
- mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
- mScreenshotView.setVisibility(View.VISIBLE);
- mScreenshotFlash.setAlpha(0f);
- mScreenshotFlash.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(android.animation.Animator animation) {
- mScreenshotFlash.setVisibility(View.GONE);
- }
- });
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
- mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(t);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
- }
- });
- return anim;
- }
-
- /**
- * If a bitmap was supplied to be used as the screenshot, animated from where that bitmap was
- * on screen, rather than using the whole screen.
- */
- private ValueAnimator createRectAnimation(Rect rect) {
- mScreenshotView.setAdjustViewBounds(true);
- mScreenshotView.setMaxHeight(rect.height());
- mScreenshotView.setMaxWidth(rect.width());
-
- final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
- / SCREENSHOT_DROP_IN_DURATION);
- final float flashDurationPct = 2f * flashPeakDurationPct;
- final Interpolator scaleInterpolator = x -> {
- // We start scaling when the flash is at it's peak
- if (x < flashPeakDurationPct) {
- return 0;
- }
- return (x - flashDurationPct) / (1f - flashDurationPct);
- };
-
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mBackgroundView.setAlpha(0f);
- mBackgroundView.setVisibility(View.VISIBLE);
- mScreenshotView.setAlpha(0f);
- mScreenshotView.setElevation(0f);
- mScreenshotView.setTranslationX(0f);
- mScreenshotView.setTranslationY(0f);
- mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
- mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
- mScreenshotView.setVisibility(View.VISIBLE);
- }
- });
- anim.addUpdateListener(animation -> {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
- mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(t);
- });
- return anim;
- }
-
- private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
- boolean navBarVisible) {
- ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
- anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBackgroundView.setVisibility(View.GONE);
- mScreenshotView.setVisibility(View.GONE);
- mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- });
-
- if (!statusBarVisible || !navBarVisible) {
- // There is no status bar/nav bar, so just fade the screenshot away in place
- anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
- - t * (SCREENSHOT_DROP_IN_MIN_SCALE
- - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
- mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(1f - t);
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- }
- });
- } else {
- // In the case where there is a status bar, animate to the origin of the bar (top-left)
- final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION
- / SCREENSHOT_DROP_OUT_DURATION;
- final Interpolator scaleInterpolator = new Interpolator() {
- @Override
- public float getInterpolation(float x) {
- if (x < scaleDurationPct) {
- // Decelerate, and scale the input accordingly
- return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
- }
- return 1f;
- }
- };
-
- // Determine the bounds of how to scale
- float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
- float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
- final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
- final PointF finalPos = new PointF(
- -halfScreenWidth
- + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
- -halfScreenHeight
- + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);
-
- // Animate the screenshot to the status bar
- anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (Float) animation.getAnimatedValue();
- float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
- - scaleInterpolator.getInterpolation(t)
- * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
- mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
- mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
- mScreenshotView.setScaleX(scaleT);
- mScreenshotView.setScaleY(scaleT);
- mScreenshotView.setTranslationX(t * finalPos.x);
- mScreenshotView.setTranslationY(t * finalPos.y);
- }
- });
- }
- return anim;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 221174f..10e6902 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -88,7 +88,6 @@
private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
private final String mScreenshotId;
private final boolean mSmartActionsEnabled;
- private final boolean mCreateDeleteAction;
private final Random mRandom = new Random();
SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) {
@@ -102,8 +101,6 @@
mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, UUID.randomUUID());
- mCreateDeleteAction = data.createDeleteAction;
-
// Initialize screenshot notification smart actions provider.
mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 46fe7f4..e4e253e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -148,84 +148,6 @@
}
/**
- * Shows a notification to inform the user that a screenshot is currently being saved.
- */
- public void showSavingScreenshotNotification() {
- final long now = System.currentTimeMillis();
-
- mPublicNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setCategory(Notification.CATEGORY_PROGRESS)
- .setWhen(now)
- .setShowWhen(true)
- .setColor(mResources.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- SystemUI.overrideNotificationAppName(mContext, mPublicNotificationBuilder, true);
-
- mNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saving_title))
- .setSmallIcon(R.drawable.stat_notify_image)
- .setWhen(now)
- .setShowWhen(true)
- .setColor(mResources.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setStyle(mNotificationStyle)
- .setPublicVersion(mPublicNotificationBuilder.build());
- mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
- SystemUI.overrideNotificationAppName(mContext, mNotificationBuilder, true);
-
- mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
- mNotificationBuilder.build());
- }
-
- /**
- * Shows a notification with the saved screenshot and actions that can be taken with it.
- *
- * @param actionData SavedImageData struct with image URI and actions
- */
- public void showScreenshotActionsNotification(
- GlobalScreenshot.SavedImageData actionData) {
- mNotificationBuilder.addAction(actionData.shareAction);
- mNotificationBuilder.addAction(actionData.editAction);
- mNotificationBuilder.addAction(actionData.deleteAction);
- for (Notification.Action smartAction : actionData.smartActions) {
- mNotificationBuilder.addAction(smartAction);
- }
-
- // Create the intent to show the screenshot in gallery
- Intent launchIntent = new Intent(Intent.ACTION_VIEW);
- launchIntent.setDataAndType(actionData.uri, "image/png");
- launchIntent.setFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- final long now = System.currentTimeMillis();
-
- // Update the text and the icon for the existing notification
- mPublicNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saved_title))
- .setContentText(mResources.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0))
- .setWhen(now)
- .setAutoCancel(true)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color));
- mNotificationBuilder
- .setContentTitle(mResources.getString(R.string.screenshot_saved_title))
- .setContentText(mResources.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent.getActivity(mContext, 0, launchIntent, 0))
- .setWhen(now)
- .setAutoCancel(true)
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setPublicVersion(mPublicNotificationBuilder.build())
- .setFlag(Notification.FLAG_NO_CLEAR, false);
-
- mNotificationManager.notify(SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT,
- mNotificationBuilder.build());
- }
-
- /**
* Shows a silent notification with the saved screenshot and actions that can be taken with it.
*
* @param actionData SavedImageData struct with image URI and actions
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 8322fe0..c05c823 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -48,7 +48,6 @@
private static final String TAG = "TakeScreenshotService";
private final GlobalScreenshot mScreenshot;
- private final GlobalScreenshotLegacy mScreenshotLegacy;
private final UserManager mUserManager;
private final UiEventLogger mUiEventLogger;
@@ -81,9 +80,6 @@
return;
}
- // TODO: clean up once notifications flow is fully deprecated
- boolean useCornerFlow = true;
-
ScreenshotHelper.ScreenshotRequest screenshotRequest =
(ScreenshotHelper.ScreenshotRequest) msg.obj;
@@ -91,22 +87,10 @@
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
- if (useCornerFlow) {
- mScreenshot.takeScreenshot(uriConsumer, onComplete);
- } else {
- mScreenshotLegacy.takeScreenshot(
- uriConsumer, screenshotRequest.getHasStatusBar(),
- screenshotRequest.getHasNavBar());
- }
+ mScreenshot.takeScreenshot(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
- if (useCornerFlow) {
- mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
- } else {
- mScreenshotLegacy.takeScreenshotPartial(
- uriConsumer, screenshotRequest.getHasStatusBar(),
- screenshotRequest.getHasNavBar());
- }
+ mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap(
@@ -116,13 +100,8 @@
int taskId = screenshotRequest.getTaskId();
int userId = screenshotRequest.getUserId();
ComponentName topComponent = screenshotRequest.getTopComponent();
- if (useCornerFlow) {
- mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
- taskId, userId, topComponent, uriConsumer, onComplete);
- } else {
- mScreenshotLegacy.handleImageAsScreenshot(screenshot, screenBounds, insets,
- taskId, userId, topComponent, uriConsumer);
- }
+ mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
+ taskId, userId, topComponent, uriConsumer, onComplete);
break;
default:
Log.d(TAG, "Invalid screenshot option: " + msg.what);
@@ -131,11 +110,9 @@
};
@Inject
- public TakeScreenshotService(GlobalScreenshot globalScreenshot,
- GlobalScreenshotLegacy globalScreenshotLegacy, UserManager userManager,
+ public TakeScreenshotService(GlobalScreenshot globalScreenshot, UserManager userManager,
UiEventLogger uiEventLogger) {
mScreenshot = globalScreenshot;
- mScreenshotLegacy = globalScreenshotLegacy;
mUserManager = userManager;
mUiEventLogger = uiEventLogger;
}
@@ -148,8 +125,6 @@
@Override
public boolean onUnbind(Intent intent) {
if (mScreenshot != null) mScreenshot.stopScreenshot();
- // TODO remove once notifications flow is fully deprecated
- if (mScreenshotLegacy != null) mScreenshotLegacy.stopScreenshot();
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 6aef6b4..6a33024 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -41,6 +41,8 @@
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
+import java.util.concurrent.Executor;
+
/**
* A class that allows activities to be launched in a seamless way where the notification
* transforms nicely into the starting window.
@@ -59,6 +61,7 @@
private final float mWindowCornerRadius;
private final NotificationShadeWindowViewController mNotificationShadeWindowViewController;
private final NotificationShadeDepthController mDepthController;
+ private final Executor mMainExecutor;
private Callback mCallback;
private final Runnable mTimeoutRunnable = () -> {
setAnimationPending(false);
@@ -73,12 +76,14 @@
Callback callback,
NotificationPanelViewController notificationPanel,
NotificationShadeDepthController depthController,
- NotificationListContainer container) {
+ NotificationListContainer container,
+ Executor mainExecutor) {
mNotificationPanel = notificationPanel;
mNotificationContainer = container;
mDepthController = depthController;
mNotificationShadeWindowViewController = notificationShadeWindowViewController;
mCallback = callback;
+ mMainExecutor = mainExecutor;
mWindowCornerRadius = ScreenDecorationsUtils
.getWindowCornerRadius(mNotificationShadeWindowViewController.getView()
.getResources());
@@ -155,7 +160,7 @@
RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
- mSourceNotification.post(() -> {
+ mMainExecutor.execute(() -> {
RemoteAnimationTarget primary = getPrimaryRemoteAnimationTarget(
remoteAnimationTargets);
if (primary == null) {
@@ -191,8 +196,9 @@
}
}
int targetWidth = primary.sourceContainerBounds.width();
- int notificationHeight = mSourceNotification.getActualHeight()
- - mSourceNotification.getClipBottomAmount();
+ // If the notification panel is collapsed, the clip may be larger than the height.
+ int notificationHeight = Math.max(mSourceNotification.getActualHeight()
+ - mSourceNotification.getClipBottomAmount(), 0);
int notificationWidth = mSourceNotification.getWidth();
anim.setDuration(ANIMATION_DURATION);
anim.setInterpolator(Interpolators.LINEAR);
@@ -292,7 +298,7 @@
@Override
public void onAnimationCancelled() throws RemoteException {
- mSourceNotification.post(() -> {
+ mMainExecutor.execute(() -> {
setAnimationPending(false);
mCallback.onLaunchAnimationCancelled();
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 8954d98..11022bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -628,6 +628,12 @@
resetSecondaryHandle();
} else {
int deltaRotation = deltaRotation(mCurrentRotation, mStartingQuickSwitchRotation);
+ if (mStartingQuickSwitchRotation == -1 || deltaRotation == -1) {
+ // Curious if starting quickswitch can change between the if check and our delta
+ Log.d(TAG, "secondary nav delta rotation: " + deltaRotation
+ + " current: " + mCurrentRotation
+ + " starting: " + mStartingQuickSwitchRotation);
+ }
int height = 0;
int width = 0;
Rect dispSize = mWindowManager.getCurrentWindowMetrics().getBounds();
@@ -691,6 +697,8 @@
dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
}
+ pw.print(" mStartingQuickSwitchRotation=" + mStartingQuickSwitchRotation);
+ pw.print(" mCurrentRotation=" + mCurrentRotation);
pw.print(" mNavigationBarView=");
if (mNavigationBarView == null) {
pw.println("null");
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 1bc42d1..e2714af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1275,7 +1275,7 @@
mActivityLaunchAnimator = new ActivityLaunchAnimator(
mNotificationShadeWindowViewController, this, mNotificationPanelViewController,
mNotificationShadeDepthControllerLazy.get(),
- (NotificationListContainer) mStackScroller);
+ (NotificationListContainer) mStackScroller, mContext.getMainExecutor());
// TODO: inject this.
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index 0db76ec..c929243 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -28,6 +28,7 @@
import android.graphics.RecordingCanvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.util.Log;
import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.ViewConfiguration;
@@ -220,7 +221,7 @@
@Override
public void jumpToCurrentState() {
- cancelAnimations();
+ cancelAnimations("jumpToCurrentState");
}
@Override
@@ -234,6 +235,7 @@
}
public void setPressed(boolean pressed) {
+ Log.d("b/63783866", "KeyButtonRipple.setPressed: pressed=" + pressed);
if (mDark != mLastDark && pressed) {
mRipplePaint = null;
mLastDark = mDark;
@@ -253,7 +255,8 @@
mHandler.removeCallbacksAndMessages(null);
}
- private void cancelAnimations() {
+ private void cancelAnimations(String reason) {
+ Log.d("b/63783866", "KeyButtonRipple.cancelAnimations: reason=" + reason);
mVisible = false;
mTmpArray.addAll(mRunningAnimations);
int size = mTmpArray.size();
@@ -284,7 +287,7 @@
}
private void enterSoftware() {
- cancelAnimations();
+ cancelAnimations("enterSoftware");
mVisible = true;
mGlowAlpha = getMaxGlowAlpha();
ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
@@ -370,7 +373,8 @@
}
private void enterHardware() {
- cancelAnimations();
+ Log.d("b/63783866", "enterHardware");
+ cancelAnimations("enterHardware");
mVisible = true;
mDrawingHardwareGlow = true;
setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2));
@@ -422,6 +426,7 @@
}
private void exitHardware() {
+ Log.d("b/63783866", "exitHardware");
mPaintProp = CanvasProperty.createPaint(getRipplePaint());
final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp,
RenderNodeAnimator.PAINT_ALPHA, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 0ca8ef0..8d7ecd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -441,6 +441,7 @@
@Override
public void abortCurrentGesture() {
+ Log.d("b/63783866", "KeyButtonView.abortCurrentGesture");
setPressed(false);
mRipple.abortDelayedRipple();
mGestureAborted = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 270f248..ce5bb05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -367,8 +367,14 @@
int id;
if (record.isGuest && record.info == null) {
// No guest user. Create one.
- UserInfo guest = mUserManager.createGuest(
- mContext, mContext.getString(com.android.settingslib.R.string.guest_nickname));
+ UserInfo guest;
+ try {
+ guest = mUserManager.createGuest(mContext,
+ mContext.getString(com.android.settingslib.R.string.guest_nickname));
+ } catch (UserManager.UserOperationException e) {
+ Log.e(TAG, "Couldn't create guest user", e);
+ return;
+ }
if (guest == null) {
// Couldn't create guest, most likely because there already exists one, we just
// haven't reloaded the user list yet.
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 7bc453a..0238799 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -452,12 +452,6 @@
}
@Test
- public void requiresAuthentication_whenEncryptedKeyguard_andBypass() {
- testStrongAuthExceptOnBouncer(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
- }
-
- @Test
public void requiresAuthentication_whenTimeoutKeyguard_andBypass() {
testStrongAuthExceptOnBouncer(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
@@ -513,10 +507,20 @@
@Test
public void testIgnoresAuth_whenLockdown() {
+ testIgnoresAuth(
+ KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ }
+
+ @Test
+ public void testIgnoresAuth_whenEncrypted() {
+ testIgnoresAuth(
+ KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ }
+
+ private void testIgnoresAuth(int strongAuth) {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
mTestableLooper.processAllMessages();
- when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
- KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index b2c3586..713aef9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -35,6 +35,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.FakeBroadcastDispatcher;
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.FalsingManager;
@@ -72,8 +73,8 @@
public void SysuiSetup() throws Exception {
SystemUIFactory.createFromConfig(mContext);
mDependency = new TestableDependency(mContext);
- mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Handler.class),
- mock(Looper.class), mock(DumpManager.class));
+ mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class),
+ mock(DumpManager.class), mock(BroadcastDispatcherLogger.class));
mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
Instrumentation inst = spy(mRealInstrumentation);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index 3357c58..4ed284e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -18,6 +18,7 @@
import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
@@ -27,6 +28,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -76,6 +78,8 @@
private lateinit var intentFilterOther: IntentFilter
@Mock
private lateinit var mockHandler: Handler
+ @Mock
+ private lateinit var logger: BroadcastDispatcherLogger
private lateinit var executor: Executor
@@ -93,9 +97,9 @@
broadcastDispatcher = TestBroadcastDispatcher(
mockContext,
- Handler(testableLooper.looper),
testableLooper.looper,
mock(DumpManager::class.java),
+ logger,
mapOf(0 to mockUBRUser0, 1 to mockUBRUser1))
// These should be valid filters
@@ -173,7 +177,12 @@
@Test
fun testRegisterCurrentAsActualUser() {
- setUserMock(mockContext, user1)
+ val intent = Intent(Intent.ACTION_USER_SWITCHED).apply {
+ putExtra(Intent.EXTRA_USER_HANDLE, user1.identifier)
+ }
+ broadcastDispatcher.onReceive(mockContext, intent)
+ testableLooper.processAllMessages()
+
broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
mockHandler, UserHandle.CURRENT)
@@ -236,11 +245,11 @@
private class TestBroadcastDispatcher(
context: Context,
- mainHandler: Handler,
bgLooper: Looper,
dumpManager: DumpManager,
+ logger: BroadcastDispatcherLogger,
var mockUBRMap: Map<Int, UserBroadcastDispatcher>
- ) : BroadcastDispatcher(context, mainHandler, bgLooper, dumpManager) {
+ ) : BroadcastDispatcher(context, bgLooper, dumpManager, logger) {
override fun createUBRForUser(userId: Int): UserBroadcastDispatcher {
return mockUBRMap.getOrDefault(userId, mock(UserBroadcastDispatcher::class.java))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index 9a5773a..09a0916 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -24,15 +24,16 @@
import android.util.ArraySet
import android.util.Log
import com.android.systemui.SysuiTestableContext
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import java.util.concurrent.Executor
class FakeBroadcastDispatcher(
context: SysuiTestableContext,
- handler: Handler,
looper: Looper,
- dumpManager: DumpManager
-) : BroadcastDispatcher(context, handler, looper, dumpManager) {
+ dumpManager: DumpManager,
+ logger: BroadcastDispatcherLogger
+) : BroadcastDispatcher(context, looper, dumpManager, logger) {
private val registeredReceivers = ArraySet<BroadcastReceiver>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
index 847e442..4433576 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
@@ -26,6 +26,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import junit.framework.Assert.assertEquals
@@ -40,6 +41,7 @@
import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.anyString
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.never
@@ -62,6 +64,8 @@
private val USER_HANDLE = UserHandle.of(USER_ID)
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+ fun <T> any(): T = Mockito.any()
+ fun <T> eq(v: T) = Mockito.eq(v) ?: v
}
@Mock
@@ -72,6 +76,8 @@
private lateinit var mockContext: Context
@Mock
private lateinit var mPendingResult: BroadcastReceiver.PendingResult
+ @Mock
+ private lateinit var logger: BroadcastDispatcherLogger
@Captor
private lateinit var argumentCaptor: ArgumentCaptor<IntentFilter>
@@ -91,7 +97,7 @@
fakeExecutor = FakeExecutor(FakeSystemClock())
userBroadcastDispatcher = UserBroadcastDispatcher(
- mockContext, USER_ID, testableLooper.looper)
+ mockContext, USER_ID, testableLooper.looper, logger)
userBroadcastDispatcher.pendingResult = mPendingResult
}
@@ -106,6 +112,13 @@
}
@Test
+ fun testNotRegisteredOnStart_logging() {
+ testableLooper.processAllMessages()
+
+ verify(logger, never()).logContextReceiverRegistered(anyInt(), any())
+ }
+
+ @Test
fun testSingleReceiverRegistered() {
intentFilter = IntentFilter(ACTION_1)
@@ -126,6 +139,18 @@
}
@Test
+ fun testSingleReceiverRegistered_logging() {
+ intentFilter = IntentFilter(ACTION_1)
+
+ userBroadcastDispatcher.registerReceiver(
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
+ testableLooper.processAllMessages()
+
+ verify(logger).logReceiverRegistered(USER_HANDLE.identifier, broadcastReceiver)
+ verify(logger).logContextReceiverRegistered(eq(USER_HANDLE.identifier), any())
+ }
+
+ @Test
fun testSingleReceiverUnregistered() {
intentFilter = IntentFilter(ACTION_1)
@@ -145,6 +170,21 @@
}
@Test
+ fun testSingleReceiverUnregistered_logger() {
+ intentFilter = IntentFilter(ACTION_1)
+
+ userBroadcastDispatcher.registerReceiver(
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
+ testableLooper.processAllMessages()
+
+ userBroadcastDispatcher.unregisterReceiver(broadcastReceiver)
+ testableLooper.processAllMessages()
+
+ verify(logger).logReceiverUnregistered(USER_HANDLE.identifier, broadcastReceiver)
+ verify(logger).logContextReceiverUnregistered(USER_HANDLE.identifier)
+ }
+
+ @Test
fun testFilterHasAllActionsAndCategories_twoReceivers() {
intentFilter = IntentFilter(ACTION_1)
intentFilterOther = IntentFilter(ACTION_2).apply {
@@ -196,6 +236,30 @@
}
@Test
+ fun testDispatch_logger() {
+ intentFilter = IntentFilter(ACTION_1)
+ intentFilterOther = IntentFilter(ACTION_2)
+
+ userBroadcastDispatcher.registerReceiver(
+ ReceiverData(broadcastReceiver, intentFilter, fakeExecutor, USER_HANDLE))
+ userBroadcastDispatcher.registerReceiver(
+ ReceiverData(broadcastReceiverOther, intentFilterOther, fakeExecutor, USER_HANDLE))
+
+ val intent = Intent(ACTION_2)
+
+ userBroadcastDispatcher.onReceive(mockContext, intent)
+ testableLooper.processAllMessages()
+ fakeExecutor.runAllReady()
+
+ val captor = ArgumentCaptor.forClass(Int::class.java)
+ verify(logger)
+ .logBroadcastReceived(captor.capture(), eq(USER_HANDLE.identifier), eq(intent))
+ verify(logger).logBroadcastDispatched(captor.value, ACTION_2, broadcastReceiverOther)
+ verify(logger, never())
+ .logBroadcastDispatched(eq(captor.value), any(), eq(broadcastReceiver))
+ }
+
+ @Test
fun testDispatchToCorrectReceiver_differentFiltersSameReceiver() {
intentFilter = IntentFilter(ACTION_1)
intentFilterOther = IntentFilter(ACTION_2)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 17022da..1ca2f02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -537,7 +537,7 @@
// Verify the selection was cleared.
verifyUpdateReceived();
assertThat(mBubbleData.isExpanded()).isFalse();
- assertSelectionCleared();
+ assertThat(mBubbleData.getSelectedBubble()).isNull();
}
// EXPANDED / ADD / UPDATE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
index cdef49d..2fa6cf0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -36,6 +36,8 @@
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Assert;
import org.junit.Before;
@@ -51,6 +53,7 @@
@TestableLooper.RunWithLooper
public class ActivityLaunchAnimatorTest extends SysuiTestCase {
+ private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private ActivityLaunchAnimator mLaunchAnimator;
@Mock
private ActivityLaunchAnimator.Callback mCallback;
@@ -80,8 +83,8 @@
mCallback,
mNotificationPanelViewController,
mNotificationShadeDepthController,
- mNotificationContainer);
-
+ mNotificationContainer,
+ mExecutor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
index 661fad2..bd59680 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/animation/PhysicsAnimatorTest.kt
@@ -38,6 +38,7 @@
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@SmallTest
+@Ignore("Blocking presubmits - investigating in b/158697054")
class PhysicsAnimatorTest : SysuiTestCase() {
private lateinit var viewGroup: ViewGroup
private lateinit var testView: View
diff --git a/packages/Tethering/TEST_MAPPING b/packages/Tethering/TEST_MAPPING
index 73254cd..5617b0c 100644
--- a/packages/Tethering/TEST_MAPPING
+++ b/packages/Tethering/TEST_MAPPING
@@ -1,7 +1,12 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "TetheringTests"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "TetheringIntegrationTests"
+ }
]
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index fd6f171..f14def6a 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -37,8 +37,8 @@
private TetheringConstants() { }
/**
- * Extra used for communicating with the TetherService. Includes the type of tethering to
- * enable if any.
+ * Extra used for communicating with the TetherService and TetherProvisioningActivity.
+ * Includes the type of tethering to enable if any.
*/
public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
/**
@@ -56,8 +56,38 @@
*/
public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
/**
- * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
- * which will receive provisioning results. Can be left empty.
+ * Extra used for communicating with the TetherService and TetherProvisioningActivity.
+ * Contains the {@link ResultReceiver} which will receive provisioning results.
+ * Can not be empty.
*/
public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+
+ /**
+ * Extra used for communicating with the TetherService and TetherProvisioningActivity.
+ * Contains the subId of current active cellular upstream.
+ * @hide
+ */
+ public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID";
+
+ /**
+ * Extra used for telling TetherProvisioningActivity the entitlement package name and class
+ * name to start UI entitlement check.
+ * @hide
+ */
+ public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME =
+ "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME";
+
+ /**
+ * Extra used for telling TetherService the intent action to start silent entitlement check.
+ * @hide
+ */
+ public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION =
+ "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION";
+
+ /**
+ * Extra used for TetherService to receive the response of provisioning check.
+ * @hide
+ */
+ public static final String EXTRA_TETHER_PROVISIONING_RESPONSE =
+ "android.net.extra.TETHER_PROVISIONING_RESPONSE";
}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 3c6e8d8..9dace70 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -19,6 +19,10 @@
import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
+import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
+import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
+import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
+import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringManager.TETHERING_INVALID;
@@ -69,7 +73,6 @@
protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
private static final String ACTION_PROVISIONING_ALARM =
"com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM";
- private static final String EXTRA_SUBID = "subId";
private final ComponentName mSilentProvisioningService;
private static final int MS_PER_HOUR = 60 * 60 * 1000;
@@ -197,9 +200,9 @@
// till upstream change to cellular.
if (mUsingCellularAsUpstream) {
if (showProvisioningUi) {
- runUiTetherProvisioning(downstreamType, config.activeDataSubId);
+ runUiTetherProvisioning(downstreamType, config);
} else {
- runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
+ runSilentTetherProvisioning(downstreamType, config);
}
mNeedReRunProvisioningUi = false;
} else {
@@ -262,9 +265,9 @@
if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) {
if (mNeedReRunProvisioningUi) {
mNeedReRunProvisioningUi = false;
- runUiTetherProvisioning(downstream, config.activeDataSubId);
+ runUiTetherProvisioning(downstream, config);
} else {
- runSilentTetherProvisioning(downstream, config.activeDataSubId);
+ runSilentTetherProvisioning(downstream, config);
}
}
}
@@ -361,7 +364,7 @@
* @param subId default data subscription ID.
*/
@VisibleForTesting
- protected void runSilentTetherProvisioning(int type, int subId) {
+ protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) {
if (DBG) mLog.i("runSilentTetherProvisioning: " + type);
// For silent provisioning, settings would stop tethering when entitlement fail.
ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null);
@@ -369,17 +372,20 @@
Intent intent = new Intent();
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
intent.putExtra(EXTRA_RUN_PROVISION, true);
+ intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi);
+ intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse);
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.putExtra(EXTRA_SUBID, subId);
+ intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
intent.setComponent(mSilentProvisioningService);
// Only admin user can change tethering and SilentTetherProvisioning don't need to
// show UI, it is fine to always start setting's background service as system user.
mContext.startService(intent);
+ return intent;
}
- private void runUiTetherProvisioning(int type, int subId) {
+ private void runUiTetherProvisioning(int type, final TetheringConfiguration config) {
ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null);
- runUiTetherProvisioning(type, subId, receiver);
+ runUiTetherProvisioning(type, config, receiver);
}
/**
@@ -389,17 +395,20 @@
* @param receiver to receive entitlement check result.
*/
@VisibleForTesting
- protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) {
+ protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config,
+ ResultReceiver receiver) {
if (DBG) mLog.i("runUiTetherProvisioning: " + type);
Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI);
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp);
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
- intent.putExtra(EXTRA_SUBID, subId);
+ intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Only launch entitlement UI for system user. Entitlement UI should not appear for other
// user because only admin user is allowed to change tethering.
mContext.startActivity(intent);
+ return intent;
}
// Not needed to check if this don't run on the handler thread because it's private.
@@ -631,7 +640,7 @@
receiver.send(cacheValue, null);
} else {
ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver);
- runUiTetherProvisioning(downstream, config.activeDataSubId, proxy);
+ runUiTetherProvisioning(downstream, config, proxy);
}
}
}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 48a600d..1d45f12 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -107,6 +107,7 @@
public final String[] provisioningApp;
public final String provisioningAppNoUi;
public final int provisioningCheckPeriod;
+ public final String provisioningResponse;
public final int activeDataSubId;
@@ -141,10 +142,13 @@
enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
- provisioningAppNoUi = getProvisioningAppNoUi(res);
+ provisioningAppNoUi = getResourceString(res,
+ R.string.config_mobile_hotspot_provision_app_no_ui);
provisioningCheckPeriod = getResourceInteger(res,
R.integer.config_mobile_hotspot_provision_check_period,
0 /* No periodic re-check */);
+ provisioningResponse = getResourceString(res,
+ R.string.config_mobile_hotspot_provision_response);
mOffloadPollInterval = getResourceInteger(res,
R.integer.config_tether_offload_poll_interval,
@@ -337,9 +341,9 @@
return copy(LEGACY_DHCP_DEFAULT_RANGE);
}
- private static String getProvisioningAppNoUi(Resources res) {
+ private static String getResourceString(Resources res, final int resId) {
try {
- return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui);
+ return res.getString(resId);
} catch (Resources.NotFoundException e) {
return "";
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index 72fa916..354e753 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -16,8 +16,16 @@
package com.android.networkstack.tethering;
+import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
+import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
+import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
+import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
+import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
+import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
+import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_ETHERNET;
+import static android.net.TetheringManager.TETHERING_INVALID;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
@@ -44,6 +52,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.net.util.SharedLog;
import android.os.Bundle;
@@ -53,6 +62,7 @@
import android.os.SystemProperties;
import android.os.test.TestLooper;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import androidx.test.filters.SmallTest;
@@ -76,6 +86,7 @@
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
+ private static final String PROVISIONING_APP_RESPONSE = "app_response";
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private Context mContext;
@@ -122,15 +133,51 @@
}
@Override
- protected void runUiTetherProvisioning(int type, int subId, ResultReceiver receiver) {
+ protected Intent runUiTetherProvisioning(int type,
+ final TetheringConfiguration config, final ResultReceiver receiver) {
+ Intent intent = super.runUiTetherProvisioning(type, config, receiver);
+ assertUiTetherProvisioningIntent(type, config, receiver, intent);
uiProvisionCount++;
receiver.send(fakeEntitlementResult, null);
+ return intent;
+ }
+
+ private void assertUiTetherProvisioningIntent(int type, final TetheringConfiguration config,
+ final ResultReceiver receiver, final Intent intent) {
+ assertEquals(Settings.ACTION_TETHER_PROVISIONING_UI, intent.getAction());
+ assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID));
+ final String[] appName = intent.getStringArrayExtra(
+ EXTRA_TETHER_UI_PROVISIONING_APP_NAME);
+ assertEquals(PROVISIONING_APP_NAME.length, appName.length);
+ for (int i = 0; i < PROVISIONING_APP_NAME.length; i++) {
+ assertEquals(PROVISIONING_APP_NAME[i], appName[i]);
+ }
+ assertEquals(receiver, intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK));
+ assertEquals(config.activeDataSubId,
+ intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
}
@Override
- protected void runSilentTetherProvisioning(int type, int subId) {
+ protected Intent runSilentTetherProvisioning(int type,
+ final TetheringConfiguration config) {
+ Intent intent = super.runSilentTetherProvisioning(type, config);
+ assertSilentTetherProvisioning(type, config, intent);
silentProvisionCount++;
addDownstreamMapping(type, fakeEntitlementResult);
+ return intent;
+ }
+
+ private void assertSilentTetherProvisioning(int type, final TetheringConfiguration config,
+ final Intent intent) {
+ assertEquals(type, intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID));
+ assertEquals(true, intent.getBooleanExtra(EXTRA_RUN_PROVISION, false));
+ assertEquals(PROVISIONING_NO_UI_APP_NAME,
+ intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION));
+ assertEquals(PROVISIONING_APP_RESPONSE,
+ intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE));
+ assertTrue(intent.hasExtra(EXTRA_PROVISION_CALLBACK));
+ assertEquals(config.activeDataSubId,
+ intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
}
}
@@ -187,6 +234,8 @@
.thenReturn(PROVISIONING_APP_NAME);
when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
.thenReturn(PROVISIONING_NO_UI_APP_NAME);
+ when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn(
+ PROVISIONING_APP_RESPONSE);
// Act like the CarrierConfigManager is present and ready unless told otherwise.
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(mCarrierConfigManager);
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index 1999ad7..3121863 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -61,6 +61,8 @@
private final SharedLog mLog = new SharedLog("TetheringConfigurationTest");
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+ private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
+ private static final String PROVISIONING_APP_RESPONSE = "app_response";
@Mock private Context mContext;
@Mock private TelephonyManager mTelephonyManager;
@Mock private Resources mResources;
@@ -388,6 +390,8 @@
new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId);
assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]);
assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]);
+ assertEquals(mockCfg.provisioningAppNoUi, PROVISIONING_NO_UI_APP_NAME);
+ assertEquals(mockCfg.provisioningResponse, PROVISIONING_APP_RESPONSE);
}
private void setUpResourceForSubId() {
@@ -403,6 +407,10 @@
new int[0]);
when(mResourcesForSubId.getStringArray(
R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
+ when(mResourcesForSubId.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
+ .thenReturn(PROVISIONING_NO_UI_APP_NAME);
+ when(mResourcesForSubId.getString(
+ R.string.config_mobile_hotspot_provision_response)).thenReturn(
+ PROVISIONING_APP_RESPONSE);
}
-
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 81de29c..b241bd1 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -988,4 +988,14 @@
* Unblocks uninstall for all packages for the user.
*/
public abstract void clearBlockUninstallForUser(@UserIdInt int userId);
+
+ /**
+ * Unsuspends all packages suspended by the given package for the user.
+ */
+ public abstract void unsuspendForSuspendingPackage(String suspendingPackage, int userId);
+
+ /**
+ * Returns {@code true} if the package is suspending any packages for the user.
+ */
+ public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 9080bdb..a3c164d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1606,7 +1606,7 @@
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
try {
r.callback.onDisplayInfoChanged(telephonyDisplayInfo);
} catch (RemoteException ex) {
@@ -2726,6 +2726,24 @@
Rlog.e(TAG, s);
}
+ /**
+ * If the registrant specified a subId, then we should only notify it if subIds match.
+ * If the registrant registered with DEFAULT subId, we should notify only when the related subId
+ * is default subId (which could be INVALID if there's no default subId).
+ *
+ * This should be the correct way to check record ID match. in idMatch the record's phoneId is
+ * speculated based on subId passed by the registrant so it's not a good reference.
+ * But to avoid triggering potential regression only replace idMatch with it when an issue with
+ * idMatch is reported. Eventually this should replace all instances of idMatch.
+ */
+ private boolean idMatchWithoutDefaultPhoneCheck(int subIdInRecord, int subIdToNotify) {
+ if (subIdInRecord == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ return (subIdToNotify == mDefaultSubId);
+ } else {
+ return (subIdInRecord == subIdToNotify);
+ }
+ }
+
boolean idMatch(int rSubId, int subId, int phoneId) {
if(subId < 0) {
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 27ea471..df3b688 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -337,6 +337,7 @@
class PortListenerImpl implements AdbConnectionPortListener {
public void onPortReceived(int port) {
+ if (DEBUG) Slog.d(TAG, "Received tls port=" + port);
Message msg = mHandler.obtainMessage(port > 0
? AdbDebuggingHandler.MSG_SERVER_CONNECTED
: AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
@@ -392,6 +393,7 @@
mOutputStream = mSocket.getOutputStream();
mInputStream = mSocket.getInputStream();
+ mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBD_SOCKET_CONNECTED);
} catch (IOException ioe) {
Slog.e(TAG, "Caught an exception opening the socket: " + ioe);
closeSocketLocked();
@@ -504,6 +506,7 @@
} catch (IOException ex) {
Slog.e(TAG, "Failed closing socket: " + ex);
}
+ mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBD_SOCKET_DISCONNECTED);
}
/** Call to stop listening on the socket and exit the thread. */
@@ -729,6 +732,10 @@
static final int MSG_SERVER_CONNECTED = 24;
// Notifies us the TLS server is disconnected
static final int MSG_SERVER_DISCONNECTED = 25;
+ // Notification when adbd socket successfully connects.
+ static final int MSG_ADBD_SOCKET_CONNECTED = 26;
+ // Notification when adbd socket is disconnected.
+ static final int MSG_ADBD_SOCKET_DISCONNECTED = 27;
// === Messages we can send to adbd ===========
static final String MSG_DISCONNECT_DEVICE = "DD";
@@ -1170,6 +1177,28 @@
}
break;
}
+ case MSG_ADBD_SOCKET_CONNECTED: {
+ if (DEBUG) Slog.d(TAG, "adbd socket connected");
+ if (mAdbWifiEnabled) {
+ // In scenarios where adbd is restarted, the tls port may change.
+ mConnectionPortPoller =
+ new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller.start();
+ }
+ break;
+ }
+ case MSG_ADBD_SOCKET_DISCONNECTED: {
+ if (DEBUG) Slog.d(TAG, "adbd socket disconnected");
+ if (mConnectionPortPoller != null) {
+ mConnectionPortPoller.cancelAndWait();
+ mConnectionPortPoller = null;
+ }
+ if (mAdbWifiEnabled) {
+ // In scenarios where adbd is restarted, the tls port may change.
+ onAdbdWifiServerDisconnected(-1);
+ }
+ break;
+ }
}
}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index e1f9a7a..ef81d71 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -241,12 +241,7 @@
private AdbService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
-
- boolean secureAdbEnabled = AdbProperties.secure().orElse(false);
- boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
- if (secureAdbEnabled && !dataEncrypted) {
- mDebuggingManager = new AdbDebuggingManager(context);
- }
+ mDebuggingManager = new AdbDebuggingManager(context);
initAdbState();
LocalServices.addService(AdbManagerInternal.class, new AdbManagerInternalImpl());
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 026f147..0795122 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1671,6 +1671,12 @@
*/
@Nullable ContentCaptureManagerInternal mContentCaptureService;
+ /**
+ * Set of {@link ProcessRecord} that have either {@link ProcessRecord#hasTopUi()} or
+ * {@link ProcessRecord#runningRemoteAnimation} set to {@code true}.
+ */
+ final ArraySet<ProcessRecord> mTopUiOrRunningRemoteAnimApps = new ArraySet<>();
+
final class UiHandler extends Handler {
public UiHandler() {
super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -14702,6 +14708,7 @@
mProcessesToGc.remove(app);
mPendingPssProcesses.remove(app);
+ mTopUiOrRunningRemoteAnimApps.remove(app);
ProcessList.abortNextPssTime(app.procStateMemTracker);
// Dismiss any open dialogs.
@@ -18490,6 +18497,22 @@
return proc;
}
+ /**
+ * @return {@code true} if {@link #mTopUiOrRunningRemoteAnimApps} set contains {@code app} or when there are no apps
+ * in this list, an false otherwise.
+ */
+ boolean containsTopUiOrRunningRemoteAnimOrEmptyLocked(ProcessRecord app) {
+ return mTopUiOrRunningRemoteAnimApps.isEmpty() || mTopUiOrRunningRemoteAnimApps.contains(app);
+ }
+
+ void addTopUiOrRunningRemoteAnim(ProcessRecord app) {
+ mTopUiOrRunningRemoteAnimApps.add(app);
+ }
+
+ void removeTopUiOrRunningRemoteAnim(ProcessRecord app) {
+ mTopUiOrRunningRemoteAnimApps.remove(app);
+ }
+
@Override
public boolean dumpHeap(String process, int userId, boolean managed, boolean mallocInfo,
boolean runGc, String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index da5f489..58b0a15 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1151,8 +1151,17 @@
// is currently showing UI.
app.systemNoUi = true;
if (app == topApp) {
+ // If specific system app has set ProcessRecord.mHasTopUi or is running a remote
+ // animation (ProcessRecord.runningRemoteAnimation), this will prevent topApp
+ // to use SCHED_GROUP_TOP_APP to ensure process with mHasTopUi will have exclusive
+ // access to configured cores.
+ if (mService.containsTopUiOrRunningRemoteAnimOrEmptyLocked(app)) {
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+ } else {
+ app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+ }
app.systemNoUi = false;
- app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP);
+
app.adjType = "pers-top-activity";
} else if (app.hasTopUi()) {
// sched group/proc state adjustment is below
@@ -1193,10 +1202,20 @@
boolean foregroundActivities = false;
if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) {
- // The last app on the list is the foreground app.
- adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
- app.adjType = "top-activity";
+
+ // If specific system app has set ProcessRecord.mHasTopUi or is running a remote
+ // animation (ProcessRecord.runningRemoteAnimation), this will prevent topApp
+ // to use SCHED_GROUP_TOP_APP to ensure process with mHasTopUi will have exclusive
+ // access to configured cores.
+ if (mService.containsTopUiOrRunningRemoteAnimOrEmptyLocked(app)) {
+ adj = ProcessList.FOREGROUND_APP_ADJ;
+ schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ app.adjType = "top-activity";
+ } else {
+ adj = ProcessList.FOREGROUND_APP_ADJ;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ app.adjType = "top-activity-behind-topui";
+ }
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c5152c0..4c75ab2 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1268,6 +1268,7 @@
void setHasTopUi(boolean hasTopUi) {
mHasTopUi = hasTopUi;
mWindowProcessController.setHasTopUi(hasTopUi);
+ updateTopUiOrRunningRemoteAnim();
}
boolean hasTopUi() {
@@ -1518,10 +1519,19 @@
Slog.i(TAG, "Setting runningRemoteAnimation=" + runningRemoteAnimation
+ " for pid=" + pid);
}
+ updateTopUiOrRunningRemoteAnim();
mService.updateOomAdjLocked(this, true, OomAdjuster.OOM_ADJ_REASON_UI_VISIBILITY);
}
}
+ void updateTopUiOrRunningRemoteAnim() {
+ if (runningRemoteAnimation || hasTopUi()) {
+ mService.addTopUiOrRunningRemoteAnim(this);
+ } else {
+ mService.removeTopUiOrRunningRemoteAnim(this);
+ }
+ }
+
public long getInputDispatchingTimeout() {
return mWindowProcessController.getInputDispatchingTimeout();
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a498e38..65a1301 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2352,6 +2352,16 @@
mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
mStartInputHistory.addEntry(info);
+ // Seems that PackageManagerInternal#grantImplicitAccess() doesn't handle cross-user
+ // implicit visibility (e.g. IME[user=10] -> App[user=0]) thus we do this only for the
+ // same-user scenarios.
+ // That said ignoring cross-user scenario will never affect IMEs that do not have
+ // INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
+ if (mSettings.getCurrentUserId() == UserHandle.getUserId(mCurClient.uid)) {
+ mPackageManagerInternal.grantImplicitAccess(mSettings.getCurrentUserId(),
+ null /* intent */, UserHandle.getAppId(mCurMethodUid), mCurClient.uid, true);
+ }
+
final SessionState session = mCurClient.curSession;
executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
MSG_START_INPUT, mCurInputContextMissingMethods, initial ? 0 : 1 /* restarting */,
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 2461b0c..30a636d 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -247,6 +247,7 @@
.setType(type)
.setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+ .setAddress(device.getAddress())
.build();
return newBtRoute;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 576e12e..ae8b3a0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13518,6 +13518,17 @@
removeSuspensionsBySuspendingPackage(allPackages, suspendingPackage::equals, userId);
}
+ boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
+ synchronized (mLock) {
+ for (final PackageSetting ps : mSettings.mPackages.values()) {
+ if (ps.isSuspendedBy(suspendingPackage, userId)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Removes any suspensions on given packages that were added by packages that pass the given
* predicate.
@@ -23913,7 +23924,6 @@
callingUid);
}
-
@Override
public boolean isPlatformSigned(String packageName) {
PackageSetting packageSetting = mSettings.mPackages.get(packageName);
@@ -25018,6 +25028,16 @@
mSettings.writePackageRestrictionsLPr(userId);
}
}
+
+ @Override
+ public void unsuspendForSuspendingPackage(final String packageName, int affectedUser) {
+ PackageManagerService.this.unsuspendForSuspendingPackage(packageName, affectedUser);
+ }
+
+ @Override
+ public boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
+ return PackageManagerService.this.isSuspendingAnyPackages(suspendingPackage, userId);
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 00a5fe7..834303c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -422,6 +422,11 @@
return readUserState(userId).suspended;
}
+ boolean isSuspendedBy(String suspendingPackage, int userId) {
+ final PackageUserState state = readUserState(userId);
+ return state.suspendParams != null && state.suspendParams.containsKey(suspendingPackage);
+ }
+
void addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
final PackageUserState existingUserState = modifyUserState(userId);
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 7c89b98..492b84a 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -363,7 +363,8 @@
pmInt.forEachPackage(pkg -> {
if (!pkg.isSystem()) return;
final String pkgName = pkg.getManifestPackageName();
- if (!allWhitelistedPackages.contains(pkgName)) {
+ if (!allWhitelistedPackages.contains(pkgName)
+ && !isAutoGeneratedRRO(pmInt.getPackage(pkgName))) {
errors.add(String.format(logMessageFmt, pkgName));
}
});
diff --git a/services/core/java/com/android/server/slice/SliceClientPermissions.java b/services/core/java/com/android/server/slice/SliceClientPermissions.java
index ab94a59..e241205 100644
--- a/services/core/java/com/android/server/slice/SliceClientPermissions.java
+++ b/services/core/java/com/android/server/slice/SliceClientPermissions.java
@@ -160,6 +160,9 @@
// Get to the beginning of the provider.
while (parser.getEventType() != XmlPullParser.START_TAG
|| !TAG_CLIENT.equals(parser.getName())) {
+ if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+ throw new XmlPullParserException("Can't find client tag in xml");
+ }
parser.next();
}
int depth = parser.getDepth();
@@ -173,6 +176,9 @@
parser.next();
while (parser.getDepth() > depth) {
+ if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+ return provider;
+ }
if (parser.getEventType() == XmlPullParser.START_TAG
&& TAG_AUTHORITY.equals(parser.getName())) {
try {
diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java
index 1d1c28f..343d2e3 100644
--- a/services/core/java/com/android/server/slice/SlicePermissionManager.java
+++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java
@@ -130,7 +130,7 @@
}
SliceClientPermissions client = getClient(pkgUser);
client.clear();
- mHandler.obtainMessage(H.MSG_REMOVE, pkgUser);
+ mHandler.obtainMessage(H.MSG_REMOVE, pkgUser).sendToTarget();
}
public String[] getAllPackagesGranted(String pkg) {
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index c2bae1a..7fe21e3 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -209,14 +209,6 @@
// Random seed stable for StatsPullAtomService life cycle - can be used for stable sampling
private static final int RANDOM_SEED = new Random().nextInt();
- /**
- * Lowest available uid for apps.
- *
- * <p>Used to quickly discard memory snapshots of the zygote forks from native process
- * measurements.
- */
- private static final int MIN_APP_UID = 10_000;
-
private static final int DIMENSION_KEY_SIZE_HARD_LIMIT = 800;
private static final int DIMENSION_KEY_SIZE_SOFT_LIMIT = 500;
private static final long APP_OPS_SAMPLING_INITIALIZATION_DELAY_MILLIS = 45000;
@@ -1819,10 +1811,6 @@
return StatsManager.PULL_SUCCESS;
}
- private static boolean isAppUid(int uid) {
- return uid >= MIN_APP_UID;
- }
-
private void registerProcessMemoryHighWaterMark() {
int tagId = FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK;
mStatsManager.setPullAtomCallback(
@@ -1859,7 +1847,7 @@
int size = processCmdlines.size();
for (int i = 0; i < size; ++i) {
final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(processCmdlines.keyAt(i));
- if (snapshot == null || isAppUid(snapshot.uid)) {
+ if (snapshot == null) {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
@@ -1920,7 +1908,7 @@
for (int i = 0; i < size; ++i) {
int pid = processCmdlines.keyAt(i);
final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
- if (snapshot == null || isAppUid(snapshot.uid)) {
+ if (snapshot == null) {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
@@ -3577,7 +3565,16 @@
private void processHistoricalOp(AppOpsManager.HistoricalOp op,
List<AppOpEntry> opsList, int uid, int samplingRatio, String packageName,
@Nullable String attributionTag) {
- AppOpEntry entry = new AppOpEntry(packageName, attributionTag, op, uid);
+ int firstChar = 0;
+ if (attributionTag != null && attributionTag.startsWith(packageName)) {
+ firstChar = packageName.length();
+ if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar) == '.') {
+ firstChar++;
+ }
+ }
+ AppOpEntry entry = new AppOpEntry(packageName,
+ attributionTag == null ? null : attributionTag.substring(firstChar), op,
+ uid);
if (entry.mHash < samplingRatio) {
opsList.add(entry);
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 77bc37f..bf9a784 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -178,6 +178,7 @@
if (imeSource != null && imeSource.isVisible()) {
imeSource = new InsetsSource(imeSource);
imeSource.setVisible(false);
+ imeSource.setFrame(0, 0, 0, 0);
state = new InsetsState(state);
state.addSource(imeSource);
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 1cd94b4..24bb7c8 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1341,6 +1341,17 @@
break;
}
+ // Tasks managed by/associated with an ActivityView should be excluded from recents.
+ // singleTaskInstance is set on the VirtualDisplay managed by ActivityView
+ // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance
+ final ActivityStack stack = task.getStack();
+ if (stack != null) {
+ DisplayContent display = stack.getDisplay();
+ if (display != null && display.isSingleTaskInstance()) {
+ return false;
+ }
+ }
+
// If we're in lock task mode, ignore the root task
if (task == mService.getLockTaskController().getRootTask()) {
return false;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3ee7ee7..ce2ae2a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1436,15 +1436,6 @@
mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
}
- final boolean isRootTask = isRootTask();
- if (isRootTask) {
- final DisplayContent display = getDisplayContent();
- if (display.isSingleTaskInstance()) {
- mAtmService.notifySingleTaskDisplayEmpty(display.mDisplayId);
- }
- display.mDisplayContent.setLayoutNeeded();
- }
-
if (hasChild()) {
updateEffectiveIntent();
@@ -1465,7 +1456,7 @@
} else if (!mReuseTask && !mCreatedByOrganizer) {
// Remove entire task if it doesn't have any activity left and it isn't marked for reuse
// or created by task organizer.
- if (!isRootTask) {
+ if (!isRootTask()) {
getStack().removeChild(this, reason);
}
EventLogTags.writeWmTaskRemoved(mTaskId,
@@ -2817,6 +2808,10 @@
if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
EventLogTags.writeWmTaskRemoved(mTaskId, "removeTask");
+ if (mDisplayContent != null && mDisplayContent.isSingleTaskInstance()) {
+ mAtmService.notifySingleTaskDisplayEmpty(mDisplayContent.mDisplayId);
+ }
+
// If applicable let the TaskOrganizer know the Task is vanishing.
setTaskOrganizer(null);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index df55b3b..10ad07c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2755,7 +2755,8 @@
Slog.i(LOG_TAG, "Giving the PO additional power...");
markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId);
Slog.i(LOG_TAG, "Migrating DO policies to PO...");
- moveDoPoliciesToProfileParentAdmin(doAdmin, poAdmin.getParentActiveAdmin());
+ moveDoPoliciesToProfileParentAdminLocked(doAdmin, poAdmin.getParentActiveAdmin());
+ migratePersonalAppSuspensionLocked(doUserId, poUserId, poAdmin);
saveSettingsLocked(poUserId);
Slog.i(LOG_TAG, "Clearing the DO...");
final ComponentName doAdminReceiver = doAdmin.info.getComponent();
@@ -2775,6 +2776,25 @@
.write();
}
+ @GuardedBy("getLockObject()")
+ private void migratePersonalAppSuspensionLocked(
+ int doUserId, int poUserId, ActiveAdmin poAdmin) {
+ final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
+ if (!pmi.isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, doUserId)) {
+ Slog.i(LOG_TAG, "DO is not suspending any apps.");
+ return;
+ }
+
+ if (getTargetSdk(poAdmin.info.getPackageName(), poUserId) >= Build.VERSION_CODES.R) {
+ Slog.i(LOG_TAG, "PO is targeting R+, keeping personal apps suspended.");
+ getUserData(doUserId).mAppsSuspended = true;
+ poAdmin.mSuspendPersonalApps = true;
+ } else {
+ Slog.i(LOG_TAG, "PO isn't targeting R+, unsuspending personal apps.");
+ pmi.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, doUserId);
+ }
+ }
+
private void uninstallOrDisablePackage(String packageName, int userHandle) {
final ApplicationInfo appInfo;
try {
@@ -2816,7 +2836,9 @@
pi.uninstall(packageName, 0 /* flags */, new IntentSender((IIntentSender) mLocalSender));
}
- private void moveDoPoliciesToProfileParentAdmin(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) {
+ @GuardedBy("getLockObject()")
+ private void moveDoPoliciesToProfileParentAdminLocked(
+ ActiveAdmin doAdmin, ActiveAdmin parentAdmin) {
// The following policies can be already controlled via parent instance, skip if so.
if (parentAdmin.mPasswordPolicy.quality == PASSWORD_QUALITY_UNSPECIFIED) {
parentAdmin.mPasswordPolicy = doAdmin.mPasswordPolicy;
@@ -16147,25 +16169,34 @@
}
Slog.i(LOG_TAG, String.format("%s personal apps for user %d",
suspended ? "Suspending" : "Unsuspending", userId));
+
+ if (suspended) {
+ suspendPersonalAppsInPackageManager(userId);
+ } else {
+ mInjector.getPackageManagerInternal().unsuspendForSuspendingPackage(
+ PLATFORM_PACKAGE_NAME, userId);
+ }
+
+ synchronized (getLockObject()) {
+ getUserData(userId).mAppsSuspended = suspended;
+ saveSettingsLocked(userId);
+ }
+ }
+
+ private void suspendPersonalAppsInPackageManager(int userId) {
mInjector.binderWithCleanCallingIdentity(() -> {
try {
final String[] appsToSuspend = mInjector.getPersonalAppsForSuspension(userId);
- final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser(
- appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId);
- if (!ArrayUtils.isEmpty(failedPackages)) {
- Slog.wtf(LOG_TAG, String.format("Failed to %s packages: %s",
- suspended ? "suspend" : "unsuspend", String.join(",", failedPackages)));
+ final String[] failedApps = mIPackageManager.setPackagesSuspendedAsUser(
+ appsToSuspend, true, null, null, null, PLATFORM_PACKAGE_NAME, userId);
+ if (!ArrayUtils.isEmpty(failedApps)) {
+ Slog.wtf(LOG_TAG, "Failed to suspend apps: " + String.join(",", failedApps));
}
} catch (RemoteException re) {
// Shouldn't happen.
Slog.e(LOG_TAG, "Failed talking to the package manager", re);
}
});
-
- synchronized (getLockObject()) {
- getUserData(userId).mAppsSuspended = suspended;
- saveSettingsLocked(userId);
- }
}
@GuardedBy("getLockObject()")
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 2e60f2a..236ac84 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -16,6 +16,8 @@
package com.android.server.people.prediction;
+import static java.util.Collections.reverseOrder;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -39,6 +41,7 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
@@ -85,7 +88,9 @@
List<ShareTarget> shareTargets = getDirectShareTargets();
SharesheetModelScorer.computeScore(shareTargets, getShareEventType(mIntentFilter),
System.currentTimeMillis());
- Collections.sort(shareTargets, (t1, t2) -> -Float.compare(t1.getScore(), t2.getScore()));
+ Collections.sort(shareTargets,
+ Comparator.comparing(ShareTarget::getScore, reverseOrder())
+ .thenComparing(t -> t.getAppTarget().getRank()));
List<AppTarget> res = new ArrayList<>();
for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(),
shareTargets.size()); i++) {
@@ -135,6 +140,7 @@
new AppTargetId(shortcutInfo.getId()),
shortcutInfo)
.setClassName(shareShortcut.getTargetComponent().getClassName())
+ .setRank(shortcutInfo.getRank())
.build();
String packageName = shortcutInfo.getPackage();
int userId = shortcutInfo.getUserId();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index fde40aa..cdafd32 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -63,6 +63,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.answer;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
@@ -170,6 +171,7 @@
mock(OomAdjProfiler.class));
doReturn(new ActivityManagerService.ProcessChangeItem()).when(sService)
.enqueueProcessChangeItemLocked(anyInt(), anyInt());
+ doReturn(true).when(sService).containsTopUiOrRunningRemoteAnimOrEmptyLocked(any());
sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList,
mock(ActiveUids.class));
sService.mOomAdjuster.mAdjSeq = 10000;
@@ -266,6 +268,21 @@
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoOne_TopApp_PreemptedByTopUi() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
+ doReturn(app).when(sService).getTopAppLocked();
+ doReturn(false).when(sService).containsTopUiOrRunningRemoteAnimOrEmptyLocked(eq(app));
+ sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
+ doReturn(null).when(sService).getTopAppLocked();
+
+ assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoOne_RunningInstrumentation() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index a0b9d9d..3167820 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -18,17 +18,24 @@
import static android.os.UserHandle.USER_SYSTEM;
import static com.android.server.devicepolicy.DpmTestUtils.writeInputStreamToFile;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ComponentName;
+import android.content.Intent;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -354,8 +361,7 @@
prepareAdmin1AsDo();
prepareAdminAnotherPackageAsPo(COPE_PROFILE_USER_ID);
- final DevicePolicyManagerServiceTestable dpms;
- dpms = bootDpmsUp();
+ final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
// DO should still be DO since no migration should happen.
assertTrue(dpms.mOwners.hasDeviceOwner());
@@ -364,13 +370,12 @@
@SmallTest
public void testCompMigrationAffiliated() throws Exception {
prepareAdmin1AsDo();
- prepareAdmin1AsPo(COPE_PROFILE_USER_ID);
+ prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R);
// Secure lock screen is needed for password policy APIs to work.
when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
- final DevicePolicyManagerServiceTestable dpms;
- dpms = bootDpmsUp();
+ final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
// DO should cease to be DO.
assertFalse(dpms.mOwners.hasDeviceOwner());
@@ -408,6 +413,66 @@
dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID)
.getEffectiveRestrictions()
.containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME));
+ assertEquals("Personal apps suspension wasn't migrated",
+ DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED,
+ dpm.getPersonalAppsSuspendedReasons(admin1));
+ });
+ }
+
+ @SmallTest
+ public void testCompMigration_keepSuspendedAppsWhenDpcIsRPlus() throws Exception {
+ prepareAdmin1AsDo();
+ prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.R);
+
+ // Pretend some packages are suspended.
+ when(getServices().packageManagerInternal.isSuspendingAnyPackages(
+ PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true);
+
+ final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
+
+ verify(getServices().packageManagerInternal, never())
+ .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_STARTED, USER_SYSTEM);
+
+ // Verify that actual package suspension state is not modified after user start
+ verify(getServices().packageManagerInternal, never())
+ .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+ verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser(
+ any(), anyBoolean(), any(), any(), any(), any(), anyInt());
+
+ final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
+ poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
+
+ runAsCaller(poContext, dpms, dpm -> {
+ assertEquals("Personal apps suspension wasn't migrated",
+ DevicePolicyManager.PERSONAL_APPS_SUSPENDED_EXPLICITLY,
+ dpm.getPersonalAppsSuspendedReasons(admin1));
+ });
+ }
+
+ @SmallTest
+ public void testCompMigration_unsuspendAppsWhenDpcNotRPlus() throws Exception {
+ prepareAdmin1AsDo();
+ prepareAdmin1AsPo(COPE_PROFILE_USER_ID, Build.VERSION_CODES.Q);
+
+ // Pretend some packages are suspended.
+ when(getServices().packageManagerInternal.isSuspendingAnyPackages(
+ PLATFORM_PACKAGE_NAME, USER_SYSTEM)).thenReturn(true);
+
+ final DevicePolicyManagerServiceTestable dpms = bootDpmsUp();
+
+ // Verify that apps get unsuspended.
+ verify(getServices().packageManagerInternal)
+ .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, USER_SYSTEM);
+
+ final DpmMockContext poContext = new DpmMockContext(getServices(), mRealTestContext);
+ poContext.binder.callingUid = UserHandle.getUid(COPE_PROFILE_USER_ID, COPE_ADMIN1_APP_ID);
+
+ runAsCaller(poContext, dpms, dpm -> {
+ assertEquals("Personal apps weren't unsuspended",
+ DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED,
+ dpm.getPersonalAppsSuspendedReasons(admin1));
});
}
@@ -439,22 +504,23 @@
.getAbsoluteFile());
}
- private void prepareAdmin1AsPo(int profileUserId) throws Exception {
+ private void prepareAdmin1AsPo(int profileUserId, int targetSdk) throws Exception {
preparePo(profileUserId, admin1, R.raw.comp_profile_owner_same_package,
- R.raw.comp_policies_profile_same_package, COPE_ADMIN1_APP_ID);
+ R.raw.comp_policies_profile_same_package, COPE_ADMIN1_APP_ID, targetSdk);
}
private void prepareAdminAnotherPackageAsPo(int profileUserId) throws Exception {
preparePo(profileUserId, adminAnotherPackage, R.raw.comp_profile_owner_another_package,
- R.raw.comp_policies_profile_another_package, COPE_ANOTHER_ADMIN_APP_ID);
+ R.raw.comp_policies_profile_another_package, COPE_ANOTHER_ADMIN_APP_ID,
+ Build.VERSION.SDK_INT);
}
private void preparePo(int profileUserId, ComponentName admin, int profileOwnerXmlResId,
- int policyXmlResId, int adminAppId) throws Exception {
+ int policyXmlResId, int adminAppId, int targetSdk) throws Exception {
final File profileDir = getServices().addUser(profileUserId, 0,
UserManager.USER_TYPE_PROFILE_MANAGED, USER_SYSTEM /* profile group */);
- setUpPackageManagerForFakeAdmin(
- admin, UserHandle.getUid(profileUserId, adminAppId), admin1);
+ setUpPackageManagerForFakeAdmin(admin, UserHandle.getUid(profileUserId, adminAppId),
+ /* enabledSetting =*/ null, targetSdk, admin1);
writeInputStreamToFile(getRawStream(policyXmlResId),
(new File(profileDir, "device_policies.xml")).getAbsoluteFile());
writeInputStreamToFile(getRawStream(profileOwnerXmlResId),
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 4a77489..daaabf8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -254,7 +254,7 @@
@Override
protected void tearDown() throws Exception {
- flushTasks();
+ flushTasks(dpms);
getMockTransferMetadataManager().deleteMetadataFile();
super.tearDown();
}
@@ -4961,7 +4961,7 @@
// CertificateMonitor.updateInstalledCertificates is called on the background thread,
// let it finish with system uid, otherwise it will throw and crash.
- flushTasks();
+ flushTasks(dpms);
mContext.binder.restoreCallingIdentity(ident);
}
@@ -5459,7 +5459,7 @@
getServices().injectBroadcast(mServiceContext, new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
.putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()),
callerUser.getIdentifier());
- flushTasks();
+ flushTasks(dpms);
final List<String> ownerInstalledCaCerts = new ArrayList<>();
@@ -5486,7 +5486,7 @@
getServices().injectBroadcast(mServiceContext, new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
.putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()),
callerUser.getIdentifier());
- flushTasks();
+ flushTasks(dpms);
// Verify that the CA cert is no longer reported as installed by the Device Owner / Profile
// Owner.
@@ -5530,7 +5530,7 @@
getServices().injectBroadcast(mServiceContext, new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
.putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()),
callerUser.getIdentifier());
- flushTasks();
+ flushTasks(dpms);
// Removing the Profile Owner should clear the information on which CA certs were installed
runAsCaller(admin1Context, dpms, dpm -> dpm.clearProfileOwner(admin1));
@@ -6311,7 +6311,7 @@
clearInvocations(getServices().alarmManager);
setUserUnlocked(CALLER_USER_HANDLE, false);
- sendBroadcastWithUser(Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
// Verify the alarm was scheduled for time when the warning should be shown.
verify(getServices().alarmManager, times(1))
@@ -6325,7 +6325,7 @@
// Pretend the alarm went off.
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_WARNING_TIME + 10);
- sendBroadcastWithUser(ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
// Verify the alarm was scheduled for the actual deadline this time.
verify(getServices().alarmManager, times(1)).set(anyInt(), eq(PROFILE_OFF_DEADLINE), any());
@@ -6340,7 +6340,7 @@
// Pretend the alarm went off.
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_DEADLINE + 10);
- sendBroadcastWithUser(ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
// Verify the alarm was not set.
verifyZeroInteractions(getServices().alarmManager);
@@ -6364,10 +6364,10 @@
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
setUserUnlocked(CALLER_USER_HANDLE, false);
- sendBroadcastWithUser(Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
clearInvocations(getServices().alarmManager);
setUserUnlocked(CALLER_USER_HANDLE, true);
- sendBroadcastWithUser(Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
// Verify that the alarm got discharged.
verify(getServices().alarmManager, times(1)).cancel((PendingIntent) null);
@@ -6384,16 +6384,16 @@
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
setUserUnlocked(CALLER_USER_HANDLE, false);
- sendBroadcastWithUser(Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
// Pretend the alarm went off.
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_WARNING_TIME + 10);
- sendBroadcastWithUser(ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
clearInvocations(getServices().alarmManager);
clearInvocations(getServices().notificationManager);
setUserUnlocked(CALLER_USER_HANDLE, true);
- sendBroadcastWithUser(Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
// Verify that the alarm got discharged.
verify(getServices().alarmManager, times(1)).cancel((PendingIntent) null);
@@ -6413,24 +6413,24 @@
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
setUserUnlocked(CALLER_USER_HANDLE, false);
- sendBroadcastWithUser(Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_STOPPED, CALLER_USER_HANDLE);
// Pretend the alarm went off after the deadline.
dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_DEADLINE + 10);
- sendBroadcastWithUser(ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, ACTION_PROFILE_OFF_DEADLINE, CALLER_USER_HANDLE);
clearInvocations(getServices().alarmManager);
clearInvocations(getServices().notificationManager);
clearInvocations(getServices().ipackageManager);
// Pretend the user clicked on the "apps suspended" notification to turn the profile on.
- sendBroadcastWithUser(ACTION_TURN_PROFILE_ON_NOTIFICATION, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, ACTION_TURN_PROFILE_ON_NOTIFICATION, CALLER_USER_HANDLE);
// Verify that the profile is turned on.
verify(getServices().userManager, times(1))
.requestQuietModeEnabled(eq(false), eq(UserHandle.of(CALLER_USER_HANDLE)));
setUserUnlocked(CALLER_USER_HANDLE, true);
- sendBroadcastWithUser(Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_UNLOCKED, CALLER_USER_HANDLE);
// Verify that the notification is removed (at this point DPC should show it).
verify(getServices().notificationManager, times(1))
@@ -6454,13 +6454,6 @@
.isEqualTo(DevicePolicyManager.PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT);
}
- private void sendBroadcastWithUser(String action, int userHandle) throws Exception {
- final Intent intent = new Intent(action);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
- getServices().injectBroadcast(mServiceContext, intent, userHandle);
- flushTasks();
- }
-
private void setUserUnlocked(int userHandle, boolean unlocked) {
when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked);
}
@@ -6471,10 +6464,6 @@
when(getServices().userManager.isUserUnlocked()).thenReturn(true);
- // Pretend our admin handles CHECK_POLICY_COMPLIANCE intent.
- final Intent intent = new Intent(ACTION_CHECK_POLICY_COMPLIANCE);
- intent.setPackage(admin1.getPackageName());
-
doReturn(Collections.singletonList(new ResolveInfo()))
.when(getServices().packageManager).queryIntentActivitiesAsUser(
any(Intent.class), anyInt(), eq(CALLER_USER_HANDLE));
@@ -6674,12 +6663,4 @@
return new StringParceledListSlice(Arrays.asList(s));
}
- private void flushTasks() throws Exception {
- dpms.mHandler.runWithScissors(() -> {}, 0 /*now*/);
- dpms.mBackgroundHandler.runWithScissors(() -> {}, 0 /*now*/);
-
- // We can't let exceptions happen on the background thread. Throw them here if they happen
- // so they still cause the test to fail despite being suppressed.
- getServices().rethrowBackgroundBroadcastExceptions();
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 9a1a5fb..41d54e9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -83,6 +83,23 @@
return mServices;
}
+ protected void sendBroadcastWithUser(DevicePolicyManagerServiceTestable dpms, String action,
+ int userHandle) throws Exception {
+ final Intent intent = new Intent(action);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
+ getServices().injectBroadcast(getContext(), intent, userHandle);
+ flushTasks(dpms);
+ }
+
+ protected void flushTasks(DevicePolicyManagerServiceTestable dpms) throws Exception {
+ dpms.mHandler.runWithScissors(() -> { }, 0 /*now*/);
+ dpms.mBackgroundHandler.runWithScissors(() -> { }, 0 /*now*/);
+
+ // We can't let exceptions happen on the background thread. Throw them here if they happen
+ // so they still cause the test to fail despite being suppressed.
+ getServices().rethrowBackgroundBroadcastExceptions();
+ }
+
protected interface DpmRunnable {
void run(DevicePolicyManager dpm) throws Exception;
}
@@ -180,7 +197,7 @@
* @param copyFromAdmin package information for {@code admin} will be built based on this
* component's information.
*/
- private void setUpPackageManagerForFakeAdmin(ComponentName admin, int packageUid,
+ protected void setUpPackageManagerForFakeAdmin(ComponentName admin, int packageUid,
Integer enabledSetting, Integer appTargetSdk, ComponentName copyFromAdmin)
throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index 60104d3..b09a3c3 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -117,10 +117,10 @@
@Test
public void testPredictTargets() {
- mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4", 0));
when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
@@ -165,12 +165,12 @@
@Test
public void testPredictTargets_reachTargetsLimit() {
- mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc5"));
- mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc6"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc5", 0));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc6", 0));
when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
@@ -250,6 +250,41 @@
}
@Test
+ public void testPredictTargets_noSharingHistoryRankedByShortcutRank() {
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1", 3));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2", 2));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3", 1));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4", 0));
+
+ when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class));
+ // "sc4" does not have a ConversationInfo.
+
+ mPredictor.predictTargets();
+
+ verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+ List<AppTarget> res = mAppTargetCaptor.getValue();
+ assertEquals(4, res.size());
+
+ assertEquals("sc4", res.get(0).getId().getId());
+ assertEquals(CLASS_2, res.get(0).getClassName());
+ assertEquals(PACKAGE_2, res.get(0).getPackageName());
+
+ assertEquals("sc3", res.get(1).getId().getId());
+ assertEquals(CLASS_2, res.get(1).getClassName());
+ assertEquals(PACKAGE_2, res.get(1).getPackageName());
+
+ assertEquals("sc2", res.get(2).getId().getId());
+ assertEquals(CLASS_1, res.get(2).getClassName());
+ assertEquals(PACKAGE_1, res.get(2).getPackageName());
+
+ assertEquals("sc1", res.get(3).getId().getId());
+ assertEquals(CLASS_1, res.get(3).getClassName());
+ assertEquals(PACKAGE_1, res.get(3).getPackageName());
+ }
+
+ @Test
public void testSortTargets() {
AppTarget appTarget1 = new AppTarget.Builder(
new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
@@ -348,19 +383,20 @@
}
private static ShareShortcutInfo buildShareShortcut(
- String packageName, String className, String shortcutId) {
- ShortcutInfo shortcutInfo = buildShortcut(packageName, shortcutId);
+ String packageName, String className, String shortcutId, int rank) {
+ ShortcutInfo shortcutInfo = buildShortcut(packageName, shortcutId, rank);
ComponentName componentName = new ComponentName(packageName, className);
return new ShareShortcutInfo(shortcutInfo, componentName);
}
- private static ShortcutInfo buildShortcut(String packageName, String shortcutId) {
+ private static ShortcutInfo buildShortcut(String packageName, String shortcutId, int rank) {
Context mockContext = mock(Context.class);
when(mockContext.getPackageName()).thenReturn(packageName);
when(mockContext.getUserId()).thenReturn(USER_ID);
when(mockContext.getUser()).thenReturn(UserHandle.of(USER_ID));
ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, shortcutId)
.setShortLabel(shortcutId)
+ .setRank(rank)
.setIntent(new Intent("TestIntent"));
return builder.build();
}
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 30df0d4..4040fa6 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -54,6 +54,8 @@
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityViewTestActivity" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityInActivityView"
android:resizeableActivity="true" />
+ <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityLaunchesNewActivityInActivityView"
+ android:resizeableActivity="true" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$LandscapeActivity"
android:screenOrientation="sensorLandscape"
android:showWhenLocked="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index 2c17bbe..2bd3424 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -299,6 +299,20 @@
waitForCallback(singleTaskDisplayDrawnLatch);
}
+ public static class ActivityLaunchesNewActivityInActivityView extends TestActivity {
+ private boolean mActivityBLaunched = false;
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ if (mActivityBLaunched) {
+ return;
+ }
+ mActivityBLaunched = true;
+ startActivity(new Intent(this, ActivityB.class));
+ }
+ }
+
@Test
public void testSingleTaskDisplayEmpty() throws Exception {
final Instrumentation instrumentation = getInstrumentation();
@@ -335,13 +349,20 @@
});
waitForCallback(activityViewReadyLatch);
+ // 1. start ActivityLaunchesNewActivityInActivityView in an ActivityView
+ // 2. ActivityLaunchesNewActivityInActivityView launches ActivityB
+ // 3. ActivityB finishes self.
+ // 4. Verify ITaskStackListener#onSingleTaskDisplayEmpty is not called yet.
final Context context = instrumentation.getContext();
- Intent intent = new Intent(context, ActivityInActivityView.class);
+ Intent intent = new Intent(context, ActivityLaunchesNewActivityInActivityView.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
activityView.startActivity(intent);
waitForCallback(singleTaskDisplayDrawnLatch);
+ UiDevice.getInstance(getInstrumentation()).waitForIdle();
assertEquals(1, singleTaskDisplayEmptyLatch.getCount());
+ // 5. Release the container, and ActivityLaunchesNewActivityInActivityView finishes.
+ // 6. Verify ITaskStackListener#onSingleTaskDisplayEmpty is called.
activityView.release();
waitForCallback(activityViewDestroyedLatch);
waitForCallback(singleTaskDisplayEmptyLatch);
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 1993550..e6279dc 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -366,7 +366,7 @@
.append(" mPci=").append(mPci)
.append(" mTac=").append(mTac)
.append(" mEarfcn=").append(mEarfcn)
- .append(" mBands=").append(mBands)
+ .append(" mBands=").append(Arrays.toString(mBands))
.append(" mBandwidth=").append(mBandwidth)
.append(" mMcc=").append(mMccStr)
.append(" mMnc=").append(mMncStr)
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 8dd7bdd..e34bbfc 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -242,7 +242,7 @@
.append(" mPci = ").append(mPci)
.append(" mTac = ").append(mTac)
.append(" mNrArfcn = ").append(mNrArfcn)
- .append(" mBands = ").append(mBands)
+ .append(" mBands = ").append(Arrays.toString(mBands))
.append(" mMcc = ").append(mMccStr)
.append(" mMnc = ").append(mMncStr)
.append(" mNci = ").append(mNci)
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 9c5b7b6..941ff61 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -12,6 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+java_defaults {
+ name: "wifi-module-sdk-version-defaults",
+ min_sdk_version: "30",
+ target_sdk_version: "30",
+}
+
filegroup {
name: "framework-wifi-updatable-exported-aidl-sources",
srcs: ["aidl-export/**/*.aidl"],
@@ -73,6 +79,7 @@
// classes before they are renamed.
java_library {
name: "framework-wifi-pre-jarjar",
+ defaults: ["wifi-module-sdk-version-defaults"],
sdk_version: "module_current",
static_libs: [
"framework-wifi-util-lib",
@@ -98,7 +105,10 @@
// post-jarjar version of framework-wifi
java_sdk_library {
name: "framework-wifi",
- defaults: ["framework-module-defaults"],
+ defaults: [
+ "framework-module-defaults",
+ "wifi-module-sdk-version-defaults",
+ ],
static_libs: [
"framework-wifi-util-lib",
"android.hardware.wifi-V1.0-java-constants",
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
index 18b26db..dcb57ec 100644
--- a/wifi/java/android/net/wifi/SoftApCapability.java
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -102,7 +102,9 @@
/**
* Returns true when all of the queried features are supported, otherwise false.
*
- * @param features One or combination of the features from {@link @HotspotFeatures}
+ * @param features One or combination of the following features:
+ * {@link #SOFTAP_FEATURE_ACS_OFFLOAD}, {@link #SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} or
+ * {@link #SOFTAP_FEATURE_WPA3_SAE}.
*/
public boolean areFeaturesSupported(@HotspotFeatures long features) {
return (mSupportedFeatures & features) == features;
@@ -122,7 +124,9 @@
* Constructor with combination of the feature.
* Zero to no supported feature.
*
- * @param features One or combination of the features from {@link @HotspotFeatures}.
+ * @param features One or combination of the following features:
+ * {@link #SOFTAP_FEATURE_ACS_OFFLOAD}, {@link #SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} or
+ * {@link #SOFTAP_FEATURE_WPA3_SAE}.
* @hide
*/
public SoftApCapability(@HotspotFeatures long features) {
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 457e0db..2bcd4f4 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -165,7 +165,8 @@
/**
* The operating band of the AP.
- * One of the band types from {@link @BandType}.
+ * One or combination of the following band type:
+ * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
*/
private final @BandType int mBand;
@@ -181,7 +182,11 @@
/**
* The operating security type of the AP.
- * One of the security types from {@link @SecurityType}
+ * One of the following security types:
+ * {@link #SECURITY_TYPE_OPEN},
+ * {@link #SECURITY_TYPE_WPA2_PSK},
+ * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
+ * {@link #SECURITY_TYPE_WPA3_SAE}
*/
private final @SecurityType int mSecurityType;
@@ -393,8 +398,12 @@
}
/**
- * Returns {@link BandType} set to be the band for the AP.
- * {@link Builder#setBand(@BandType int)}.
+ * Returns band type set to be the band for the AP.
+ *
+ * One or combination of the following band type:
+ * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
+ *
+ * {@link Builder#setBand(int)}.
*
* @hide
*/
@@ -679,15 +688,19 @@
/**
* Specifies that this AP should use specific security type with the given ASCII passphrase.
*
- * @param securityType one of the security types from {@link @SecurityType}.
- * @param passphrase The passphrase to use for sepcific {@link @SecurityType} configuration
- * or null with {@link @SecurityType#SECURITY_TYPE_OPEN}.
+ * @param securityType One of the following security types:
+ * {@link #SECURITY_TYPE_OPEN},
+ * {@link #SECURITY_TYPE_WPA2_PSK},
+ * {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION},
+ * {@link #SECURITY_TYPE_WPA3_SAE}.
+ * @param passphrase The passphrase to use for sepcific {@code securityType} configuration
+ * or null with {@link #SECURITY_TYPE_OPEN}.
*
* @return Builder for chaining.
* @throws IllegalArgumentException when the passphrase length is invalid and
- * {@code securityType} is not {@link @SecurityType#SECURITY_TYPE_OPEN}
+ * {@code securityType} is not {@link #SECURITY_TYPE_OPEN}
* or non-null passphrase and {@code securityType} is
- * {@link @SecurityType#SECURITY_TYPE_OPEN}.
+ * {@link #SECURITY_TYPE_OPEN}.
*/
@NonNull
public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) {
@@ -735,9 +748,10 @@
/**
* Specifies the band for the AP.
* <p>
- * <li>If not set, defaults to BAND_2GHZ {@link @BandType}.</li>
+ * <li>If not set, defaults to {@link #BAND_2GHZ}.</li>
*
- * @param band One or combination of the band types from {@link @BandType}.
+ * @param band One or combination of the following band type:
+ * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}.
* @return Builder for chaining.
*/
@NonNull
@@ -758,7 +772,7 @@
* <p>
* The default for the channel is a the special value 0 to have the framework
* auto-select a valid channel from the band configured with
- * {@link #setBand(@BandType int)}.
+ * {@link #setBand(int)}.
*
* The channel auto selection will offload to driver when
* {@link SoftApCapability#areFeaturesSupported(
diff --git a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
index 3215246..4116234 100644
--- a/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -1039,11 +1039,11 @@
* The result depends on the on the country code that has been set.
*
* @param band as specified by one of the WifiScanner.WIFI_BAND_* constants.
- * The following bands are supported {@link @WifiScanner.WifiBandBasic}:
- * WifiScanner.WIFI_BAND_24_GHZ
- * WifiScanner.WIFI_BAND_5_GHZ
- * WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
- * WifiScanner.WIFI_BAND_6_GHZ
+ * The following bands are supported:
+ * {@link WifiScanner#WIFI_BAND_24_GHZ},
+ * {@link WifiScanner#WIFI_BAND_5_GHZ},
+ * {@link WifiScanner#WIFI_BAND_5_GHZ_DFS_ONLY},
+ * {@link WifiScanner#WIFI_BAND_6_GHZ}
* @return frequencies vector of valid frequencies (MHz), or an empty array for error.
* @throws IllegalArgumentException if band is not recognized.
*/