Merge "DND alert info set to INVISIBLE (not GONE) to retain formatting." into oc-mr1-dev
diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone
index c829728..c1cbb64 100644
--- a/config/compiled-classes-phone
+++ b/config/compiled-classes-phone
@@ -5243,7 +5243,6 @@
com.android.internal.app.IVoiceInteractor$Stub
com.android.internal.app.NightDisplayController
com.android.internal.app.NightDisplayController$Callback
-com.android.internal.app.NightDisplayController$LocalTime
com.android.internal.app.ProcessMap
com.android.internal.app.ResolverActivity
com.android.internal.app.ToolbarActionBar
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 1b8ab49..ff61d18 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2784,7 +2784,6 @@
com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor$Stub
com.android.internal.app.NightDisplayController
-com.android.internal.app.NightDisplayController$1
com.android.internal.appwidget.IAppWidgetService
com.android.internal.appwidget.IAppWidgetService$Stub
com.android.internal.appwidget.IAppWidgetService$Stub$Proxy
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 897e42b..1811748 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -308,7 +308,15 @@
boolean shouldUpRecreateTask(in IBinder token, in String destAffinity);
boolean navigateUpTo(in IBinder token, in Intent target, int resultCode,
in Intent resultData);
- void setLockScreenShown(boolean showing);
+ /**
+ * Informs ActivityManagerService that the keyguard is showing.
+ *
+ * @param showing True if the keyguard is showing, false otherwise.
+ * @param secondaryDisplayShowing The displayId of the secondary display on which the keyguard
+ * is showing, or INVALID_DISPLAY if there is no such display. Only meaningful if
+ * showing is true.
+ */
+ void setLockScreenShown(boolean showing, int secondaryDisplayShowing);
boolean finishActivityAffinity(in IBinder token);
// This is not public because you need to be very careful in how you
// manage your activity to make sure it is always the uid you expect.
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 363e20a7..5786238 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -62,7 +62,10 @@
* @param callback The callback to register.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.RESTRICTED_VR_ACCESS,
+ android.Manifest.permission.ACCESS_VR_STATE
+ })
public void registerVrStateCallback(VrStateCallback callback, @NonNull Handler handler) {
if (callback == null || mCallbackMap.containsKey(callback)) {
return;
@@ -88,7 +91,10 @@
* @param callback The callback to deregister.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.RESTRICTED_VR_ACCESS,
+ android.Manifest.permission.ACCESS_VR_STATE
+ })
public void unregisterVrStateCallback(VrStateCallback callback) {
CallbackEntry entry = mCallbackMap.remove(callback);
if (entry != null) {
@@ -110,7 +116,10 @@
* Returns the current VrMode state.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_VR_STATE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.RESTRICTED_VR_ACCESS,
+ android.Manifest.permission.ACCESS_VR_STATE
+ })
public boolean getVrModeEnabled() {
try {
return mService.getVrModeState();
@@ -124,7 +133,10 @@
* Returns the current VrMode state.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_VR_STATE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.RESTRICTED_VR_ACCESS,
+ android.Manifest.permission.ACCESS_VR_STATE
+ })
public boolean getPersistentVrModeEnabled() {
try {
return mService.getPersistentVrModeEnabled();
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index 1a51acd..cc484ea 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -18,7 +18,6 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.ImageFormat;
@@ -37,6 +36,7 @@
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
+import java.util.Locale;
import java.util.TimeZone;
/**
@@ -122,7 +122,7 @@
// Create this fresh each time since the time zone may change while a long-running application
// is active.
final DateFormat dateTimeStampFormat =
- new SimpleDateFormat(TIFF_DATETIME_FORMAT);
+ new SimpleDateFormat(TIFF_DATETIME_FORMAT, Locale.US);
dateTimeStampFormat.setTimeZone(TimeZone.getDefault());
// Format for metadata
@@ -472,7 +472,8 @@
private static final String GPS_DATE_FORMAT_STR = "yyyy:MM:dd";
private static final String TIFF_DATETIME_FORMAT = "yyyy:MM:dd HH:mm:ss";
- private static final DateFormat sExifGPSDateStamp = new SimpleDateFormat(GPS_DATE_FORMAT_STR);
+ private static final DateFormat sExifGPSDateStamp =
+ new SimpleDateFormat(GPS_DATE_FORMAT_STR, Locale.US);
private final Calendar mGPSTimeStampCalendar = Calendar
.getInstance(TimeZone.getTimeZone("UTC"));
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
index ac6d83f..2623830 100644
--- a/core/java/android/hardware/location/NanoAppInstanceInfo.java
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -287,8 +287,10 @@
mPublisher = in.readString();
mName = in.readString();
+ mHandle = in.readInt();
mAppId = in.readLong();
mAppVersion = in.readInt();
+ mContexthubId = in.readInt();
mNeededReadMemBytes = in.readInt();
mNeededWriteMemBytes = in.readInt();
mNeededExecMemBytes = in.readInt();
@@ -309,6 +311,8 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(mPublisher);
out.writeString(mName);
+
+ out.writeInt(mHandle);
out.writeLong(mAppId);
out.writeInt(mAppVersion);
out.writeInt(mContexthubId);
diff --git a/core/java/android/net/metrics/WakeupEvent.java b/core/java/android/net/metrics/WakeupEvent.java
new file mode 100644
index 0000000..cbf3fc8
--- /dev/null
+++ b/core/java/android/net/metrics/WakeupEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+/**
+ * An event logged when NFLOG notifies userspace of a wakeup packet for
+ * watched interfaces.
+ * {@hide}
+ */
+public class WakeupEvent {
+ public String iface;
+ public long timestampMs;
+ public int uid;
+
+ @Override
+ public String toString() {
+ return String.format("WakeupEvent(%tT.%tL, %s, uid: %d)",
+ timestampMs, timestampMs, iface, uid);
+ }
+}
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
new file mode 100644
index 0000000..97e83f9
--- /dev/null
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Process;
+import android.os.SystemClock;
+
+/**
+ * An event logged per interface and that aggregates WakeupEvents for that interface.
+ * {@hide}
+ */
+public class WakeupStats {
+
+ private static final int NO_UID = -1;
+
+ public final long creationTimeMs = SystemClock.elapsedRealtime();
+ public final String iface;
+
+ public long totalWakeups = 0;
+ public long rootWakeups = 0;
+ public long systemWakeups = 0;
+ public long nonApplicationWakeups = 0;
+ public long applicationWakeups = 0;
+ public long noUidWakeups = 0;
+ public long durationSec = 0;
+
+ public WakeupStats(String iface) {
+ this.iface = iface;
+ }
+
+ /** Update durationSec with current time. */
+ public void updateDuration() {
+ durationSec = (SystemClock.elapsedRealtime() - creationTimeMs) / 1000;
+ }
+
+ /** Update wakeup counters for the given WakeupEvent. */
+ public void countEvent(WakeupEvent ev) {
+ totalWakeups++;
+ switch (ev.uid) {
+ case Process.ROOT_UID:
+ rootWakeups++;
+ break;
+ case Process.SYSTEM_UID:
+ systemWakeups++;
+ break;
+ case NO_UID:
+ noUidWakeups++;
+ break;
+ default:
+ if (ev.uid >= Process.FIRST_APPLICATION_UID) {
+ applicationWakeups++;
+ } else {
+ nonApplicationWakeups++;
+ }
+ break;
+ }
+ }
+
+ @Override
+ public String toString() {
+ updateDuration();
+ return new StringBuilder()
+ .append("WakeupStats(").append(iface)
+ .append(", total: ").append(totalWakeups)
+ .append(", root: ").append(rootWakeups)
+ .append(", system: ").append(systemWakeups)
+ .append(", apps: ").append(applicationWakeups)
+ .append(", non-apps: ").append(nonApplicationWakeups)
+ .append(", no uid: ").append(noUidWakeups)
+ .append(", ").append(durationSec).append("s)")
+ .toString();
+ }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 507edc4..54f587e 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6931,8 +6931,9 @@
public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
/**
- * Time in milliseconds (since epoch) when Night display was last activated. Use to decide
- * whether to apply the current activated state after a reboot or user change.
+ * A String representing the LocalDateTime when Night display was last activated. Use to
+ * decide whether to apply the current activated state after a reboot or user change. In
+ * legacy cases, this is represented by the time in milliseconds (since epoch).
* @hide
*/
public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME =
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index fc56455..2c83fc4 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -32,6 +32,7 @@
import android.text.TextUtils;
import android.view.View;
+import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.util.Locale;
@@ -194,13 +195,29 @@
/**
* ICU doesn't support PETABYTE yet. Fake it so that we can treat all units the same way.
- * {@hide}
*/
- public static final MeasureUnit PETABYTE = MeasureUnit.internalGetInstance(
- "digital", "petabyte");
+ private static final MeasureUnit PETABYTE = createPetaByte();
- /** {@hide} */
- public static class RoundedBytesResult {
+ /**
+ * Create a petabyte MeasureUnit without registering it with ICU.
+ * ICU doesn't support user-create MeasureUnit and the only public (but hidden) method to do so
+ * is {@link MeasureUnit#internalGetInstance(String, String)} which also registers the unit as
+ * an available type and thus leaks it to code that doesn't expect or support it.
+ * <p>This method uses reflection to create an instance of MeasureUnit to avoid leaking it. This
+ * instance is <b>only</b> to be used in this class.
+ */
+ private static MeasureUnit createPetaByte() {
+ try {
+ Constructor<MeasureUnit> constructor = MeasureUnit.class
+ .getDeclaredConstructor(String.class, String.class);
+ constructor.setAccessible(true);
+ return constructor.newInstance("digital", "petabyte");
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException("Failed to create petabyte MeasureUnit", e);
+ }
+ }
+
+ private static class RoundedBytesResult {
public final float value;
public final MeasureUnit units;
public final int fractionDigits;
@@ -218,7 +235,7 @@
* Returns a RoundedBytesResult object based on the input size in bytes and the rounding
* flags. The result can be used for formatting.
*/
- public static RoundedBytesResult roundBytes(long sizeBytes, int flags) {
+ static RoundedBytesResult roundBytes(long sizeBytes, int flags) {
final boolean isNegative = (sizeBytes < 0);
float result = isNegative ? -sizeBytes : sizeBytes;
MeasureUnit units = MeasureUnit.BYTE;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index cac27af..462dad3 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -231,6 +231,7 @@
updateRequestedVisibility();
mAttachedToWindow = true;
+ mParent.requestTransparentRegion(SurfaceView.this);
if (!mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
observer.addOnScrollChangedListener(mScrollChangedListener);
@@ -269,8 +270,6 @@
if (mPendingReportDraws > 0) {
mDrawFinished = true;
if (mAttachedToWindow) {
- mParent.requestTransparentRegion(SurfaceView.this);
-
notifyDrawFinished();
invalidate();
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d030c93..166d6b7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -127,6 +127,7 @@
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -1078,6 +1079,29 @@
* <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
* value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}</code>).
*
+ * <p>When annotating a view with this hint, it's recommended to use a date autofill value to
+ * avoid ambiguity when the autofill service provides a value for it. To understand why a
+ * value can be ambiguous, consider "April of 2020", which could be represented as either of
+ * the following options:
+ *
+ * <ul>
+ * <li>{@code "04/2020"}
+ * <li>{@code "4/2020"}
+ * <li>{@code "2020/04"}
+ * <li>{@code "2020/4"}
+ * <li>{@code "April/2020"}
+ * <li>{@code "Apr/2020"}
+ * </ul>
+ *
+ * <p>You define a date autofill value for the view by overriding the following methods:
+ *
+ * <ol>
+ * <li>{@link #getAutofillType()} to return {@link #AUTOFILL_TYPE_DATE}.
+ * <li>{@link #getAutofillValue()} to return a
+ * {@link AutofillValue#forDate(long) date autofillvalue}.
+ * <li>{@link #autofill(AutofillValue)} to expect a data autofillvalue.
+ * </ol>
+ *
* <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE =
@@ -1090,6 +1114,22 @@
* <a href="#attr_android:autofillHint"> {@code android:autofillHint}</a> (in which case the
* value should be <code>{@value #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH}</code>).
*
+ * <p>When annotating a view with this hint, it's recommended to use a text autofill value
+ * whose value is the numerical representation of the month, starting on {@code 1} to avoid
+ * ambiguity when the autofill service provides a value for it. To understand why a
+ * value can be ambiguous, consider "January", which could be represented as either of
+ *
+ * <ul>
+ * <li>{@code "1"}: recommended way.
+ * <li>{@code "0"}: if following the {@link Calendar#MONTH} convention.
+ * <li>{@code "January"}: full name, in English.
+ * <li>{@code "jan"}: abbreviated name, in English.
+ * <li>{@code "Janeiro"}: full name, in another language.
+ * </ul>
+ *
+ * <p>Another recommended approach is to use a date autofill value - see
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE} for more details.
+ *
* <p>See {@link #setAutofillHints(String...)} for more info about autofill hints.
*/
public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH =
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 61cbce9..f888ba2 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -37,14 +37,13 @@
import android.service.autofill.FillEventHistory;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.DebugUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -202,9 +201,12 @@
* Initial state of the autofill context, set when there is no session (i.e., when
* {@link #mSessionId} is {@link #NO_SESSION}).
*
+ * <p>In this state, app callbacks (such as {@link #notifyViewEntered(View)}) are notified to
+ * the server.
+ *
* @hide
*/
- public static final int STATE_UNKNOWN = 1;
+ public static final int STATE_UNKNOWN = 0;
/**
* State where the autofill context hasn't been {@link #commit() finished} nor
@@ -212,7 +214,18 @@
*
* @hide
*/
- public static final int STATE_ACTIVE = 2;
+ public static final int STATE_ACTIVE = 1;
+
+ /**
+ * State where the autofill context was finished by the server because the autofill
+ * service could not autofill the page.
+ *
+ * <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
+ * exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
+ *
+ * @hide
+ */
+ public static final int STATE_FINISHED = 2;
/**
* State where the autofill context has been {@link #commit() finished} but the server still has
@@ -220,7 +233,7 @@
*
* @hide
*/
- public static final int STATE_SHOWING_SAVE_UI = 4;
+ public static final int STATE_SHOWING_SAVE_UI = 3;
/**
* Makes an authentication id from a request id and a dataset id.
@@ -559,6 +572,14 @@
}
AutofillCallback callback = null;
synchronized (mLock) {
+ if (isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
+ if (sVerbose) {
+ Log.v(TAG, "notifyViewEntered(flags=" + flags + ", view=" + view
+ + "): ignored on state " + getStateAsStringLocked());
+ }
+ return;
+ }
+
ensureServiceClientAddedIfNeededLocked();
if (!mEnabled) {
@@ -682,6 +703,14 @@
}
AutofillCallback callback = null;
synchronized (mLock) {
+ if (isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
+ if (sVerbose) {
+ Log.v(TAG, "notifyViewEntered(flags=" + flags + ", view=" + view
+ + ", virtualId=" + virtualId
+ + "): ignored on state " + getStateAsStringLocked());
+ }
+ return;
+ }
ensureServiceClientAddedIfNeededLocked();
if (!mEnabled) {
@@ -765,6 +794,10 @@
}
if (!mEnabled || !isActiveLocked()) {
+ if (sVerbose && mEnabled) {
+ Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
+ + getStateAsStringLocked());
+ }
return;
}
@@ -950,10 +983,13 @@
@NonNull AutofillValue value, int flags) {
if (sVerbose) {
Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
- + ", flags=" + flags + ", state=" + mState);
+ + ", flags=" + flags + ", state=" + getStateAsStringLocked());
}
- if (mState != STATE_UNKNOWN) {
- if (sDebug) Log.d(TAG, "not starting session for " + id + " on state " + mState);
+ if (mState != STATE_UNKNOWN && (flags & FLAG_MANUAL_REQUEST) == 0) {
+ if (sVerbose) {
+ Log.v(TAG, "not automatically starting session for " + id
+ + " on state " + getStateAsStringLocked());
+ }
return;
}
try {
@@ -973,7 +1009,7 @@
}
private void finishSessionLocked() {
- if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + mState);
+ if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
if (!isActiveLocked()) return;
@@ -987,7 +1023,7 @@
}
private void cancelSessionLocked() {
- if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + mState);
+ if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
if (!isActiveLocked()) return;
@@ -1245,10 +1281,10 @@
}
}
- final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
- log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
- log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED,
- numApplied);
+ final LogMaker log = new LogMaker(MetricsEvent.AUTOFILL_DATASET_APPLIED)
+ .setPackageName(mContext.getPackageName())
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
mMetricsLogger.write(log);
}
}
@@ -1306,6 +1342,14 @@
}
}
+ private void setSessionFinished() {
+ if (sVerbose) Log.v(TAG, "setSessionFinished()");
+ synchronized (mLock) {
+ resetSessionLocked();
+ mState = STATE_FINISHED;
+ }
+ }
+
private void requestHideFillUi(AutofillId id) {
final View anchor = findView(id);
if (sVerbose) Log.v(TAG, "requestHideFillUi(" + id + "): anchor = " + anchor);
@@ -1341,7 +1385,11 @@
}
}
- private void notifyNoFillUi(int sessionId, AutofillId id) {
+ private void notifyNoFillUi(int sessionId, AutofillId id, boolean sessionFinished) {
+ if (sVerbose) {
+ Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
+ + ", finished=" + sessionFinished);
+ }
final View anchor = findView(id);
if (anchor == null) {
return;
@@ -1361,7 +1409,11 @@
} else {
callback.onAutofillEvent(anchor, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
}
+ }
+ if (sessionFinished) {
+ // Callback call was "hijacked" to also update the session state.
+ setSessionFinished();
}
}
@@ -1434,8 +1486,7 @@
pw.print(outerPrefix); pw.println("AutofillManager:");
final String pfx = outerPrefix + " ";
pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
- pw.print(pfx); pw.print("state: "); pw.println(
- DebugUtils.flagsToString(AutofillManager.class, "STATE_", mState));
+ pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
@@ -1452,10 +1503,29 @@
pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
}
+ private String getStateAsStringLocked() {
+ switch (mState) {
+ case STATE_UNKNOWN:
+ return "STATE_UNKNOWN";
+ case STATE_ACTIVE:
+ return "STATE_ACTIVE";
+ case STATE_FINISHED:
+ return "STATE_FINISHED";
+ case STATE_SHOWING_SAVE_UI:
+ return "STATE_SHOWING_SAVE_UI";
+ default:
+ return "INVALID:" + mState;
+ }
+ }
+
private boolean isActiveLocked() {
return mState == STATE_ACTIVE;
}
+ private boolean isFinishedLocked() {
+ return mState == STATE_FINISHED;
+ }
+
private void post(Runnable runnable) {
final AutofillClient client = getClientLocked();
if (client == null) {
@@ -1787,10 +1857,10 @@
}
@Override
- public void notifyNoFillUi(int sessionId, AutofillId id) {
+ public void notifyNoFillUi(int sessionId, AutofillId id, boolean sessionFinished) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.notifyNoFillUi(sessionId, id));
+ afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinished));
}
}
@@ -1823,7 +1893,15 @@
public void setSaveUiState(int sessionId, boolean shown) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() ->afm.setSaveUiState(sessionId, shown));
+ afm.post(() -> afm.setSaveUiState(sessionId, shown));
+ }
+ }
+
+ @Override
+ public void setSessionFinished() {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.setSessionFinished());
}
}
}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 0eae858..db6855a 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -67,9 +67,9 @@
void requestHideFillUi(int sessionId, in AutofillId id);
/**
- * Notifies no fill UI will be shown.
+ * Notifies no fill UI will be shown, and also mark the state as finished if necessary.
*/
- void notifyNoFillUi(int sessionId, in AutofillId id);
+ void notifyNoFillUi(int sessionId, in AutofillId id, boolean sessionFinished);
/**
* Starts the provided intent sender.
@@ -80,4 +80,10 @@
* Sets the state of the Autofill Save UI for a given session.
*/
void setSaveUiState(int sessionId, boolean shown);
+
+ /**
+ * Marks the state of the session as finished (because the AutofillService returned a null
+ * FillResponse).
+ */
+ void setSessionFinished();
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f918cad..637b60e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2723,25 +2723,20 @@
* {@code IFRAME}, in which case it would be treated the same way as multiple forms described
* above, except that the {@link ViewStructure#setWebDomain(String) web domain} of the
* {@code FORM} contains the {@code src} attribute from the {@code IFRAME} node.
- * <li>If the Android SDK provides a similar View, then should be set with the
- * fully-qualified class name of such view.
* <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to
- * {@link ViewStructure#setAutofillHints(String[])}.
- * <li>The {@code type} attribute of {@code INPUT} tags maps to
- * {@link ViewStructure#setInputType(int)}.
- * <li>The {@code value} attribute of {@code INPUT} tags maps to
- * {@link ViewStructure#setText(CharSequence)}.
- * <li>If the view is editalbe, the {@link ViewStructure#setAutofillType(int)} and
+ * {@link ViewStructure#setAutofillHints(String[])}.
+ * <li>If the view is editable, the {@link ViewStructure#setAutofillType(int)} and
* {@link ViewStructure#setAutofillValue(AutofillValue)} must be set.
* <li>The {@code placeholder} attribute maps to {@link ViewStructure#setHint(CharSequence)}.
* <li>Other HTML attributes can be represented through
* {@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}.
* </ol>
*
- * <p>It should also call {@code structure.setDataIsSensitive(false)} for fields whose value
- * were not dynamically changed (for example, through Javascript).
+ * <p>If the WebView implementation can determine that the value of a field was set statically
+ * (for example, not through Javascript), it should also call
+ * {@code structure.setDataIsSensitive(false)}.
*
- * <p>Example1: an HTML form with 2 fields for username and password.
+ * <p>For example, an HTML form with 2 fields for username and password:
*
* <pre class="prettyprint">
* <input type="text" name="username" id="user" value="Type your username" autocomplete="username" placeholder="Email or username">
@@ -2754,51 +2749,27 @@
* int index = structure.addChildCount(2);
* ViewStructure username = structure.newChild(index);
* username.setAutofillId(structure.getAutofillId(), 1); // id 1 - first child
- * username.setClassName("input");
- * username.setInputType("android.widget.EditText");
* username.setAutofillHints("username");
* username.setHtmlInfo(username.newHtmlInfoBuilder("input")
* .addAttribute("type", "text")
* .addAttribute("name", "username")
- * .addAttribute("id", "user")
* .build());
* username.setHint("Email or username");
* username.setAutofillType(View.AUTOFILL_TYPE_TEXT);
* username.setAutofillValue(AutofillValue.forText("Type your username"));
- * username.setText("Type your username");
- * // Value of the field is not sensitive because it was not dynamically changed:
+ * // Value of the field is not sensitive because it was created statically and not changed.
* username.setDataIsSensitive(false);
*
* ViewStructure password = structure.newChild(index + 1);
* username.setAutofillId(structure, 2); // id 2 - second child
- * password.setInputType("android.widget.EditText");
- * password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
* password.setAutofillHints("current-password");
* password.setHtmlInfo(password.newHtmlInfoBuilder("input")
* .addAttribute("type", "password")
* .addAttribute("name", "password")
- * .addAttribute("id", "pass")
* .build());
* password.setHint("Password");
* password.setAutofillType(View.AUTOFILL_TYPE_TEXT);
* </pre>
- *
- * <p>Example2: an IFRAME tag.
- *
- * <pre class="prettyprint">
- * <iframe src="https://example.com/login"/>
- * </pre>
- *
- * <p>Would map to:
- *
- * <pre class="prettyprint">
- * int index = structure.addChildCount(1);
- * ViewStructure iframe = structure.newChildFor(index);
- * iframe.setAutofillId(structure.getAutofillId(), 1);
- * iframe.setHtmlInfo(iframe.newHtmlInfoBuilder("iframe")
- * .addAttribute("src", "https://example.com/login")
- * .build());
- * </pre>
*/
@Override
public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 05f7c0a..f8b6837 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -61,7 +61,10 @@
// This value will be set to 0 as soon as the first tab is added to TabHost.
private int mSelectedTab = -1;
+ @Nullable
private Drawable mLeftStrip;
+
+ @Nullable
private Drawable mRightStrip;
private boolean mDrawBottomStrips = true;
@@ -374,23 +377,36 @@
final Drawable leftStrip = mLeftStrip;
final Drawable rightStrip = mRightStrip;
- leftStrip.setState(selectedChild.getDrawableState());
- rightStrip.setState(selectedChild.getDrawableState());
+ if (leftStrip != null) {
+ leftStrip.setState(selectedChild.getDrawableState());
+ }
+ if (rightStrip != null) {
+ rightStrip.setState(selectedChild.getDrawableState());
+ }
if (mStripMoved) {
final Rect bounds = mBounds;
bounds.left = selectedChild.getLeft();
bounds.right = selectedChild.getRight();
final int myHeight = getHeight();
- leftStrip.setBounds(Math.min(0, bounds.left - leftStrip.getIntrinsicWidth()),
- myHeight - leftStrip.getIntrinsicHeight(), bounds.left, myHeight);
- rightStrip.setBounds(bounds.right, myHeight - rightStrip.getIntrinsicHeight(),
- Math.max(getWidth(), bounds.right + rightStrip.getIntrinsicWidth()), myHeight);
+ if (leftStrip != null) {
+ leftStrip.setBounds(Math.min(0, bounds.left - leftStrip.getIntrinsicWidth()),
+ myHeight - leftStrip.getIntrinsicHeight(), bounds.left, myHeight);
+ }
+ if (rightStrip != null) {
+ rightStrip.setBounds(bounds.right, myHeight - rightStrip.getIntrinsicHeight(),
+ Math.max(getWidth(), bounds.right + rightStrip.getIntrinsicWidth()),
+ myHeight);
+ }
mStripMoved = false;
}
- leftStrip.draw(canvas);
- rightStrip.draw(canvas);
+ if (leftStrip != null) {
+ leftStrip.draw(canvas);
+ }
+ if (rightStrip != null) {
+ rightStrip.draw(canvas);
+ }
}
/**
diff --git a/core/java/com/android/internal/app/NightDisplayController.java b/core/java/com/android/internal/app/NightDisplayController.java
index 860c5c4..7a1383c 100644
--- a/core/java/com/android/internal/app/NightDisplayController.java
+++ b/core/java/com/android/internal/app/NightDisplayController.java
@@ -32,8 +32,12 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Calendar;
-import java.util.Locale;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
/**
* Controller for managing Night display settings.
@@ -116,8 +120,9 @@
*/
public boolean setActivated(boolean activated) {
if (isActivated() != activated) {
- Secure.putLongForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, System.currentTimeMillis(),
+ Secure.putStringForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ LocalDateTime.now().toString(),
mUserId);
}
return Secure.putIntForUser(mContext.getContentResolver(),
@@ -128,17 +133,22 @@
* Returns the time when Night display's activation state last changed, or {@code null} if it
* has never been changed.
*/
- public Calendar getLastActivatedTime() {
+ public LocalDateTime getLastActivatedTime() {
final ContentResolver cr = mContext.getContentResolver();
- final long lastActivatedTimeMillis = Secure.getLongForUser(
- cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1, mUserId);
- if (lastActivatedTimeMillis < 0) {
- return null;
+ final String lastActivatedTime = Secure.getStringForUser(
+ cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
+ if (lastActivatedTime != null) {
+ try {
+ return LocalDateTime.parse(lastActivatedTime);
+ } catch (DateTimeParseException ignored) {}
+ // Uses the old epoch time.
+ try {
+ return LocalDateTime.ofInstant(
+ Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
+ ZoneId.systemDefault());
+ } catch (DateTimeException|NumberFormatException ignored) {}
}
-
- final Calendar lastActivatedTime = Calendar.getInstance();
- lastActivatedTime.setTimeInMillis(lastActivatedTimeMillis);
- return lastActivatedTime;
+ return null;
}
/**
@@ -183,8 +193,10 @@
}
if (getAutoMode() != autoMode) {
- Secure.putLongForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1L, mUserId);
+ Secure.putStringForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ null,
+ mUserId);
}
return Secure.putIntForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
@@ -206,7 +218,7 @@
R.integer.config_defaultNightDisplayCustomStartTime);
}
- return LocalTime.valueOf(startTimeValue);
+ return LocalTime.ofSecondOfDay(startTimeValue / 1000);
}
/**
@@ -221,7 +233,7 @@
throw new IllegalArgumentException("startTime cannot be null");
}
return Secure.putIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId);
+ Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toSecondOfDay() * 1000, mUserId);
}
/**
@@ -240,7 +252,7 @@
R.integer.config_defaultNightDisplayCustomEndTime);
}
- return LocalTime.valueOf(endTimeValue);
+ return LocalTime.ofSecondOfDay(endTimeValue / 1000);
}
/**
@@ -255,7 +267,7 @@
throw new IllegalArgumentException("endTime cannot be null");
}
return Secure.putIntForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId);
+ Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toSecondOfDay() * 1000, mUserId);
}
/**
@@ -379,106 +391,6 @@
}
/**
- * A time without a time-zone or date.
- */
- public static class LocalTime {
-
- /**
- * The hour of the day from 0 - 23.
- */
- public final int hourOfDay;
- /**
- * The minute within the hour from 0 - 59.
- */
- public final int minute;
-
- public LocalTime(int hourOfDay, int minute) {
- if (hourOfDay < 0 || hourOfDay > 23) {
- throw new IllegalArgumentException("Invalid hourOfDay: " + hourOfDay);
- } else if (minute < 0 || minute > 59) {
- throw new IllegalArgumentException("Invalid minute: " + minute);
- }
-
- this.hourOfDay = hourOfDay;
- this.minute = minute;
- }
-
- /**
- * Returns the first date time corresponding to this local time that occurs before the
- * provided date time.
- *
- * @param time the date time to compare against
- * @return the prior date time corresponding to this local time
- */
- public Calendar getDateTimeBefore(Calendar time) {
- final Calendar c = Calendar.getInstance();
- c.set(Calendar.YEAR, time.get(Calendar.YEAR));
- c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
-
- c.set(Calendar.HOUR_OF_DAY, hourOfDay);
- c.set(Calendar.MINUTE, minute);
- c.set(Calendar.SECOND, 0);
- c.set(Calendar.MILLISECOND, 0);
-
- // Check if the local time has past, if so return the same time tomorrow.
- if (c.after(time)) {
- c.add(Calendar.DATE, -1);
- }
-
- return c;
- }
-
- /**
- * Returns the first date time corresponding to this local time that occurs after the
- * provided date time.
- *
- * @param time the date time to compare against
- * @return the next date time corresponding to this local time
- */
- public Calendar getDateTimeAfter(Calendar time) {
- final Calendar c = Calendar.getInstance();
- c.set(Calendar.YEAR, time.get(Calendar.YEAR));
- c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
-
- c.set(Calendar.HOUR_OF_DAY, hourOfDay);
- c.set(Calendar.MINUTE, minute);
- c.set(Calendar.SECOND, 0);
- c.set(Calendar.MILLISECOND, 0);
-
- // Check if the local time has past, if so return the same time tomorrow.
- if (c.before(time)) {
- c.add(Calendar.DATE, 1);
- }
-
- return c;
- }
-
- /**
- * Returns a local time corresponding the given number of milliseconds from midnight.
- *
- * @param millis the number of milliseconds from midnight
- * @return the corresponding local time
- */
- private static LocalTime valueOf(int millis) {
- final int hourOfDay = (millis / 3600000) % 24;
- final int minutes = (millis / 60000) % 60;
- return new LocalTime(hourOfDay, minutes);
- }
-
- /**
- * Returns the local time represented as milliseconds from midnight.
- */
- private int toMillis() {
- return hourOfDay * 3600000 + minute * 60000;
- }
-
- @Override
- public String toString() {
- return String.format(Locale.US, "%02d:%02d", hourOfDay, minute);
- }
- }
-
- /**
* Callback invoked whenever the Night display settings are changed.
*/
public interface Callback {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 031c3f5..1afc212 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3891,6 +3891,9 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+ <service android:name="com.android.server.timezone.TimeZoneUpdateIdler"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
</application>
</manifest>
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index 92419c8..5c5b985 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -46,13 +46,14 @@
<ImageView
android:id="@+id/autofill_save_icon"
- android:layout_width="wrap_content"
- android:layout_height="24sp"/>
+ android:scaleType="fitStart"
+ android:layout_width="24dp"
+ android:layout_height="24dp"/>
<TextView
android:id="@+id/autofill_save_title"
android:paddingLeft="8dp"
- android:layout_width="0dp"
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/autofill_save_title"
android:textSize="16sp"
diff --git a/core/tests/coretests/src/android/database/NewDatabasePerformanceTests.java b/core/tests/coretests/src/android/database/NewDatabasePerformanceTests.java
index bcd9060..75809c0 100644
--- a/core/tests/coretests/src/android/database/NewDatabasePerformanceTests.java
+++ b/core/tests/coretests/src/android/database/NewDatabasePerformanceTests.java
@@ -18,41 +18,70 @@
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
+import android.os.SystemProperties;
import android.test.PerformanceTestCase;
+import android.util.ArrayMap;
import android.util.Log;
import junit.framework.TestCase;
import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.List;
+import java.util.Map;
import java.util.Random;
/**
- * Database Performance Tests
- *
+ * Database Performance Tests.
+ *
+ * <p>Usage:
+ * <code>./frameworks/base/core/tests/coretests/src/android/database/run_newdb_perf_test.sh</code>
+ * <p>Test with WAL journaling enabled:
+ * <code>setprop debug.NewDatabasePerformanceTests.enable_wal 1</code>
*/
-
public class NewDatabasePerformanceTests {
private static final String TAG = "NewDatabasePerformanceTests";
+ private static final boolean DEBUG_ENABLE_WAL = SystemProperties
+ .getBoolean("debug.NewDatabasePerformanceTests.enable_wal", false);
+
private static final int DATASET_SIZE = 100; // Size of dataset to use for testing
private static final int FAST_OP_MULTIPLIER = 25;
private static final int FAST_OP_COUNT = FAST_OP_MULTIPLIER * DATASET_SIZE;
+ private static Long sInitialWriteBytes;
+
+ static {
+ sInitialWriteBytes = getIoStats().get("write_bytes");
+ if (DEBUG_ENABLE_WAL) {
+ Log.i(TAG, "Testing with WAL enabled");
+ }
+ }
+
public static class PerformanceBase extends TestCase
- implements PerformanceTestCase {
+ implements PerformanceTestCase {
protected static final int CURRENT_DATABASE_VERSION = 42;
protected SQLiteDatabase mDatabase;
protected File mDatabaseFile;
private long mSetupFinishedTime;
+ private Long mSetupWriteBytes;
public void setUp() {
long setupStarted = System.currentTimeMillis();
mDatabaseFile = new File("/sdcard", "perf_database_test.db");
if (mDatabaseFile.exists()) {
- mDatabaseFile.delete();
+ SQLiteDatabase.deleteDatabase(mDatabaseFile);
}
- mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
- assertTrue(mDatabase != null);
+ SQLiteDatabase.OpenParams.Builder params = new SQLiteDatabase.OpenParams.Builder();
+ params.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
+ if (DEBUG_ENABLE_WAL) {
+ params.addOpenFlags(SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING);
+ }
+ mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params.build());
+ if (DEBUG_ENABLE_WAL) {
+ assertTrue("Cannot enable WAL", mDatabase.isWriteAheadLoggingEnabled());
+ }
mDatabase.setVersion(CURRENT_DATABASE_VERSION);
mDatabase.beginTransactionNonExclusive();
prepareForTest();
@@ -61,6 +90,7 @@
mSetupFinishedTime = System.currentTimeMillis();
Log.i(TAG, "Setup for " + getClass().getSimpleName() + " took "
+ (mSetupFinishedTime - setupStarted) + " ms");
+ mSetupWriteBytes = getIoStats().get("write_bytes");
}
protected void prepareForTest() {
@@ -70,7 +100,14 @@
long duration = System.currentTimeMillis() - mSetupFinishedTime;
Log.i(TAG, "Test " + getClass().getSimpleName() + " took " + duration + " ms");
mDatabase.close();
- mDatabaseFile.delete();
+ SQLiteDatabase.deleteDatabase(mDatabaseFile);
+ Long writeBytes = getIoStats().get("write_bytes");
+ if (writeBytes != null && sInitialWriteBytes != null) {
+ long testWriteBytes = writeBytes - mSetupWriteBytes;
+ long totalWriteBytes = (writeBytes - sInitialWriteBytes);
+ Log.i(TAG, "Test " + getClass().getSimpleName() + " write_bytes=" + testWriteBytes
+ + ". Since tests started - totalWriteBytes=" + totalWriteBytes);
+ }
}
public boolean isPerformanceOnly() {
@@ -897,4 +934,31 @@
static final String[] TENS =
{"", "ten", "twenty", "thirty", "forty", "fifty", "sixty",
"seventy", "eighty", "ninety"};
+
+ static Map<String, Long> getIoStats() {
+ String ioStat = "/proc/self/io";
+ Map<String, Long> results = new ArrayMap<>();
+ try {
+ List<String> lines = Files.readAllLines(new File(ioStat).toPath());
+ for (String line : lines) {
+ line = line.trim();
+ String[] split = line.split(":");
+ if (split.length == 2) {
+ try {
+ String key = split[0].trim();
+ Long value = Long.valueOf(split[1].trim());
+ results.put(key, value);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Cannot parse number from " + line);
+ }
+ } else if (line.isEmpty()) {
+ Log.e(TAG, "Cannot parse line " + line);
+ }
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Can't read: " + ioStat, e);
+ }
+ return results;
+ }
+
}
diff --git a/core/tests/coretests/src/android/database/process_newdb_perf_test_logs.py b/core/tests/coretests/src/android/database/process_newdb_perf_test_logs.py
index 1faeceb..27b20c3 100644
--- a/core/tests/coretests/src/android/database/process_newdb_perf_test_logs.py
+++ b/core/tests/coretests/src/android/database/process_newdb_perf_test_logs.py
@@ -25,7 +25,16 @@
all_lines = f.readlines()
timings = {}
running_sum = 0
+ # If WAL was enabled for the test
+ wal_enabled = False
+ # Number of bytes which test process caused to be sent to the storage layer.
+ # Reported as max value across all runs.
+ max_write_bytes = 0
for line in all_lines:
+ if "NewDatabasePerformanceTests: Testing with WAL enabled" in line:
+ wal_enabled = True
+ continue
+
regex = r"NewDatabasePerformanceTests: Test (\w+) took (\d+) ms"
matches = re.search(regex, line)
if matches:
@@ -35,16 +44,31 @@
timings[test_name] = []
timings[test_name].append(duration)
running_sum += duration
+ continue
+
if ("TestRunner: run finished:" in line) and (running_sum > 0):
- test_name = '*** TOTAL ALL TESTS (ms) ***'
+ test_name = ('*** TOTAL ALL TESTS [WAL] (ms) ***' if wal_enabled
+ else '*** TOTAL ALL TESTS (ms) ***')
if not test_name in timings:
timings[test_name] = []
timings[test_name].append(running_sum)
running_sum=0
+ continue
+
+ # Determine max from all reported totalWriteBytes
+ regex = r"Test .* totalWriteBytes=(\d+)"
+ matches = re.search(regex, line)
+ if matches:
+ max_write_bytes = max(max_write_bytes, int(matches.group(1)))
+ continue
for k in sorted(timings):
timings_ar = timings[k]
print "%s: %s avg: %s" % (k, timings_ar, sum(timings_ar) / float(len(timings_ar)) )
+
+ print "\nAdditional stats: "
+ print " max write_bytes: %d" % max_write_bytes
+
if __name__ == '__main__':
main()
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index ff75c29..24e3646 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -17,10 +17,12 @@
package android.text.format;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.icu.util.MeasureUnit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -32,6 +34,8 @@
import org.junit.runner.RunWith;
import java.util.Locale;
+import java.util.Set;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -205,4 +209,24 @@
Locale.setDefault(locale);
}
+
+ /**
+ * Verifies that Formatter doesn't "leak" the locally defined petabyte unit into ICU via the
+ * {@link MeasureUnit} registry. This test can fail for two reasons:
+ * 1. we regressed and started leaking again. In this case the code needs to be fixed.
+ * 2. ICU started supporting petabyte as a unit, in which case change one needs to revert this
+ * change (I494fb59a3b3742f35cbdf6b8705817f404a2c6b0), remove Formatter.PETABYTE and replace any
+ * usages of that field with just MeasureUnit.PETABYTE.
+ */
+ // http://b/65632959
+ @Test
+ public void doesNotLeakPetabyte() {
+ // Ensure that the Formatter class is loaded when we call .getAvailable().
+ Formatter.formatFileSize(mContext, Long.MAX_VALUE);
+ Set<MeasureUnit> digitalUnits = MeasureUnit.getAvailable("digital");
+ for (MeasureUnit unit : digitalUnits) {
+ // This assert can fail if we don't leak PETABYTE, but ICU has added it, see #2 above.
+ assertNotEquals("petabyte", unit.getSubtype());
+ }
+ }
}
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 3a77195..afb1193 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -40,7 +40,7 @@
constexpr int sHistogramSize = ProfileData::HistogramSize();
-static void mergeProfileDataIntoProto(service::GraphicsStatsProto* proto,
+static bool mergeProfileDataIntoProto(service::GraphicsStatsProto* proto,
const std::string& package, int versionCode, int64_t startTime, int64_t endTime,
const ProfileData* data);
static void dumpAsTextToFd(service::GraphicsStatsProto* proto, int outFd);
@@ -159,7 +159,7 @@
return success;
}
-void mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::string& package,
+bool mergeProfileDataIntoProto(service::GraphicsStatsProto* proto, const std::string& package,
int versionCode, int64_t startTime, int64_t endTime, const ProfileData* data) {
if (proto->stats_start() == 0 || proto->stats_start() > startTime) {
proto->set_stats_start(startTime);
@@ -188,23 +188,31 @@
proto->mutable_histogram()->Reserve(sHistogramSize);
creatingHistogram = true;
} else if (proto->histogram_size() != sHistogramSize) {
- LOG_ALWAYS_FATAL("Histogram size mismatch, proto is %d expected %d",
+ ALOGE("Histogram size mismatch, proto is %d expected %d",
proto->histogram_size(), sHistogramSize);
+ return false;
}
int index = 0;
+ bool hitMergeError = false;
data->histogramForEach([&](ProfileData::HistogramEntry entry) {
+ if (hitMergeError) return;
+
service::GraphicsStatsHistogramBucketProto* bucket;
if (creatingHistogram) {
bucket = proto->add_histogram();
bucket->set_render_millis(entry.renderTimeMs);
} else {
bucket = proto->mutable_histogram(index);
- LOG_ALWAYS_FATAL_IF(bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs),
- "Frame time mistmatch %d vs. %u", bucket->render_millis(), entry.renderTimeMs);
+ if (bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs)) {
+ ALOGW("Frame time mistmatch %d vs. %u", bucket->render_millis(), entry.renderTimeMs);
+ hitMergeError = true;
+ return;
+ }
}
bucket->set_frame_count(bucket->frame_count() + entry.frameCount);
index++;
});
+ return !hitMergeError;
}
static int32_t findPercentile(service::GraphicsStatsProto* proto, int percentile) {
@@ -221,9 +229,11 @@
void dumpAsTextToFd(service::GraphicsStatsProto* proto, int fd) {
// This isn't a full validation, just enough that we can deref at will
- LOG_ALWAYS_FATAL_IF(proto->package_name().empty()
- || !proto->has_summary(), "package_name() '%s' summary %d",
- proto->package_name().c_str(), proto->has_summary());
+ if (proto->package_name().empty() || !proto->has_summary()) {
+ ALOGW("Skipping dump, invalid package_name() '%s' or summary %d",
+ proto->package_name().c_str(), proto->has_summary());
+ return;
+ }
dprintf(fd, "\nPackage: %s", proto->package_name().c_str());
dprintf(fd, "\nVersion: %d", proto->version_code());
dprintf(fd, "\nStats since: %lldns", proto->stats_start());
@@ -254,14 +264,20 @@
if (!parseFromFile(path, &statsProto)) {
statsProto.Clear();
}
- mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data);
+ if (!mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data)) {
+ return;
+ }
// Although we might not have read any data from the file, merging the existing data
// should always fully-initialize the proto
- LOG_ALWAYS_FATAL_IF(!statsProto.IsInitialized(), "%s",
- statsProto.InitializationErrorString().c_str());
- LOG_ALWAYS_FATAL_IF(statsProto.package_name().empty()
- || !statsProto.has_summary(), "package_name() '%s' summary %d",
- statsProto.package_name().c_str(), statsProto.has_summary());
+ if (!statsProto.IsInitialized()) {
+ ALOGE("proto initialization error %s", statsProto.InitializationErrorString().c_str());
+ return;
+ }
+ if (statsProto.package_name().empty() || !statsProto.has_summary()) {
+ ALOGE("missing package_name() '%s' summary %d",
+ statsProto.package_name().c_str(), statsProto.has_summary());
+ return;
+ }
int outFd = open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0660);
if (outFd <= 0) {
int err = errno;
@@ -312,8 +328,9 @@
if (!path.empty() && !parseFromFile(path, &statsProto)) {
statsProto.Clear();
}
- if (data) {
- mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data);
+ if (data && !mergeProfileDataIntoProto(
+ &statsProto, package, versionCode, startTime, endTime, data)) {
+ return;
}
if (!statsProto.IsInitialized()) {
ALOGW("Failed to load profile data from path '%s' and data %p",
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index f85925d..f41e33f7 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2749,8 +2749,8 @@
mQualityRange = Utils
.parseIntRange(info.getString("quality-range"), mQualityRange);
}
- if (info.containsKey("feature-bitrate-control")) {
- for (String mode: info.getString("feature-bitrate-control").split(",")) {
+ if (info.containsKey("feature-bitrate-modes")) {
+ for (String mode: info.getString("feature-bitrate-modes").split(",")) {
mBitControl |= parseBitrateMode(mode);
}
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 2894e89..b072f65 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -202,7 +202,6 @@
audioRoutesChanged = true;
}
- final int mainType = mCurAudioRoutesInfo.mainType;
if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
if (mCurAudioRoutesInfo.bluetoothName != null) {
@@ -229,8 +228,11 @@
}
if (audioRoutesChanged) {
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, getDefaultSystemAudioRoute(), false);
Log.v(TAG, "Audio routes updated: " + newRoutes + ", a2dp=" + isBluetoothA2dpOn());
+ if (mSelectedRoute == null || mSelectedRoute == mDefaultAudioVideo
+ || mSelectedRoute == mBluetoothA2dpRoute) {
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, getDefaultSystemAudioRoute(), false);
+ }
}
}
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index b53765b..f8cf107 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -377,8 +377,8 @@
<string name="screen_zoom_summary_small" msgid="5867245310241621570">"Petit"</string>
<string name="screen_zoom_summary_default" msgid="2247006805614056507">"Par défaut"</string>
<string name="screen_zoom_summary_large" msgid="4835294730065424084">"Grand"</string>
- <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Plus grand"</string>
- <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Le plus grand"</string>
+ <string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Très grand"</string>
+ <string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Extrêmement grand"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personnalisé (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
<string name="help_feedback_label" msgid="6815040660801785649">"Aide et commentaires"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 99dd3a1..5e9ecdb 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -368,9 +368,9 @@
<string name="install_other_apps" msgid="6986686991775883017">"अज्ञात अॅप्स इंस्टॉल करा"</string>
<string name="home" msgid="3256884684164448244">"सेटिंग्ज होम"</string>
<string-array name="battery_labels">
- <item msgid="8494684293649631252">"0%"</item>
- <item msgid="8934126114226089439">"50%"</item>
- <item msgid="1286113608943010849">"100%"</item>
+ <item msgid="8494684293649631252">"०%"</item>
+ <item msgid="8934126114226089439">"५०%"</item>
+ <item msgid="1286113608943010849">"१००%"</item>
</string-array>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> पूर्वी"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> शिल्लक"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index ba9c9e7..6e676bc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -26,7 +26,6 @@
import android.content.res.Resources;
import android.graphics.drawable.Icon;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -327,6 +326,15 @@
Context context, UserHandle user, Intent intent,
Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon) {
+ getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles,
+ usePriority, checkCategory, forceTintExternalIcon, false /* shouldUpdateTiles */);
+ }
+
+ public static void getTilesForIntent(
+ Context context, UserHandle user, Intent intent,
+ Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
+ boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon,
+ boolean shouldUpdateTiles) {
PackageManager pm = context.getPackageManager();
List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
PackageManager.GET_META_DATA, user.getIdentifier());
@@ -364,9 +372,11 @@
updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
pm, providerMap, forceTintExternalIcon);
if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
-
addedCache.put(key, tile);
+ } else if (shouldUpdateTiles) {
+ updateSummaryAndTitle(context, providerMap, tile);
}
+
if (!tile.userHandle.contains(user)) {
tile.userHandle.add(user);
}
@@ -387,7 +397,6 @@
String summary = null;
String keyHint = null;
boolean isIconTintable = false;
- RemoteViews remoteViews = null;
// Get the activity's meta-data
try {
@@ -442,7 +451,8 @@
}
if (metaData.containsKey(META_DATA_PREFERENCE_CUSTOM_VIEW)) {
int layoutId = metaData.getInt(META_DATA_PREFERENCE_CUSTOM_VIEW);
- remoteViews = new RemoteViews(applicationInfo.packageName, layoutId);
+ tile.remoteViews = new RemoteViews(applicationInfo.packageName, layoutId);
+ updateSummaryAndTitle(context, providerMap, tile);
}
}
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
@@ -474,7 +484,6 @@
// Suggest a key for this tile
tile.key = keyHint;
tile.isIconTintable = isIconTintable;
- tile.remoteViews = remoteViews;
return true;
}
@@ -482,6 +491,26 @@
return false;
}
+ private static void updateSummaryAndTitle(
+ Context context, Map<String, IContentProvider> providerMap, Tile tile) {
+ if (tile == null || tile.metaData == null
+ || !tile.metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {
+ return;
+ }
+
+ String uriString = tile.metaData.getString(META_DATA_PREFERENCE_SUMMARY_URI);
+ Bundle bundle = getBundleFromUri(context, uriString, providerMap);
+ String overrideSummary = getString(bundle, META_DATA_PREFERENCE_SUMMARY);
+ String overrideTitle = getString(bundle, META_DATA_PREFERENCE_TITLE);
+ if (overrideSummary != null) {
+ tile.remoteViews.setTextViewText(android.R.id.summary, overrideSummary);
+ }
+
+ if (overrideTitle != null) {
+ tile.remoteViews.setTextViewText(android.R.id.title, overrideTitle);
+ }
+ }
+
/**
* Gets the icon package name and resource id from content provider.
* @param Context context
@@ -547,37 +576,6 @@
}
}
- public static void updateTileUsingSummaryUri(Context context, final Tile tile) {
- if (tile == null || tile.metaData == null ||
- !tile.metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {
- return;
- }
-
- new AsyncTask<Void, Void, Bundle>() {
- @Override
- protected Bundle doInBackground(Void... params) {
- return getBundleFromUri(context,
- tile.metaData.getString(META_DATA_PREFERENCE_SUMMARY_URI), new HashMap<>());
- }
-
- @Override
- protected void onPostExecute(Bundle bundle) {
- if (bundle == null) {
- return;
- }
- final String overrideSummary = getString(bundle, META_DATA_PREFERENCE_SUMMARY);
- final String overrideTitle = getString(bundle, META_DATA_PREFERENCE_TITLE);
-
- if (overrideSummary != null) {
- tile.remoteViews.setTextViewText(android.R.id.summary, overrideSummary);
- }
- if (overrideTitle != null) {
- tile.remoteViews.setTextViewText(android.R.id.title, overrideTitle);
- }
- }
- }.execute();
- }
-
private static String getString(Bundle bundle, String key) {
return bundle == null ? null : bundle.getString(key);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
index 00f32b2..56b8441 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
@@ -195,7 +195,7 @@
intent.setPackage(category.pkg);
}
TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
- mAddCache, null, suggestions, true, false, false);
+ mAddCache, null, suggestions, true, false, false, true /* shouldUpdateTiles */);
filterSuggestions(suggestions, countBefore, isSmartSuggestionEnabled);
if (!category.multiple && suggestions.size() > (countBefore + 1)) {
// If there are too many, remove them all and only re-add the one with the highest
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 3fd7c39..682b85e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -133,7 +133,7 @@
private final Map<String, TimestampedScoredNetwork> mScoredNetworkCache = new HashMap<>();
/** Maximum age of scan results to hold onto while actively scanning. **/
- private static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000;
+ private static final long MAX_SCAN_RESULT_AGE_MILLIS = 25000;
static final String KEY_NETWORKINFO = "key_networkinfo";
static final String KEY_WIFIINFO = "key_wifiinfo";
@@ -426,6 +426,11 @@
}
builder.append(",metered=").append(isMetered());
+ if (WifiTracker.sVerboseLogging) {
+ builder.append(",rssi=").append(mRssi);
+ builder.append(",scan cache size=").append(mScanResultCache.size());
+ }
+
return builder.append(')').toString();
}
@@ -951,6 +956,7 @@
evictOldScanResults();
// TODO: sort list by RSSI or age
+ long nowMs = SystemClock.elapsedRealtime();
for (ScanResult result : mScanResultCache.values()) {
if (result.frequency >= LOWER_FREQ_5GHZ
&& result.frequency <= HIGHER_FREQ_5GHZ) {
@@ -961,7 +967,7 @@
maxRssi5 = result.level;
}
if (num5 <= maxDisplayedScans) {
- scans5GHz.append(verboseScanResultSummary(result, bssid));
+ scans5GHz.append(verboseScanResultSummary(result, bssid, nowMs));
}
} else if (result.frequency >= LOWER_FREQ_24GHZ
&& result.frequency <= HIGHER_FREQ_24GHZ) {
@@ -972,7 +978,7 @@
maxRssi24 = result.level;
}
if (num24 <= maxDisplayedScans) {
- scans24GHz.append(verboseScanResultSummary(result, bssid));
+ scans24GHz.append(verboseScanResultSummary(result, bssid, nowMs));
}
}
}
@@ -1000,7 +1006,7 @@
}
@VisibleForTesting
- /* package */ String verboseScanResultSummary(ScanResult result, String bssid) {
+ /* package */ String verboseScanResultSummary(ScanResult result, String bssid, long nowMs) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(" \n{").append(result.BSSID);
if (result.BSSID.equals(bssid)) {
@@ -1013,6 +1019,8 @@
stringBuilder.append(",")
.append(getSpeedLabel(speed));
}
+ int ageSeconds = (int) (nowMs - result.timestamp / 1000) / 1000;
+ stringBuilder.append(",").append(ageSeconds).append("s");
stringBuilder.append("}");
return stringBuilder.toString();
}
@@ -1209,6 +1217,7 @@
/** Attempt to update the AccessPoint and return true if an update occurred. */
public boolean update(
@Nullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) {
+
boolean updated = false;
final int oldLevel = getLevel();
if (info != null && isInfoForThisAccessPoint(config, info)) {
@@ -1240,6 +1249,7 @@
mAccessPointListener.onLevelChanged(this);
}
}
+
return updated;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 6895550..3c664b0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -624,7 +624,7 @@
String prevSsid = prevAccessPoint.getSsidStr();
boolean found = false;
for (AccessPoint newAccessPoint : accessPoints) {
- if (newAccessPoint.getSsid() != null && newAccessPoint.getSsid()
+ if (newAccessPoint.getSsidStr() != null && newAccessPoint.getSsidStr()
.equals(prevSsid)) {
found = true;
break;
@@ -676,24 +676,23 @@
private void updateNetworkInfo(NetworkInfo networkInfo) {
/* sticky broadcasts can call this when wifi is disabled */
if (!mWifiManager.isWifiEnabled()) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_PAUSE_SCANNING);
clearAccessPointsAndConditionallyUpdate();
return;
}
- if (networkInfo != null &&
- networkInfo.getDetailedState() == DetailedState.OBTAINING_IPADDR) {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_PAUSE_SCANNING);
- } else {
- mMainHandler.sendEmptyMessage(MainHandler.MSG_RESUME_SCANNING);
- }
-
if (networkInfo != null) {
mLastNetworkInfo = networkInfo;
+ if (DBG()) {
+ Log.d(TAG, "mLastNetworkInfo set: " + mLastNetworkInfo);
+ }
}
WifiConfiguration connectionConfig = null;
+
mLastInfo = mWifiManager.getConnectionInfo();
+ if (DBG()) {
+ Log.d(TAG, "mLastInfo set as: " + mLastInfo);
+ }
if (mLastInfo != null) {
connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId(),
mWifiManager.getConfiguredNetworks());
@@ -717,7 +716,6 @@
}
if (reorder) Collections.sort(mInternalAccessPoints);
-
if (updated) mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
}
}
@@ -866,6 +864,9 @@
if (mScanner != null) {
mScanner.pause();
}
+ synchronized (mLock) {
+ mStaleScanResults = true;
+ }
break;
}
}
@@ -928,6 +929,9 @@
if (mScanner != null) {
mScanner.pause();
}
+ synchronized (mLock) {
+ mStaleScanResults = true;
+ }
}
mMainHandler.obtainMessage(MainHandler.MSG_WIFI_STATE_CHANGED, msg.arg1, 0)
.sendToTarget();
@@ -982,7 +986,7 @@
}
return;
}
- sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
+ sendEmptyMessageDelayed(MSG_SCAN, WIFI_RESCAN_INTERVAL_MS);
}
}
@@ -1075,10 +1079,12 @@
oldAccessPoints.put(accessPoint.mId, accessPoint);
}
- if (DBG()) {
- Log.d(TAG, "Starting to copy AP items on the MainHandler");
- }
synchronized (mLock) {
+ if (DBG()) {
+ Log.d(TAG, "Starting to copy AP items on the MainHandler. Internal APs: "
+ + mInternalAccessPoints);
+ }
+
if (notifyListeners) {
notificationMap = mAccessPointListenerAdapter.mPendingNotifications.clone();
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index b99584f..230b200 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -454,7 +454,7 @@
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */,
MAX_SCORE_CACHE_AGE_MILLIS);
- String summary = ap.verboseScanResultSummary(scanResults.get(0), null);
+ String summary = ap.verboseScanResultSummary(scanResults.get(0), null, 0);
assertThat(summary.contains(mContext.getString(R.string.speed_label_very_fast))).isTrue();
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index df6587e..7cce648 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -105,15 +105,30 @@
private static final byte SCORE_2 = 15;
private static final int BADGE_2 = AccessPoint.Speed.FAST;
- private static final int CONNECTED_NETWORK_ID = 123;
+ // TODO(b/65594609): Convert mutable Data objects to instance variables / builder pattern
+ private static final int NETWORK_ID_1 = 123;
private static final int CONNECTED_RSSI = -50;
private static final WifiInfo CONNECTED_AP_1_INFO = new WifiInfo();
static {
CONNECTED_AP_1_INFO.setSSID(WifiSsid.createFromAsciiEncoded(SSID_1));
CONNECTED_AP_1_INFO.setBSSID(BSSID_1);
- CONNECTED_AP_1_INFO.setNetworkId(CONNECTED_NETWORK_ID);
+ CONNECTED_AP_1_INFO.setNetworkId(NETWORK_ID_1);
CONNECTED_AP_1_INFO.setRssi(CONNECTED_RSSI);
}
+ private static final WifiConfiguration CONFIGURATION_1 = new WifiConfiguration();
+ static {
+ CONFIGURATION_1.SSID = SSID_1;
+ CONFIGURATION_1.BSSID = BSSID_1;
+ CONFIGURATION_1.networkId = NETWORK_ID_1;
+ }
+
+ private static final int NETWORK_ID_2 = 2;
+ private static final WifiConfiguration CONFIGURATION_2 = new WifiConfiguration();
+ static {
+ CONFIGURATION_2.SSID = SSID_2;
+ CONFIGURATION_2.BSSID = BSSID_2;
+ CONFIGURATION_2.networkId = NETWORK_ID_2;
+ }
@Captor ArgumentCaptor<WifiNetworkScoreCache> mScoreCacheCaptor;
@Mock private ConnectivityManager mockConnectivityManager;
@@ -159,6 +174,8 @@
when(mockWifiManager.isWifiEnabled()).thenReturn(true);
when(mockWifiManager.getScanResults())
.thenReturn(Arrays.asList(buildScanResult1(), buildScanResult2()));
+ when(mockWifiManager.getConfiguredNetworks())
+ .thenReturn(Arrays.asList(CONFIGURATION_1, CONFIGURATION_2));
when(mockCurve1.lookupScore(RSSI_1)).thenReturn(SCORE_1);
@@ -333,8 +350,7 @@
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = SSID_1;
configuration.BSSID = BSSID_1;
- configuration.networkId = CONNECTED_NETWORK_ID;
- when(mockWifiManager.getConfiguredNetworks()).thenReturn(Arrays.asList(configuration));
+ configuration.networkId = NETWORK_ID_1;
NetworkInfo networkInfo = new NetworkInfo(
ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
@@ -365,6 +381,24 @@
mainLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
}
+ private void switchToNetwork2(WifiTracker tracker) throws InterruptedException {
+ NetworkInfo networkInfo = new NetworkInfo(
+ ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
+ networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTING, "connecting", "test");
+
+ WifiInfo info = new WifiInfo();
+ info.setSSID(WifiSsid.createFromAsciiEncoded(SSID_2));
+ info.setBSSID(BSSID_2);
+ info.setRssi(CONNECTED_RSSI);
+ info.setNetworkId(NETWORK_ID_2);
+ when(mockWifiManager.getConnectionInfo()).thenReturn(info);
+
+ Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
+ tracker.mReceiver.onReceive(mContext, intent);
+ waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
+ }
+
@Test
public void testAccessPointListenerSetWhenLookingUpUsingScanResults() {
ScanResult scanResult = new ScanResult();
@@ -722,12 +756,6 @@
when(mockWifiManager.getConnectionInfo()).thenReturn(CONNECTED_AP_1_INFO);
- WifiConfiguration configuration = new WifiConfiguration();
- configuration.SSID = SSID_1;
- configuration.BSSID = BSSID_1;
- configuration.networkId = CONNECTED_NETWORK_ID;
- when(mockWifiManager.getConfiguredNetworks()).thenReturn(Arrays.asList(configuration));
-
NetworkInfo networkInfo = new NetworkInfo(
ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "connected", "test");
@@ -881,4 +909,17 @@
assertThat(tracker.isConnected()).isFalse();
verify(mockWifiListener, times(2)).onConnectedChanged();
}
+
+ @Test
+ public void updateNetworkInfoWithNewConnectedNetwork_switchesNetworks() throws Exception {
+ WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
+
+ switchToNetwork2(tracker);
+
+ List<AccessPoint> aps = tracker.getAccessPoints();
+ assertThat(aps.get(0).getSsidStr()).isEqualTo(SSID_2);
+
+ assertThat(aps.get(0).isReachable()).isTrue();
+ assertThat(aps.get(1).isReachable()).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 386e820..e9ca753 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -22,12 +22,10 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application;
@@ -66,7 +64,9 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.internal.ShadowExtractor;
import java.util.ArrayList;
import java.util.Collections;
@@ -75,7 +75,8 @@
@RunWith(RobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH,
- sdk = TestConfig.SDK_VERSION)
+ sdk = TestConfig.SDK_VERSION,
+ shadows = {TileUtilsTest.TileUtilsShadowRemoteViews.class})
public class TileUtilsTest {
@Mock
@@ -420,12 +421,24 @@
}
@Test
- public void updateTileUsingSummaryUri_summaryUriSpecified_shouldOverrideRemoteViewSummary()
+ public void getTilesForIntent_summaryUriSpecified_shouldOverrideRemoteViewSummary()
throws RemoteException {
+ Intent intent = new Intent();
+ Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
+ List<Tile> outTiles = new ArrayList<>();
+ List<ResolveInfo> info = new ArrayList<>();
+ ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
+ null, URI_GET_SUMMARY);
+ resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
+ R.layout.user_preference);
+ info.add(resolveInfo);
+
+ when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
+ .thenReturn(info);
+
// Mock the content provider interaction.
Bundle bundle = new Bundle();
- String expectedSummary = "new summary text";
- bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, expectedSummary);
+ bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
when(mIContentProvider.call(anyString(),
eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
any())).thenReturn(bundle);
@@ -434,14 +447,57 @@
when(mContentResolver.acquireUnstableProvider(any(Uri.class)))
.thenReturn(mIContentProvider);
- Tile tile = new Tile();
- tile.metaData = new Bundle();
- tile.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, URI_GET_SUMMARY);
- tile.remoteViews = mock(RemoteViews.class);
- TileUtils.updateTileUsingSummaryUri(mContext, tile);
- ShadowApplication.runBackgroundTasks();
+ TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
+ null /* defaultCategory */, outTiles, false /* usePriority */,
+ false /* checkCategory */, true /* forceTintExternalIcon */);
- verify(tile.remoteViews, times(1)).setTextViewText(anyInt(), eq(expectedSummary));
+ assertThat(outTiles.size()).isEqualTo(1);
+ Tile tile = outTiles.get(0);
+ assertThat(tile.remoteViews).isNotNull();
+ assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
+ // Make sure the summary TextView got a new text string.
+ TileUtilsShadowRemoteViews shadowRemoteViews =
+ (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews);
+ assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary);
+ assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text");
+ }
+
+ @Test
+ public void getTilesForIntent_providerUnavailable_shouldNotOverrideRemoteViewSummary()
+ throws RemoteException {
+ Intent intent = new Intent();
+ Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
+ List<Tile> outTiles = new ArrayList<>();
+ List<ResolveInfo> info = new ArrayList<>();
+ ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
+ null, URI_GET_SUMMARY);
+ resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
+ R.layout.user_preference);
+ info.add(resolveInfo);
+
+ when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
+ .thenReturn(info);
+
+ // Mock the content provider interaction.
+ Bundle bundle = new Bundle();
+ bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
+ when(mIContentProvider.call(anyString(),
+ eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
+ any())).thenReturn(bundle);
+
+ TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
+ null /* defaultCategory */, outTiles, false /* usePriority */,
+ false /* checkCategory */, true /* forceTintExternalIcon */);
+
+ assertThat(outTiles.size()).isEqualTo(1);
+ Tile tile = outTiles.get(0);
+ assertThat(tile.remoteViews).isNotNull();
+ assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
+ // Make sure the summary TextView didn't get any text view updates.
+ TileUtilsShadowRemoteViews shadowRemoteViews =
+ (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews);
+ assertThat(shadowRemoteViews.overrideViewId).isNull();
+ assertThat(shadowRemoteViews.overrideText).isNull();
}
public static ResolveInfo newInfo(boolean systemApp, String category) {
@@ -506,4 +562,16 @@
}
}
+ @Implements(RemoteViews.class)
+ public static class TileUtilsShadowRemoteViews {
+
+ private Integer overrideViewId;
+ private CharSequence overrideText;
+
+ @Implementation
+ public void setTextViewText(int viewId, CharSequence text) {
+ overrideViewId = viewId;
+ overrideText = text;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 8de1d31..2bc0e45c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -15,6 +15,8 @@
*/
package com.android.keyguard;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.app.Presentation;
import android.content.Context;
import android.content.DialogInterface;
@@ -28,16 +30,21 @@
import android.view.View;
import android.view.WindowManager;
+// TODO(multi-display): Support multiple external displays
public class KeyguardDisplayManager {
protected static final String TAG = "KeyguardDisplayManager";
private static boolean DEBUG = KeyguardConstants.DEBUG;
+
+ private final ViewMediatorCallback mCallback;
+ private final MediaRouter mMediaRouter;
+ private final Context mContext;
+
Presentation mPresentation;
- private MediaRouter mMediaRouter;
- private Context mContext;
private boolean mShowing;
- public KeyguardDisplayManager(Context context) {
+ public KeyguardDisplayManager(Context context, ViewMediatorCallback callback) {
mContext = context;
+ mCallback = callback;
mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
}
@@ -90,6 +97,7 @@
};
protected void updateDisplays(boolean showing) {
+ Presentation originalPresentation = mPresentation;
if (showing) {
MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(
MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
@@ -121,6 +129,13 @@
mPresentation = null;
}
}
+
+ // mPresentation is only updated when the display changes
+ if (mPresentation != originalPresentation) {
+ final int displayId = mPresentation != null
+ ? mPresentation.getDisplay().getDisplayId() : INVALID_DISPLAY;
+ mCallback.onSecondaryDisplayShowingChanged(displayId);
+ }
}
private final static class KeyguardPresentation extends Presentation {
diff --git a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
index 327d218..b194de4 100644
--- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
@@ -88,4 +88,9 @@
* {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
*/
int getBouncerPromptReason();
+
+ /**
+ * Invoked when the secondary display showing a keyguard window changes.
+ */
+ void onSecondaryDisplayShowingChanged(int displayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3eb68f5..28adca9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.telephony.IccCardConstants.State.ABSENT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
@@ -239,6 +240,9 @@
// answer whether the input should be restricted)
private boolean mShowing;
+ // display id of the secondary display on which we have put a keyguard window
+ private int mSecondaryDisplayShowing = INVALID_DISPLAY;
+
/** Cached value of #isInputRestricted */
private boolean mInputRestricted;
@@ -646,6 +650,13 @@
}
return KeyguardSecurityView.PROMPT_REASON_NONE;
}
+
+ @Override
+ public void onSecondaryDisplayShowingChanged(int displayId) {
+ synchronized (KeyguardViewMediator.this) {
+ setShowingLocked(mShowing, displayId, false);
+ }
+ }
};
public void userActivity() {
@@ -670,7 +681,7 @@
filter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiver(mBroadcastReceiver, filter);
- mKeyguardDisplayManager = new KeyguardDisplayManager(mContext);
+ mKeyguardDisplayManager = new KeyguardDisplayManager(mContext, mViewMediatorCallback);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
@@ -685,7 +696,8 @@
com.android.keyguard.R.bool.config_enableKeyguardService)) {
setShowingLocked(!shouldWaitForProvisioning()
&& !mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser()), true /* forceCallbacks */);
+ KeyguardUpdateMonitor.getCurrentUser()),
+ mSecondaryDisplayShowing, true /* forceCallbacks */);
}
mStatusBarKeyguardViewManager =
@@ -1694,10 +1706,10 @@
playSound(mTrustedSoundId);
}
- private void updateActivityLockScreenState(boolean showing) {
+ private void updateActivityLockScreenState(boolean showing, int secondaryDisplayShowing) {
mUiOffloadThread.submit(() -> {
try {
- ActivityManager.getService().setLockScreenShown(showing);
+ ActivityManager.getService().setLockScreenShown(showing, secondaryDisplayShowing);
} catch (RemoteException e) {
}
});
@@ -2060,30 +2072,39 @@
}
private void setShowingLocked(boolean showing) {
- setShowingLocked(showing, false /* forceCallbacks */);
+ setShowingLocked(showing, mSecondaryDisplayShowing, false /* forceCallbacks */);
}
- private void setShowingLocked(boolean showing, boolean forceCallbacks) {
- if (showing != mShowing || forceCallbacks) {
+ private void setShowingLocked(
+ boolean showing, int secondaryDisplayShowing, boolean forceCallbacks) {
+ final boolean notifyDefaultDisplayCallbacks = showing != mShowing || forceCallbacks;
+ if (notifyDefaultDisplayCallbacks || secondaryDisplayShowing != mSecondaryDisplayShowing) {
mShowing = showing;
- int size = mKeyguardStateCallbacks.size();
- for (int i = size - 1; i >= 0; i--) {
- IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i);
- try {
- callback.onShowingStateChanged(showing);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onShowingStateChanged", e);
- if (e instanceof DeadObjectException) {
- mKeyguardStateCallbacks.remove(callback);
- }
+ mSecondaryDisplayShowing = secondaryDisplayShowing;
+ if (notifyDefaultDisplayCallbacks) {
+ notifyDefaultDisplayCallbacks(showing);
+ }
+ updateActivityLockScreenState(showing, secondaryDisplayShowing);
+ }
+ }
+
+ private void notifyDefaultDisplayCallbacks(boolean showing) {
+ int size = mKeyguardStateCallbacks.size();
+ for (int i = size - 1; i >= 0; i--) {
+ IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i);
+ try {
+ callback.onShowingStateChanged(showing);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onShowingStateChanged", e);
+ if (e instanceof DeadObjectException) {
+ mKeyguardStateCallbacks.remove(callback);
}
}
- updateInputRestrictedLocked();
- mUiOffloadThread.submit(() -> {
- mTrustManager.reportKeyguardShowingChanged();
- });
- updateActivityLockScreenState(showing);
}
+ updateInputRestrictedLocked();
+ mUiOffloadThread.submit(() -> {
+ mTrustManager.reportKeyguardShowingChanged();
+ });
}
private void notifyTrustedChangedLocked(boolean trusted) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7fe7f39..8fa904e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -2024,14 +2024,15 @@
}
@Override
- public int getMinHeight() {
- if (mGuts != null && mGuts.isExposed()) {
+ public int getMinHeight(boolean ignoreTemporaryStates) {
+ if (!ignoreTemporaryStates && mGuts != null && mGuts.isExposed()) {
return mGuts.getIntrinsicHeight();
- } else if (isHeadsUpAllowed() && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
+ } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp
+ && mHeadsUpManager.isTrackingHeadsUp()) {
return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
} else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
return mChildrenContainer.getMinHeight();
- } else if (isHeadsUpAllowed() && mIsHeadsUp) {
+ } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) {
return mHeadsUpHeight;
}
NotificationContentView showingLayout = getShowingLayout();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index efe5e0c..aac9af8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -151,9 +151,21 @@
}
/**
- * @return The minimum content height of this notification.
+ * @return The minimum content height of this notification. This also respects the temporary
+ * states of the view.
*/
public int getMinHeight() {
+ return getMinHeight(false /* ignoreTemporaryStates */);
+ }
+
+ /**
+ * Get the minimum height of this view.
+ *
+ * @param ignoreTemporaryStates should temporary states be ignored like the guts or heads-up.
+ *
+ * @return The minimum height that this view needs.
+ */
+ public int getMinHeight(boolean ignoreTemporaryStates) {
return getHeight();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index ddc7dd0..d0417b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -292,8 +292,8 @@
if (mRankingMap != null) {
// RankingMap as received from NoMan
- mRankingMap.getRanking(a.key, mRankingA);
- mRankingMap.getRanking(b.key, mRankingB);
+ getRanking(a.key, mRankingA);
+ getRanking(b.key, mRankingB);
aImportance = mRankingA.getImportance();
bImportance = mRankingB.getImportance();
aRank = mRankingA.getRank();
@@ -381,7 +381,7 @@
public boolean isAmbient(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.isAmbient();
}
return false;
@@ -389,7 +389,7 @@
public int getVisibilityOverride(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.getVisibilityOverride();
}
return Ranking.VISIBILITY_NO_OVERRIDE;
@@ -397,7 +397,7 @@
public boolean shouldSuppressScreenOff(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return (mTmpRanking.getSuppressedVisualEffects()
& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0;
}
@@ -406,7 +406,7 @@
public boolean shouldSuppressScreenOn(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return (mTmpRanking.getSuppressedVisualEffects()
& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON) != 0;
}
@@ -415,7 +415,7 @@
public int getImportance(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.getImportance();
}
return NotificationManager.IMPORTANCE_UNSPECIFIED;
@@ -423,7 +423,7 @@
public String getOverrideGroupKey(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.getOverrideGroupKey();
}
return null;
@@ -431,7 +431,7 @@
public List<SnoozeCriterion> getSnoozeCriteria(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.getSnoozeCriteria();
}
return null;
@@ -439,7 +439,7 @@
public NotificationChannel getChannel(String key) {
if (mRankingMap != null) {
- mRankingMap.getRanking(key, mTmpRanking);
+ getRanking(key, mTmpRanking);
return mTmpRanking.getChannel();
}
return null;
@@ -452,6 +452,9 @@
final int N = mEntries.size();
for (int i = 0; i < N; i++) {
Entry entry = mEntries.valueAt(i);
+ if (!getRanking(entry.key, mTmpRanking)) {
+ continue;
+ }
final StatusBarNotification oldSbn = entry.notification.cloneLight();
final String overrideGroupKey = getOverrideGroupKey(entry.key);
if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
@@ -466,6 +469,19 @@
filterAndSort();
}
+ /**
+ * Get the ranking from the current ranking map.
+ *
+ * @param key the key to look up
+ * @param outRanking the ranking to populate
+ *
+ * @return {@code true} if the ranking was properly obtained.
+ */
+ @VisibleForTesting
+ protected boolean getRanking(String key, Ranking outRanking) {
+ return mRankingMap.getRanking(key, outRanking);
+ }
+
// TODO: This should not be public. Instead the Environment should notify this class when
// anything changed, and this class should call back the UI so it updates itself.
public void filterAndSort() {
@@ -573,7 +589,7 @@
}
private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) {
- mRankingMap.getRanking(e.key, mTmpRanking);
+ getRanking(e.key, mTmpRanking);
pw.print(indent);
pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon);
StatusBarNotification n = e.notification;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 1f44abe..4bca797 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -50,7 +50,7 @@
public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6;
public static final int LIGHTS_IN_DURATION = 250;
- public static final int LIGHTS_OUT_DURATION = 750;
+ public static final int LIGHTS_OUT_DURATION = 1500;
public static final int BACKGROUND_DURATION = 200;
private final String mTag;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 078e818..c191618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -509,7 +509,8 @@
if (row.isRemoved()) {
continue;
}
- availableSpace -= child.getMinHeight() + notificationPadding;
+ availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */)
+ + notificationPadding;
if (availableSpace >= 0 && count < maximum) {
count++;
} else if (availableSpace > -shelfSize) {
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 efc8d8b..200cada 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -339,7 +339,7 @@
private static final int STATUS_OR_NAV_TRANSIENT =
View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
- private static final long AUTOHIDE_TIMEOUT_MS = 3000;
+ private static final long AUTOHIDE_TIMEOUT_MS = 2250;
/** The minimum delay in ms between reports of notification visibility. */
private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
@@ -3321,7 +3321,6 @@
} else {
cancelAutohide();
}
- touchAutoDim();
}
protected int computeStatusBarMode(int oldVal, int newVal) {
@@ -3404,10 +3403,10 @@
}
// manually dismiss the volume panel when interacting with the nav bar
if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) {
+ touchAutoDim();
dismissVolumeDialog();
}
checkBarModes();
- touchAutoDim();
}
private void dismissVolumeDialog() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
index 18c5756..972eddb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
@@ -135,5 +135,11 @@
public NotificationChannel getChannel(String key) {
return new NotificationChannel(null, null, 0);
}
+
+ @Override
+ protected boolean getRanking(String key, NotificationListenerService.Ranking outRanking) {
+ super.getRanking(key, outRanking);
+ return true;
+ }
}
}
diff --git a/proto/src/ipconnectivity.proto b/proto/src/ipconnectivity.proto
index 885896f..437da8f 100644
--- a/proto/src/ipconnectivity.proto
+++ b/proto/src/ipconnectivity.proto
@@ -473,6 +473,38 @@
repeated Pair validation_states = 8;
}
+// Represents statistics from NFLOG wakeup events due to ingress packets.
+// Since oc-mr1.
+// Next tag: 8.
+message WakeupStats {
+ // The time duration in seconds covered by these stats, for deriving
+ // exact wakeup rates.
+ optional int64 duration_sec = 1;
+
+ // The total number of ingress packets waking up the device.
+ optional int64 total_wakeups = 2;
+
+ // The total number of wakeup packets routed to a socket belonging to
+ // the root uid (uid 0).
+ optional int64 root_wakeups = 3;
+
+ // The total number of wakeup packets routed to a socket belonging to
+ // the system server (uid 1000).
+ optional int64 system_wakeups = 4;
+
+ // The total number of wakeup packets routed to a socket belonging to
+ // an application (uid > 9999).
+ optional int64 application_wakeups = 5;
+
+ // The total number of wakeup packets routed to a socket belonging to another
+ // uid than the root uid, system uid, or an application uid (any uid in
+ // between [1001, 9999]. See android.os.Process for possible uids.
+ optional int64 non_application_wakeups = 6;
+
+ // The total number of wakeup packets with no associated socket or uid.
+ optional int64 no_uid_wakeups = 7;
+}
+
// Represents one of the IP connectivity event defined in this file.
// Next tag: 20
message IpConnectivityEvent {
@@ -547,6 +579,9 @@
// Network statistics.
NetworkStats network_stats = 19;
+
+ // Ingress packet wakeup statistics.
+ WakeupStats wakeup_stats = 20;
};
};
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index a29af5d..f15749f 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4451,6 +4451,82 @@
// OS: O MR
FIELD_SELECTION_SMART_RANGE = 1123;
+ // The value of an autofillable and savable view was reset
+ // Package: Package of app that was autofilled
+ // OS: O MR
+ // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // Tag FIELD_AUTOFILL_PREVIOUS_LENGTH: the previous length of the value
+ AUTOFILL_VALUE_RESET = 1124;
+
+ // Tag of AUTOFILL_VALUE_RESET
+ // OS: O MR
+ FIELD_AUTOFILL_PREVIOUS_LENGTH = 1125;
+
+ // An autofill dataset authentification succeeded
+ // Package: Package of app that was autofilled
+ // OS: O MR
+ // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ AUTOFILL_DATASET_AUTHENTICATED = 1126;
+
+ // An autofill service provided an invalid dataset authentification
+ // Package: Package of app that was autofilled
+ // OS: O MR
+ // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ AUTOFILL_INVALID_DATASET_AUTHENTICATION = 1127;
+
+ // An autofill service provided an invalid authentification extra
+ // Package: Package of app that was autofilled
+ // OS: O MR
+ // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ AUTOFILL_INVALID_AUTHENTICATION = 1128;
+
+ // An autofill service used a custom description (using RemoteViews) in the Save affordance
+ // Package: Package of app that is autofilled
+ // OS: O MR
+ // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // Tag FIELD_AUTOFILL_SAVE_TYPE: Type of save object passed by the service
+ AUTOFILL_SAVE_CUSTOM_DESCRIPTION = 1129;
+
+ // FIELD - Type of save object passed by the service when the Save UI is shown
+ // OS: O MR
+ FIELD_AUTOFILL_SAVE_TYPE = 1130;
+
+ // An autofill service used a custom subtitle (String) in the Save affordance
+ // Package: Package of app that is autofilled
+ // OS: O MR
+ // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // Tag FIELD_AUTOFILL_SAVE_TYPE: Type of save object passed by the service
+ AUTOFILL_SAVE_CUSTOM_SUBTITLE = 1131;
+
+ // User tapped a link in the custom description of the Save affordance provided by an autofill service
+ // Package: Package of app that is autofilled
+ // OS: O MR
+ // Type TYPE_UNKNOWN: The link was not properly set by the service
+ // Type TYPE_OPEN: The link launched an activity
+ // Type TYPE_FAILURE: The link could not launc an activity
+ // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // Tag FIELD_AUTOFILL_SAVE_TYPE: Type of save object passed by the service
+ AUTOFILL_SAVE_LINK_TAPPED = 1132;
+
+ // Result of the validation on save when an autofill service provided a validator
+ // Package: Package of app that is autofilled
+ // OS: O MR
+ // Type TYPE_FAILURE: The validation could not be performed due to an error
+ // Type TYPE_SUCCESS: The validation passed
+ // Type TYPE_DISMISS: The validation failed
+ // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+ // Tag FIELD_AUTOFILL_SAVE_TYPE: Type of save object passed by the service
+ AUTOFILL_SAVE_VALIDATION = 1133;
+
+ // Result of an operation in the autofill save affordance after the user tapped a link in the custom description
+ // provided by the autofill service
+ // Package: Package of app that is autofilled
+ // OS: O MR
+ // Type TYPE_OPEN: The save affordance was restored
+ // Type TYPE_DISMISS: The save affordcance was destroyed
+ // Type TYPE_FAILURE: An invalid opperation was reported by the app's AutofillManager
+ AUTOFILL_PENDING_SAVE_UI_OPERATION = 1134;
+
// ---- End O-MR1 Constants, all O-MR1 constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index ddc819d..a1c75bf 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -650,7 +650,7 @@
synchronized (mLock) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service == null) return false;
- return Objects.equals(packageName, service.getPackageName());
+ return Objects.equals(packageName, service.getServicePackageName());
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 2b7a671..e28e1c9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -146,8 +146,9 @@
updateLocked(disabled);
}
+ @Nullable
CharSequence getServiceName() {
- final String packageName = getPackageName();
+ final String packageName = getServicePackageName();
if (packageName == null) {
return null;
}
@@ -162,7 +163,8 @@
}
}
- String getPackageName() {
+ @Nullable
+ String getServicePackageName() {
final ComponentName serviceComponent = getServiceComponentName();
if (serviceComponent != null) {
return serviceComponent.getPackageName();
@@ -631,7 +633,7 @@
void destroySessionsLocked() {
if (mSessions.size() == 0) {
- mUi.destroyAll(null, null);
+ mUi.destroyAll(null, null, false);
return;
}
while (mSessions.size() > 0) {
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 086dd29..236fbfd 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.metrics.LogMaker;
import android.os.Bundle;
import android.service.autofill.Dataset;
import android.util.ArrayMap;
@@ -25,6 +26,8 @@
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
@@ -99,4 +102,14 @@
}
return fields;
}
+
+ @NonNull
+ public static LogMaker newLogMaker(int category, String packageName,
+ String servicePackageName) {
+ final LogMaker log = new LogMaker(category).setPackageName(packageName);
+ if (servicePackageName != null) {
+ log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
+ }
+ return log;
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index f315148..af55807 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -578,9 +578,8 @@
public void run() {
synchronized (mLock) {
if (isCancelledLocked()) {
- // TODO(b/653742740): we should probably return here, but for now we're justing
- // logging to confirm this is the problem if it happens again.
- Slog.e(LOG_TAG, "run() called after canceled: " + mRequest);
+ if (sDebug) Slog.d(LOG_TAG, "run() called after canceled: " + mRequest);
+ return;
}
}
final RemoteFillService remoteService = getService();
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index dd0c874..ff6e94b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -411,7 +411,7 @@
mPackageName = packageName;
mClient = IAutoFillManagerClient.Stub.asInterface(client);
- mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_STARTED, mPackageName);
+ writeLog(MetricsEvent.AUTOFILL_SESSION_STARTED);
}
/**
@@ -465,19 +465,16 @@
if ((response.getDatasets() == null || response.getDatasets().isEmpty())
&& response.getAuthentication() == null) {
// Response is "empty" from an UI point of view, need to notify client.
- notifyUnavailableToClient();
+ notifyUnavailableToClient(false);
}
synchronized (mLock) {
processResponseLocked(response, requestFlags);
}
- final LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST))
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST, servicePackageName)
.setType(MetricsEvent.TYPE_SUCCESS)
- .setPackageName(mPackageName)
.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
- response.getDatasets() == null ? 0 : response.getDatasets().size())
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE,
- servicePackageName);
+ response.getDatasets() == null ? 0 : response.getDatasets().size());
mMetricsLogger.write(log);
}
@@ -493,10 +490,8 @@
}
mService.resetLastResponse();
}
- LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST))
- .setType(MetricsEvent.TYPE_FAILURE)
- .setPackageName(mPackageName)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
+ LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST, servicePackageName)
+ .setType(MetricsEvent.TYPE_FAILURE);
mMetricsLogger.write(log);
getUiForShowing().showError(message, this);
@@ -515,11 +510,8 @@
return;
}
}
- LogMaker log = (new LogMaker(
- MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST))
- .setType(MetricsEvent.TYPE_SUCCESS)
- .setPackageName(mPackageName)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
+ LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
+ .setType(MetricsEvent.TYPE_SUCCESS);
mMetricsLogger.write(log);
// Nothing left to do...
@@ -539,11 +531,8 @@
return;
}
}
- LogMaker log = (new LogMaker(
- MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST))
- .setType(MetricsEvent.TYPE_FAILURE)
- .setPackageName(mPackageName)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName);
+ LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
+ .setType(MetricsEvent.TYPE_FAILURE);
mMetricsLogger.write(log);
getUiForShowing().showError(message, this);
@@ -577,6 +566,10 @@
// FillServiceCallbacks
@Override
public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras) {
+ if (sDebug) {
+ Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex
+ + "; intentSender=" + intent);
+ }
final Intent fillInIntent;
synchronized (mLock) {
if (mDestroyed) {
@@ -585,6 +578,10 @@
return;
}
fillInIntent = createAuthFillInIntentLocked(requestId, extras);
+ if (fillInIntent == null) {
+ forceRemoveSelfLocked();
+ return;
+ }
}
mService.setAuthenticationSelected(id);
@@ -739,21 +736,22 @@
final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
if (sDebug) Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result);
if (result instanceof FillResponse) {
- final FillResponse response = (FillResponse) result;
- mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName);
- replaceResponseLocked(authenticatedResponse, response);
+ writeLog(MetricsEvent.AUTOFILL_AUTHENTICATED);
+ replaceResponseLocked(authenticatedResponse, (FillResponse) result);
} else if (result instanceof Dataset) {
- // TODO: add proper metric
if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
+ writeLog(MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
final Dataset dataset = (Dataset) result;
authenticatedResponse.getDatasets().set(datasetIdx, dataset);
autoFill(requestId, datasetIdx, dataset, false);
+ } else {
+ writeLog(MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
}
} else {
if (result != null) {
Slog.w(TAG, "service returned invalid auth type: " + result);
}
- // TODO: add proper metric (on else)
+ writeLog(MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION);
processNullResponseLocked(0);
}
}
@@ -767,6 +765,44 @@
mHasCallback = hasIt;
}
+ @Nullable
+ private FillResponse getLastResponseLocked(@Nullable String logPrefix) {
+ if (mContexts == null) {
+ if (sDebug && logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts");
+ return null;
+ }
+ if (mResponses == null) {
+ // Happens when the activity / session was finished before the service replied, or
+ // when the service cannot autofill it (and returned a null response).
+ if (sVerbose && logPrefix != null) {
+ Slog.v(TAG, logPrefix + ": no responses on session");
+ }
+ return null;
+ }
+
+ final int lastResponseIdx = getLastResponseIndexLocked();
+ if (lastResponseIdx < 0) {
+ if (logPrefix != null) {
+ Slog.w(TAG, logPrefix + ": did not get last response. mResponses=" + mResponses
+ + ", mViewStates=" + mViewStates);
+ }
+ return null;
+ }
+
+ final FillResponse response = mResponses.valueAt(lastResponseIdx);
+ if (sVerbose && logPrefix != null) {
+ Slog.v(TAG, logPrefix + ": mResponses=" + mResponses + ", mContexts=" + mContexts
+ + ", mViewStates=" + mViewStates);
+ }
+ return response;
+ }
+
+ @Nullable
+ private SaveInfo getSaveInfoLocked() {
+ final FillResponse response = getLastResponseLocked(null);
+ return response == null ? null : response.getSaveInfo();
+ }
+
/**
* Shows the save UI, when session can be saved.
*
@@ -778,32 +814,8 @@
+ id + " destroyed");
return false;
}
- if (mContexts == null) {
- Slog.d(TAG, "showSaveLocked(): no contexts");
- return true;
- }
- if (mResponses == null) {
- // Happens when the activity / session was finished before the service replied, or
- // when the service cannot autofill it (and returned a null response).
- if (sVerbose) {
- Slog.v(TAG, "showSaveLocked(): no responses on session");
- }
- return true;
- }
-
- final int lastResponseIdx = getLastResponseIndexLocked();
- if (lastResponseIdx < 0) {
- Slog.w(TAG, "showSaveLocked(): did not get last response. mResponses=" + mResponses
- + ", mViewStates=" + mViewStates);
- return true;
- }
-
- final FillResponse response = mResponses.valueAt(lastResponseIdx);
- final SaveInfo saveInfo = response.getSaveInfo();
- if (sVerbose) {
- Slog.v(TAG, "showSaveLocked(): mResponses=" + mResponses + ", mContexts=" + mContexts
- + ", mViewStates=" + mViewStates);
- }
+ final FillResponse response = getLastResponseLocked("showSaveLocked()");
+ final SaveInfo saveInfo = response == null ? null : response.getSaveInfo();
/*
* The Save dialog is only shown if all conditions below are met:
@@ -915,15 +927,21 @@
final InternalValidator validator = saveInfo.getValidator();
if (validator != null) {
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SAVE_VALIDATION);
boolean isValid;
try {
isValid = validator.isValid(valueFinder);
+ log.setType(isValid
+ ? MetricsEvent.TYPE_SUCCESS
+ : MetricsEvent.TYPE_DISMISS);
} catch (Exception e) {
- Slog.e(TAG, "Not showing save UI because of exception during validation "
- + e.getClass());
+ Slog.e(TAG, "Not showing save UI because validation failed:", e);
+ log.setType(MetricsEvent.TYPE_FAILURE);
+ mMetricsLogger.write(log);
return true;
}
+ mMetricsLogger.write(log);
if (!isValid) {
Slog.i(TAG, "not showing save UI because fields failed validation");
return true;
@@ -971,7 +989,8 @@
final IAutoFillManagerClient client = getClient();
mPendingSaveUi = new PendingUi(mActivityToken, id, client);
getUiForShowing().showSaveUi(mService.getServiceLabel(), mService.getServiceIcon(),
- saveInfo, valueFinder, mPackageName, this, mPendingSaveUi);
+ mService.getServicePackageName(), saveInfo, valueFinder, mPackageName, this,
+ mPendingSaveUi);
if (client != null) {
try {
client.setSaveUiState(id, true);
@@ -1236,6 +1255,21 @@
break;
case ACTION_VALUE_CHANGED:
if (value != null && !value.equals(viewState.getCurrentValue())) {
+ if (value.isEmpty()
+ && viewState.getCurrentValue() != null
+ && viewState.getCurrentValue().isText()
+ && viewState.getCurrentValue().getTextValue() != null
+ && getSaveInfoLocked() != null) {
+ final int length = viewState.getCurrentValue().getTextValue().length();
+ if (sDebug) {
+ Slog.d(TAG, "updateLocked(" + id + "): resetting value that was "
+ + length + " chars long");
+ }
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length);
+ mMetricsLogger.write(log);
+ }
+
// Always update the internal state.
viewState.setCurrentValue(value);
@@ -1311,7 +1345,8 @@
filterText = value.getTextValue().toString();
}
- getUiForShowing().showFillUi(filledId, response, filterText, mPackageName, this);
+ getUiForShowing().showFillUi(filledId, response, filterText,
+ mService.getServicePackageName(), mPackageName, this);
}
boolean isDestroyed() {
@@ -1326,11 +1361,15 @@
}
}
- private void notifyUnavailableToClient() {
+ private void notifyUnavailableToClient(boolean sessionFinished) {
synchronized (mLock) {
- if (!mHasCallback || mCurrentViewId == null) return;
+ if (mCurrentViewId == null) return;
try {
- mClient.notifyNoFillUi(id, mCurrentViewId);
+ if (mHasCallback) {
+ mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinished);
+ } else if (sessionFinished) {
+ mClient.setSessionFinished();
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e);
}
@@ -1418,7 +1457,7 @@
}
mService.resetLastResponse();
// Nothing to be done, but need to notify client.
- notifyUnavailableToClient();
+ notifyUnavailableToClient(true);
removeSelf();
}
@@ -1537,6 +1576,10 @@
}
void autoFill(int requestId, int datasetIndex, Dataset dataset, boolean generateEvent) {
+ if (sDebug) {
+ Slog.d(TAG, "autoFill(): requestId=" + requestId + "; datasetIdx=" + datasetIndex
+ + "; dataset=" + dataset);
+ }
synchronized (mLock) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#autoFill() rejected - session: "
@@ -1557,10 +1600,14 @@
mService.logDatasetAuthenticationSelected(dataset.getId(), id);
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState);
-
+ if (fillInIntent == null) {
+ forceRemoveSelfLocked();
+ return;
+ }
final int authenticationId = AutofillManager.makeAuthenticationId(requestId,
datasetIndex);
startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent);
+
}
}
@@ -1570,14 +1617,16 @@
}
}
+ // TODO: this should never be null, but we got at least one occurrence, probably due to a race.
+ @Nullable
private Intent createAuthFillInIntentLocked(int requestId, Bundle extras) {
final Intent fillInIntent = new Intent();
final FillContext context = getFillContextByRequestIdLocked(requestId);
if (context == null) {
- // TODO(b/653742740): this will crash system_server. We need to handle it, but we're
- // keeping it crashing for now so we can diagnose when it happens again
- Slog.wtf(TAG, "no FillContext for requestId" + requestId + "; mContexts= " + mContexts);
+ Slog.wtf(TAG, "createAuthFillInIntentLocked(): no FillContext. requestId=" + requestId
+ + "; mContexts= " + mContexts);
+ return null;
}
fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure());
fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, extras);
@@ -1717,10 +1766,10 @@
if (mDestroyed) {
return null;
}
- mUi.destroyAll(mPendingSaveUi, this);
+ mUi.destroyAll(mPendingSaveUi, this, true);
mUi.clearCallback(this);
mDestroyed = true;
- mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName);
+ writeLog(MetricsEvent.AUTOFILL_SESSION_FINISHED);
return mRemoteFillService;
}
@@ -1733,7 +1782,16 @@
mPendingSaveUi = null;
removeSelfLocked();
- mUi.destroyAll(mPendingSaveUi, this);
+
+ mHandlerCaller.getHandler().post(() -> {
+ try {
+ mClient.setState(mService.isEnabled(), true, false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "error updating client state: " + e);
+ }
+ });
+
+ mUi.destroyAll(mPendingSaveUi, this, false);
}
/**
@@ -1805,4 +1863,16 @@
}
return lastResponseIdx;
}
+
+ private LogMaker newLogMaker(int category) {
+ return newLogMaker(category, mService.getServicePackageName());
+ }
+
+ private LogMaker newLogMaker(int category, String servicePackageName) {
+ return Helper.newLogMaker(category, mPackageName, servicePackageName);
+ }
+
+ private void writeLog(int category) {
+ mMetricsLogger.write(newLogMaker(category));
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index cac2bff..36b95fc 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -40,8 +40,9 @@
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.UiThread;
+import com.android.server.autofill.Helper;
import java.io.PrintWriter;
@@ -158,21 +159,22 @@
* @param focusedId the currently focused field
* @param response the current fill response
* @param filterText text of the view to be filled
+ * @param servicePackageName package name of the autofill service filling the activity
* @param packageName package name of the activity that is filled
* @param callback Identifier for the caller
*/
public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
- @Nullable String filterText, @NonNull String packageName,
- @NonNull AutoFillUiCallback callback) {
+ @Nullable String filterText, @Nullable String servicePackageName,
+ @NonNull String packageName, @NonNull AutoFillUiCallback callback) {
if (sDebug) {
final int size = filterText == null ? 0 : filterText.length();
Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars");
}
- final LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_FILL_UI))
- .setPackageName(packageName)
- .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN,
+ final LogMaker log =
+ Helper.newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, packageName, servicePackageName)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN,
filterText == null ? 0 : filterText.length())
- .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
response.getDatasets() == null ? 0 : response.getDatasets().size());
mHandler.post(() -> {
@@ -184,7 +186,7 @@
filterText, mOverlayControl, new FillUi.Callback() {
@Override
public void onResponsePicked(FillResponse response) {
- log.setType(MetricsProto.MetricsEvent.TYPE_DETAIL);
+ log.setType(MetricsEvent.TYPE_DETAIL);
hideFillUiUiThread(callback);
if (mCallback != null) {
mCallback.authenticate(response.getRequestId(),
@@ -195,7 +197,7 @@
@Override
public void onDatasetPicked(Dataset dataset) {
- log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
+ log.setType(MetricsEvent.TYPE_ACTION);
hideFillUiUiThread(callback);
if (mCallback != null) {
final int datasetIndex = response.getDatasets().indexOf(dataset);
@@ -205,14 +207,14 @@
@Override
public void onCanceled() {
- log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
+ log.setType(MetricsEvent.TYPE_DISMISS);
hideFillUiUiThread(callback);
}
@Override
public void onDestroy() {
- if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) {
- log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
+ if (log.getType() == MetricsEvent.TYPE_UNKNOWN) {
+ log.setType(MetricsEvent.TYPE_CLOSE);
}
mMetricsLogger.write(log);
}
@@ -246,37 +248,39 @@
* Shows the UI asking the user to save for autofill.
*/
public void showSaveUi(@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
- @NonNull SaveInfo info,@NonNull ValueFinder valueFinder, @NonNull String packageName,
+ @Nullable String servicePackageName, @NonNull SaveInfo info,
+ @NonNull ValueFinder valueFinder, @NonNull String packageName,
@NonNull AutoFillUiCallback callback, @NonNull PendingUi pendingSaveUi) {
if (sVerbose) Slog.v(TAG, "showSaveUi() for " + packageName + ": " + info);
int numIds = 0;
numIds += info.getRequiredIds() == null ? 0 : info.getRequiredIds().length;
numIds += info.getOptionalIds() == null ? 0 : info.getOptionalIds().length;
- LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_SAVE_UI))
- .setPackageName(packageName).addTaggedData(
- MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds);
+ final LogMaker log =
+ Helper.newLogMaker(MetricsEvent.AUTOFILL_SAVE_UI, packageName, servicePackageName)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds);
mHandler.post(() -> {
if (callback != mCallback) {
return;
}
hideAllUiThread(callback);
- mSaveUi = new SaveUi(mContext, pendingSaveUi, serviceLabel, serviceIcon, info,
- valueFinder, mOverlayControl, new SaveUi.OnSaveListener() {
+ mSaveUi = new SaveUi(mContext, pendingSaveUi, serviceLabel, serviceIcon,
+ servicePackageName, packageName, info, valueFinder, mOverlayControl,
+ new SaveUi.OnSaveListener() {
@Override
public void onSave() {
- log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
+ log.setType(MetricsEvent.TYPE_ACTION);
hideSaveUiUiThread(mCallback);
if (mCallback != null) {
mCallback.save();
}
- destroySaveUiUiThread(pendingSaveUi);
+ destroySaveUiUiThread(pendingSaveUi, true);
}
@Override
public void onCancel(IntentSender listener) {
- log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
+ log.setType(MetricsEvent.TYPE_DISMISS);
hideSaveUiUiThread(mCallback);
if (listener != null) {
try {
@@ -289,13 +293,13 @@
if (mCallback != null) {
mCallback.cancelSave();
}
- destroySaveUiUiThread(pendingSaveUi);
+ destroySaveUiUiThread(pendingSaveUi, true);
}
@Override
public void onDestroy() {
- if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) {
- log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
+ if (log.getType() == MetricsEvent.TYPE_UNKNOWN) {
+ log.setType(MetricsEvent.TYPE_CLOSE);
if (mCallback != null) {
mCallback.cancelSave();
@@ -331,8 +335,8 @@
* Destroy all UI affordances.
*/
public void destroyAll(@Nullable PendingUi pendingSaveUi,
- @Nullable AutoFillUiCallback callback) {
- mHandler.post(() -> destroyAllUiThread(pendingSaveUi, callback));
+ @Nullable AutoFillUiCallback callback, boolean notifyClient) {
+ mHandler.post(() -> destroyAllUiThread(pendingSaveUi, callback, notifyClient));
}
public void dump(PrintWriter pw) {
@@ -375,7 +379,7 @@
}
@android.annotation.UiThread
- private void destroySaveUiUiThread(@Nullable PendingUi pendingSaveUi) {
+ private void destroySaveUiUiThread(@Nullable PendingUi pendingSaveUi, boolean notifyClient) {
if (mSaveUi == null) {
// Calling destroySaveUiUiThread() twice is normal - it usually happens when the
// first call is made after the SaveUI is hidden and the second when the session is
@@ -387,7 +391,7 @@
if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): " + pendingSaveUi);
mSaveUi.destroy();
mSaveUi = null;
- if (pendingSaveUi != null) {
+ if (pendingSaveUi != null && notifyClient) {
try {
if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): notifying client");
pendingSaveUi.client.setSaveUiState(pendingSaveUi.id, false);
@@ -399,9 +403,9 @@
@android.annotation.UiThread
private void destroyAllUiThread(@Nullable PendingUi pendingSaveUi,
- @Nullable AutoFillUiCallback callback) {
+ @Nullable AutoFillUiCallback callback, boolean notifyClient) {
hideFillUiUiThread(callback);
- destroySaveUiUiThread(pendingSaveUi);
+ destroySaveUiUiThread(pendingSaveUi, notifyClient);
}
@android.annotation.UiThread
@@ -413,7 +417,7 @@
Slog.d(TAG, "hideAllUiThread(): "
+ "destroying Save UI because pending restoration is finished");
}
- destroySaveUiUiThread(pendingSaveUi);
+ destroySaveUiUiThread(pendingSaveUi, true);
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index d0b2e92..32f4d69 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -20,16 +20,15 @@
import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -53,6 +52,8 @@
import android.widget.TextView;
import com.android.internal.R;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.UiThread;
import java.io.PrintWriter;
@@ -111,6 +112,7 @@
}
private final Handler mHandler = UiThread.getHandler();
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
private final @NonNull Dialog mDialog;
@@ -121,16 +123,21 @@
private final CharSequence mTitle;
private final CharSequence mSubTitle;
private final PendingUi mPendingUi;
+ private final String mServicePackageName;
+ private final String mPackageName;
private boolean mDestroyed;
SaveUi(@NonNull Context context, @NonNull PendingUi pendingUi,
@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
+ @Nullable String servicePackageName, @NonNull String packageName,
@NonNull SaveInfo info, @NonNull ValueFinder valueFinder,
@NonNull OverlayControl overlayControl, @NonNull OnSaveListener listener) {
mPendingUi= pendingUi;
mListener = new OneTimeListener(listener);
mOverlayControl = overlayControl;
+ mServicePackageName = servicePackageName;
+ mPackageName = packageName;
final LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.autofill_save, null);
@@ -181,6 +188,8 @@
ScrollView subtitleContainer = null;
final CustomDescription customDescription = info.getCustomDescription();
if (customDescription != null) {
+ writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_DESCRIPTION, type);
+
mSubTitle = null;
if (sDebug) Slog.d(TAG, "Using custom description");
@@ -190,40 +199,35 @@
@Override
public boolean onClickHandler(View view, PendingIntent pendingIntent,
Intent intent) {
+ final LogMaker log =
+ newLogMaker(MetricsEvent.AUTOFILL_SAVE_LINK_TAPPED, type);
// We need to hide the Save UI before launching the pending intent, and
// restore back it once the activity is finished, and that's achieved by
// adding a custom extra in the activity intent.
- if (pendingIntent != null) {
- if (intent == null) {
- Slog.w(TAG,
- "remote view on custom description does not have intent");
- return false;
- }
- if (!pendingIntent.isActivity()) {
- Slog.w(TAG, "ignoring custom description pending intent that's not "
- + "for an activity: " + pendingIntent);
- return false;
- }
- if (sVerbose) {
- Slog.v(TAG,
- "Intercepting custom description intent: " + intent);
- }
- final IBinder token = mPendingUi.getToken();
- intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token);
- try {
- pendingUi.client.startIntentSender(pendingIntent.getIntentSender(),
- intent);
- mPendingUi.setState(PendingUi.STATE_PENDING);
- if (sDebug) {
- Slog.d(TAG, "hiding UI until restored with token " + token);
- }
- hide();
- } catch (RemoteException e) {
- Slog.w(TAG, "error triggering pending intent: " + intent);
- return false;
- }
+ final boolean isValid = isValidLink(pendingIntent, intent);
+ if (!isValid) {
+ log.setType(MetricsEvent.TYPE_UNKNOWN);
+ mMetricsLogger.write(log);
+ return false;
}
- return true;
+ if (sVerbose) Slog.v(TAG, "Intercepting custom description intent");
+ final IBinder token = mPendingUi.getToken();
+ intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token);
+ try {
+ pendingUi.client.startIntentSender(pendingIntent.getIntentSender(),
+ intent);
+ mPendingUi.setState(PendingUi.STATE_PENDING);
+ if (sDebug) Slog.d(TAG, "hiding UI until restored with token " + token);
+ hide();
+ log.setType(MetricsEvent.TYPE_OPEN);
+ mMetricsLogger.write(log);
+ return true;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "error triggering pending intent: " + intent);
+ log.setType(MetricsEvent.TYPE_FAILURE);
+ mMetricsLogger.write(log);
+ return false;
+ }
}
};
@@ -241,6 +245,7 @@
} else {
mSubTitle = info.getDescription();
if (mSubTitle != null) {
+ writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_SUBTITLE, type);
subtitleContainer = view.findViewById(R.id.autofill_save_custom_subtitle);
final TextView subtitleView = new TextView(context);
subtitleView.setText(mSubTitle);
@@ -309,10 +314,41 @@
Slog.w(TAG, "Not adding service icon of size "
+ "(" + actualWidth + "x" + actualHeight + ") because maximum is "
+ "(" + maxWidth + "x" + maxHeight + ").");
- iconView.setVisibility(View.INVISIBLE);
+ ((ViewGroup)iconView.getParent()).removeView(iconView);
}
}
+ private static boolean isValidLink(PendingIntent pendingIntent, Intent intent) {
+ if (pendingIntent == null) {
+ Slog.w(TAG, "isValidLink(): custom description without pending intent");
+ return false;
+ }
+ if (!pendingIntent.isActivity()) {
+ Slog.w(TAG, "isValidLink(): pending intent not for activity");
+ return false;
+ }
+ if (intent == null) {
+ Slog.w(TAG, "isValidLink(): no intent");
+ return false;
+ }
+ return true;
+ }
+
+ private LogMaker newLogMaker(int category, int saveType) {
+ return newLogMaker(category)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SAVE_TYPE, saveType);
+ }
+
+ private LogMaker newLogMaker(int category) {
+ return new LogMaker(category)
+ .setPackageName(mPackageName)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, mServicePackageName);
+ }
+
+ private void writeLog(int category, int saveType) {
+ mMetricsLogger.write(newLogMaker(category, saveType));
+ }
+
/**
* Update the pending UI, if any.
*
@@ -326,17 +362,25 @@
+ mPendingUi.getToken());
return;
}
- switch (operation) {
- case AutofillManager.PENDING_UI_OPERATION_RESTORE:
- if (sDebug) Slog.d(TAG, "Restoring save dialog for " + token);
- show();
- break;
- case AutofillManager.PENDING_UI_OPERATION_CANCEL:
- if (sDebug) Slog.d(TAG, "Cancelling pending save dialog for " + token);
- hide();
- break;
- default:
- Slog.w(TAG, "restore(): invalid operation " + operation);
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_PENDING_SAVE_UI_OPERATION);
+ try {
+ switch (operation) {
+ case AutofillManager.PENDING_UI_OPERATION_RESTORE:
+ if (sDebug) Slog.d(TAG, "Restoring save dialog for " + token);
+ log.setType(MetricsEvent.TYPE_OPEN);
+ show();
+ break;
+ case AutofillManager.PENDING_UI_OPERATION_CANCEL:
+ log.setType(MetricsEvent.TYPE_DISMISS);
+ if (sDebug) Slog.d(TAG, "Cancelling pending save dialog for " + token);
+ hide();
+ break;
+ default:
+ log.setType(MetricsEvent.TYPE_FAILURE);
+ Slog.w(TAG, "restore(): invalid operation " + operation);
+ }
+ } finally {
+ mMetricsLogger.write(log);
}
mPendingUi.setState(PendingUi.STATE_FINISHED);
}
@@ -385,6 +429,8 @@
pw.print(prefix); pw.print("title: "); pw.println(mTitle);
pw.print(prefix); pw.print("subtitle: "); pw.println(mSubTitle);
pw.print(prefix); pw.print("pendingUi: "); pw.println(mPendingUi);
+ pw.print(prefix); pw.print("service: "); pw.println(mServicePackageName);
+ pw.print(prefix); pw.print("app: "); pw.println(mPackageName);
final View view = mDialog.getWindow().getDecorView();
final int[] loc = view.getLocationOnScreen();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e0cde72..90ad8a5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2162,6 +2162,15 @@
}
}
+ if (r.fgRequired) {
+ if (DEBUG_FOREGROUND_SERVICE) {
+ Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid)
+ + " for fg-service launch");
+ }
+ mAm.tempWhitelistUidLocked(r.appInfo.uid,
+ SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch");
+ }
+
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8337892..cbfc747 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -713,7 +713,7 @@
public boolean canShowErrorDialogs() {
return mShowDialogs && !mSleeping && !mShuttingDown
- && !mKeyguardController.isKeyguardShowing()
+ && !mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)
&& !(UserManager.isDeviceInDemoMode(mContext)
&& mUserController.getCurrentUser().isDemo());
}
@@ -12463,19 +12463,14 @@
void onWakefulnessChanged(int wakefulness) {
synchronized(this) {
+ boolean wasAwake = mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
+ boolean isAwake = wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
mWakefulness = wakefulness;
- // Also update state in a special way for running foreground services UI.
- switch (mWakefulness) {
- case PowerManagerInternal.WAKEFULNESS_ASLEEP:
- case PowerManagerInternal.WAKEFULNESS_DREAMING:
- case PowerManagerInternal.WAKEFULNESS_DOZING:
- mServices.updateScreenStateLocked(false /* screenOn */);
- break;
- case PowerManagerInternal.WAKEFULNESS_AWAKE:
- default:
- mServices.updateScreenStateLocked(true /* screenOn */);
- break;
+ if (wasAwake != isAwake) {
+ // Also update state in a special way for running foreground services UI.
+ mServices.updateScreenStateLocked(isAwake);
+ sendNotifyVrManagerOfSleepState(!isAwake);
}
}
}
@@ -12511,7 +12506,6 @@
}
mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
if (wasSleeping) {
- sendNotifyVrManagerOfSleepState(false);
updateOomAdjLocked();
}
} else if (!mSleeping && shouldSleep) {
@@ -12521,7 +12515,6 @@
}
mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
mStackSupervisor.goingToSleepLocked();
- sendNotifyVrManagerOfSleepState(true);
updateOomAdjLocked();
}
}
@@ -12615,7 +12608,7 @@
}
@Override
- public void setLockScreenShown(boolean showing) {
+ public void setLockScreenShown(boolean showing, int secondaryDisplayShowing) {
if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
@@ -12625,7 +12618,7 @@
synchronized(this) {
long ident = Binder.clearCallingIdentity();
try {
- mKeyguardController.setKeyguardShown(showing);
+ mKeyguardController.setKeyguardShown(showing, secondaryDisplayShowing);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -14046,10 +14039,10 @@
mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
|| Settings.Global.getInt(
resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
- final boolean supportsPictureInPicture =
- mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
final boolean supportsMultiWindow = ActivityManager.supportsMultiWindow(mContext);
+ final boolean supportsPictureInPicture = supportsMultiWindow &&
+ mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
final boolean supportsSplitScreenMultiWindow =
ActivityManager.supportsSplitScreenMultiWindow(mContext);
final boolean supportsMultiDisplay = mContext.getPackageManager()
@@ -24088,7 +24081,7 @@
@Override
public void notifyKeyguardTrustedChanged() {
synchronized (ActivityManagerService.this) {
- if (mKeyguardController.isKeyguardShowing()) {
+ if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 5b27c9c..b2bbf19 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1176,8 +1176,15 @@
* can be put a secondary screen.
*/
boolean canBeLaunchedOnDisplay(int displayId) {
+ final TaskRecord task = getTask();
+
+ // The resizeability of an Activity's parent task takes precendence over the ActivityInfo.
+ // This allows for a non resizable activity to be launched into a resizeable task.
+ final boolean resizeable =
+ task != null ? task.isResizeable() : supportsResizeableMultiWindow();
+
return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
- supportsResizeableMultiWindow(), launchedFromPid, launchedFromUid, info);
+ resizeable, launchedFromPid, launchedFromUid, info);
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 791b2c0..a0817c4 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1980,7 +1980,8 @@
boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
boolean isTop) {
final boolean isInPinnedStack = r.getStack().getStackId() == PINNED_STACK_ID;
- final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing();
+ final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing(
+ mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
final boolean keyguardLocked = mStackSupervisor.mKeyguardController.isKeyguardLocked();
final boolean showWhenLocked = r.canShowWhenLocked() && !isInPinnedStack;
final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
@@ -5205,8 +5206,8 @@
voiceInteractor, type);
// add the task to stack first, mTaskPositioner might need the stack association
addTask(task, toTop, "createTaskRecord");
- final boolean isLockscreenShown =
- mService.mStackSupervisor.mKeyguardController.isKeyguardShowing();
+ final boolean isLockscreenShown = mService.mStackSupervisor.mKeyguardController
+ .isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
if (!layoutTaskInStack(task, info.windowLayout) && mBounds != null && task.isResizeable()
&& !isLockscreenShown) {
task.updateOverrideConfiguration(mBounds);
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index cea80c8..8596113 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
@@ -66,6 +67,7 @@
private int mBeforeUnoccludeTransit;
private int mVisibilityTransactionDepth;
private SleepToken mSleepToken;
+ private int mSecondaryDisplayShowing = INVALID_DISPLAY;
KeyguardController(ActivityManagerService service,
ActivityStackSupervisor stackSupervisor) {
@@ -78,10 +80,12 @@
}
/**
- * @return true if Keyguard is showing, not going away, and not being occluded, false otherwise
+ * @return true if Keyguard is showing, not going away, and not being occluded on the given
+ * display, false otherwise
*/
- boolean isKeyguardShowing() {
- return mKeyguardShowing && !mKeyguardGoingAway && !mOccluded;
+ boolean isKeyguardShowing(int displayId) {
+ return mKeyguardShowing && !mKeyguardGoingAway &&
+ (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing);
}
/**
@@ -94,15 +98,19 @@
/**
* Update the Keyguard showing state.
*/
- void setKeyguardShown(boolean showing) {
- if (showing == mKeyguardShowing) {
+ void setKeyguardShown(boolean showing, int secondaryDisplayShowing) {
+ boolean showingChanged = showing != mKeyguardShowing;
+ if (!showingChanged && secondaryDisplayShowing == mSecondaryDisplayShowing) {
return;
}
mKeyguardShowing = showing;
- dismissDockedStackIfNeeded();
- if (showing) {
- setKeyguardGoingAway(false);
- mDismissalRequested = false;
+ mSecondaryDisplayShowing = secondaryDisplayShowing;
+ if (showingChanged) {
+ dismissDockedStackIfNeeded();
+ if (showing) {
+ setKeyguardGoingAway(false);
+ mDismissalRequested = false;
+ }
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
updateKeyguardSleepToken();
@@ -337,9 +345,9 @@
}
private void updateKeyguardSleepToken() {
- if (mSleepToken == null && isKeyguardShowing()) {
+ if (mSleepToken == null && isKeyguardShowing(DEFAULT_DISPLAY)) {
mSleepToken = mService.acquireSleepToken("Keyguard", DEFAULT_DISPLAY);
- } else if (mSleepToken != null && !isKeyguardShowing()) {
+ } else if (mSleepToken != null && !isKeyguardShowing(DEFAULT_DISPLAY)) {
mSleepToken.release();
mSleepToken = null;
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 91b1591..11d0470 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6134,12 +6134,12 @@
private int mSafeMediaVolumeIndex;
// mSafeUsbMediaVolumeIndex is used for USB Headsets and is the music volume UI index
// corresponding to a gain of -30 dBFS in audio flinger mixer.
- // We remove -15 dBs from the theoretical -15dB to account for the EQ boost when bands are set
- // to max gain.
+ // We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost
+ // amplification when both effects are on with all band gains at maximum.
// This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when
// the headset is compliant to EN 60950 with a max loudness of 100dB SPL.
private int mSafeUsbMediaVolumeIndex;
- private static final float SAFE_VOLUME_GAIN_DBFS = -30.0f;
+ private static final float SAFE_VOLUME_GAIN_DBFS = -37.0f;
// mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET |
AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index ee38219..67e7216 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -38,6 +38,7 @@
import android.net.metrics.NetworkEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
+import android.net.metrics.WakeupStats;
import android.os.Parcelable;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -115,6 +116,22 @@
return out;
}
+ public static IpConnectivityEvent toProto(WakeupStats in) {
+ IpConnectivityLogClass.WakeupStats wakeupStats =
+ new IpConnectivityLogClass.WakeupStats();
+ in.updateDuration();
+ wakeupStats.durationSec = in.durationSec;
+ wakeupStats.totalWakeups = in.totalWakeups;
+ wakeupStats.rootWakeups = in.rootWakeups;
+ wakeupStats.systemWakeups = in.systemWakeups;
+ wakeupStats.nonApplicationWakeups = in.nonApplicationWakeups;
+ wakeupStats.applicationWakeups = in.applicationWakeups;
+ wakeupStats.noUidWakeups = in.noUidWakeups;
+ final IpConnectivityEvent out = buildEvent(0, 0, in.iface);
+ out.setWakeupStats(wakeupStats);
+ return out;
+ }
+
private static IpConnectivityEvent buildEvent(int netId, long transports, String ifname) {
final IpConnectivityEvent ev = new IpConnectivityEvent();
ev.networkId = netId;
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 4094083..25dba35 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import static android.util.TimeUtils.NANOS_PER_MS;
+
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetdEventCallback;
@@ -25,9 +27,12 @@
import android.net.metrics.DnsEvent;
import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
+import android.net.metrics.WakeupEvent;
+import android.net.metrics.WakeupStats;
import android.os.RemoteException;
import android.text.format.DateUtils;
import android.util.Log;
+import android.util.ArrayMap;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -59,12 +64,28 @@
private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS;
private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;
+ @VisibleForTesting
+ static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;
+ // TODO: dedup this String constant with the one used in
+ // ConnectivityService#wakeupModifyInterface().
+ @VisibleForTesting
+ static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:";
+
// Sparse arrays of DNS and connect events, grouped by net id.
@GuardedBy("this")
private final SparseArray<DnsEvent> mDnsEvents = new SparseArray<>();
@GuardedBy("this")
private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>();
+ // Array of aggregated wakeup event stats, grouped by interface name.
+ @GuardedBy("this")
+ private final ArrayMap<String, WakeupStats> mWakeupStats = new ArrayMap<>();
+ // Ring buffer array for storing packet wake up events sent by Netd.
+ @GuardedBy("this")
+ private final WakeupEvent[] mWakeupEvents = new WakeupEvent[WAKEUP_EVENT_BUFFER_LENGTH];
+ @GuardedBy("this")
+ private long mWakeupEventCursor = 0;
+
private final ConnectivityManager mCm;
@GuardedBy("this")
@@ -137,11 +158,62 @@
@Override
public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
+ maybeVerboseLog("onWakeupEvent(%s, %d, %d, %sns)", prefix, uid, gid, timestampNs);
+
+ // TODO: add ip protocol and port
+
+ String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
+ final long timestampMs;
+ if (timestampNs > 0) {
+ timestampMs = timestampNs / NANOS_PER_MS;
+ } else {
+ timestampMs = System.currentTimeMillis();
+ }
+
+ addWakeupEvent(iface, timestampMs, uid);
+ }
+
+ @GuardedBy("this")
+ private void addWakeupEvent(String iface, long timestampMs, int uid) {
+ int index = wakeupEventIndex(mWakeupEventCursor);
+ mWakeupEventCursor++;
+ WakeupEvent event = new WakeupEvent();
+ event.iface = iface;
+ event.timestampMs = timestampMs;
+ event.uid = uid;
+ mWakeupEvents[index] = event;
+ WakeupStats stats = mWakeupStats.get(iface);
+ if (stats == null) {
+ stats = new WakeupStats(iface);
+ mWakeupStats.put(iface, stats);
+ }
+ stats.countEvent(event);
+ }
+
+ @GuardedBy("this")
+ private WakeupEvent[] getWakeupEvents() {
+ int length = (int) Math.min(mWakeupEventCursor, (long) mWakeupEvents.length);
+ WakeupEvent[] out = new WakeupEvent[length];
+ // Reverse iteration from youngest event to oldest event.
+ long inCursor = mWakeupEventCursor - 1;
+ int outIdx = out.length - 1;
+ while (outIdx >= 0) {
+ out[outIdx--] = mWakeupEvents[wakeupEventIndex(inCursor--)];
+ }
+ return out;
+ }
+
+ private static int wakeupEventIndex(long cursor) {
+ return (int) Math.abs(cursor % WAKEUP_EVENT_BUFFER_LENGTH);
}
public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
+ for (int i = 0; i < mWakeupStats.size(); i++) {
+ events.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
+ }
+ mWakeupStats.clear();
}
public synchronized void dump(PrintWriter writer) {
@@ -153,13 +225,22 @@
}
public synchronized void list(PrintWriter pw) {
- listEvents(pw, mConnectEvents, (x) -> x);
- listEvents(pw, mDnsEvents, (x) -> x);
+ listEvents(pw, mConnectEvents, (x) -> x, "\n");
+ listEvents(pw, mDnsEvents, (x) -> x, "\n");
+ for (int i = 0; i < mWakeupStats.size(); i++) {
+ pw.println(mWakeupStats.valueAt(i));
+ }
+ for (WakeupEvent wakeup : getWakeupEvents()) {
+ pw.println(wakeup);
+ }
}
public synchronized void listAsProtos(PrintWriter pw) {
- listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto);
- listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto);
+ listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto, "");
+ listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto, "");
+ for (int i = 0; i < mWakeupStats.size(); i++) {
+ pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
+ }
}
private static <T> void flushProtos(List<IpConnectivityEvent> out, SparseArray<T> in,
@@ -170,10 +251,13 @@
in.clear();
}
- public static <T> void listEvents(
- PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper) {
+ private static <T> void listEvents(
+ PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper, String separator) {
+ // Proto derived Classes have toString method that adds a \n at the end.
+ // Let the caller control that by passing in the line separator explicitly.
for (int i = 0; i < events.size(); i++) {
- pw.println(mapper.apply(events.valueAt(i)).toString());
+ pw.print(mapper.apply(events.valueAt(i)));
+ pw.print(separator);
}
}
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java
index c4911b8..eec2c99 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/NightDisplayService.java
@@ -48,8 +48,10 @@
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.Calendar;
import java.util.TimeZone;
import com.android.internal.R;
@@ -308,7 +310,7 @@
}
@Override
- public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+ public void onCustomStartTimeChanged(LocalTime startTime) {
Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
if (mAutoMode != null) {
@@ -317,7 +319,7 @@
}
@Override
- public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+ public void onCustomEndTimeChanged(LocalTime endTime) {
Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
if (mAutoMode != null) {
@@ -416,6 +418,36 @@
outTemp[10] = blue;
}
+ /**
+ * Returns the first date time corresponding to the local time that occurs before the
+ * provided date time.
+ *
+ * @param compareTime the LocalDateTime to compare against
+ * @return the prior LocalDateTime corresponding to this local time
+ */
+ public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
+ final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
+ compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
+
+ // Check if the local time has passed, if so return the same time yesterday.
+ return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
+ }
+
+ /**
+ * Returns the first date time corresponding to this local time that occurs after the
+ * provided date time.
+ *
+ * @param compareTime the LocalDateTime to compare against
+ * @return the next LocalDateTime corresponding to this local time
+ */
+ public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
+ final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
+ compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
+
+ // Check if the local time has passed, if so return the same time tomorrow.
+ return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
+ }
+
private abstract class AutoMode implements NightDisplayController.Callback {
public abstract void onStart();
@@ -427,10 +459,10 @@
private final AlarmManager mAlarmManager;
private final BroadcastReceiver mTimeChangedReceiver;
- private NightDisplayController.LocalTime mStartTime;
- private NightDisplayController.LocalTime mEndTime;
+ private LocalTime mStartTime;
+ private LocalTime mEndTime;
- private Calendar mLastActivatedTime;
+ private LocalDateTime mLastActivatedTime;
CustomAutoMode() {
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
@@ -443,31 +475,15 @@
}
private void updateActivated() {
- final Calendar now = Calendar.getInstance();
- final Calendar startTime = mStartTime.getDateTimeBefore(now);
- final Calendar endTime = mEndTime.getDateTimeAfter(startTime);
+ final LocalDateTime now = LocalDateTime.now();
+ final LocalDateTime start = getDateTimeBefore(mStartTime, now);
+ final LocalDateTime end = getDateTimeAfter(mEndTime, start);
+ boolean activate = now.isBefore(end);
- boolean activate = now.before(endTime);
if (mLastActivatedTime != null) {
- // Convert mLastActivatedTime to the current timezone if needed.
- final TimeZone currentTimeZone = now.getTimeZone();
- if (!currentTimeZone.equals(mLastActivatedTime.getTimeZone())) {
- final int year = mLastActivatedTime.get(Calendar.YEAR);
- final int dayOfYear = mLastActivatedTime.get(Calendar.DAY_OF_YEAR);
- final int hourOfDay = mLastActivatedTime.get(Calendar.HOUR_OF_DAY);
- final int minute = mLastActivatedTime.get(Calendar.MINUTE);
-
- mLastActivatedTime.setTimeZone(currentTimeZone);
- mLastActivatedTime.set(Calendar.YEAR, year);
- mLastActivatedTime.set(Calendar.DAY_OF_YEAR, dayOfYear);
- mLastActivatedTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
- mLastActivatedTime.set(Calendar.MINUTE, minute);
- }
-
// Maintain the existing activated state if within the current period.
- if (mLastActivatedTime.before(now)
- && mLastActivatedTime.after(startTime)
- && (mLastActivatedTime.after(endTime) || now.before(endTime))) {
+ if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
+ && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
activate = mController.isActivated();
}
}
@@ -475,14 +491,16 @@
if (mIsActivated == null || mIsActivated != activate) {
mController.setActivated(activate);
}
+
updateNextAlarm(mIsActivated, now);
}
- private void updateNextAlarm(@Nullable Boolean activated, @NonNull Calendar now) {
+ private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
if (activated != null) {
- final Calendar next = activated ? mEndTime.getDateTimeAfter(now)
- : mStartTime.getDateTimeAfter(now);
- mAlarmManager.setExact(AlarmManager.RTC, next.getTimeInMillis(), TAG, this, null);
+ final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
+ : getDateTimeAfter(mStartTime, now);
+ final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
}
}
@@ -512,18 +530,18 @@
@Override
public void onActivated(boolean activated) {
mLastActivatedTime = mController.getLastActivatedTime();
- updateNextAlarm(activated, Calendar.getInstance());
+ updateNextAlarm(activated, LocalDateTime.now());
}
@Override
- public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+ public void onCustomStartTimeChanged(LocalTime startTime) {
mStartTime = startTime;
mLastActivatedTime = null;
updateActivated();
}
@Override
- public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+ public void onCustomEndTimeChanged(LocalTime endTime) {
mEndTime = endTime;
mLastActivatedTime = null;
updateActivated();
@@ -552,15 +570,14 @@
}
boolean activate = state.isNight();
- final Calendar lastActivatedTime = mController.getLastActivatedTime();
+ final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
if (lastActivatedTime != null) {
- final Calendar now = Calendar.getInstance();
- final Calendar sunrise = state.sunrise();
- final Calendar sunset = state.sunset();
-
+ final LocalDateTime now = LocalDateTime.now();
+ final LocalDateTime sunrise = state.sunrise();
+ final LocalDateTime sunset = state.sunset();
// Maintain the existing activated state if within the current period.
- if (lastActivatedTime.before(now)
- && (lastActivatedTime.after(sunrise) ^ lastActivatedTime.after(sunset))) {
+ if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
+ ^ lastActivatedTime.isBefore(sunset))) {
activate = mController.isActivated();
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 11043bd..14d9afb 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2366,6 +2366,13 @@
Slog.w(TAG, "Invalid escrow token supplied");
return false;
}
+ if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
+ // Most likely, an untrusted credential reset happened in the past which
+ // changed the synthetic password
+ Slog.e(TAG, "Obsolete token: synthetic password derived but it fails GK "
+ + "verification.");
+ return false;
+ }
// Update PASSWORD_TYPE_KEY since it's needed by notifyActivePasswordMetricsAvailable()
// called by setLockCredentialWithAuthTokenLocked().
// TODO: refactor usage of PASSWORD_TYPE_KEY b/65239740
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 0f580d8..4fafe34 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -438,16 +438,7 @@
PackageDexUsage.DexUseInfo dexUseInfo = e.getValue();
pw.println(dex);
pw.increaseIndent();
- for (String isa : dexUseInfo.getLoaderIsas()) {
- String status = null;
- try {
- status = DexFile.getDexFileStatus(path, isa);
- } catch (IOException ioe) {
- status = "[Exception]: " + ioe.getMessage();
- }
- pw.println(isa + ": " + status);
- }
-
+ // TODO(calin): get the status of the oat file (needs installd call)
pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
if (dexUseInfo.isUsedByOtherApps()) {
pw.println("used be other apps: " + dexUseInfo.getLoadingPackages());
@@ -474,8 +465,9 @@
}
if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
- // If the dex files is used by other apps, we cannot use profile-guided compilation.
- return getNonProfileGuidedCompilerFilter(targetCompilerFilter);
+ // If the dex files is used by other apps, apply the shared filter.
+ return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerService.REASON_SHARED);
}
return targetCompilerFilter;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f594b38..b2e0a08 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -577,8 +577,9 @@
public static final int REASON_BACKGROUND_DEXOPT = 3;
public static final int REASON_AB_OTA = 4;
public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 5;
+ public static final int REASON_SHARED = 6;
- public static final int REASON_LAST = REASON_INACTIVE_PACKAGE_DOWNGRADE;
+ public static final int REASON_LAST = REASON_SHARED;
/** All dangerous permission names in the same order as the events in MetricsEvent */
private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
@@ -9803,19 +9804,6 @@
compilerFilter,
dexoptFlags));
- if (pkg.isSystemApp()) {
- // Only dexopt shared secondary dex files belonging to system apps to not slow down
- // too much boot after an OTA.
- int secondaryDexoptFlags = dexoptFlags |
- DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
- DexoptOptions.DEXOPT_ONLY_SHARED_DEX;
- mDexManager.dexoptSecondaryDex(new DexoptOptions(
- pkg.packageName,
- compilerFilter,
- secondaryDexoptFlags));
- }
-
- // TODO(shubhamajmera): Record secondary dexopt stats.
switch (primaryDexOptStaus) {
case PackageDexOptimizer.DEX_OPT_PERFORMED:
numberOfPackagesOptimized++;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 1a97a72..19b0d9b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -26,14 +26,19 @@
public class PackageManagerServiceCompilerMapping {
// Names for compilation reasons.
static final String REASON_STRINGS[] = {
- "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive"
+ "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive", "shared"
};
+ static final int REASON_SHARED_INDEX = 6;
+
// Static block to ensure the strings array is of the right length.
static {
if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) {
throw new IllegalStateException("REASON_STRINGS not correct");
}
+ if (!"shared".equals(REASON_STRINGS[REASON_SHARED_INDEX])) {
+ throw new IllegalStateException("REASON_STRINGS not correct because of shared index");
+ }
}
private static String getSystemPropertyName(int reason) {
@@ -52,11 +57,18 @@
!DexFile.isValidCompilerFilter(sysPropValue)) {
throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
+ "(reason " + REASON_STRINGS[reason] + ")");
+ } else if (!isFilterAllowedForReason(reason, sysPropValue)) {
+ throw new IllegalStateException("Value \"" + sysPropValue +"\" not allowed "
+ + "(reason " + REASON_STRINGS[reason] + ")");
}
return sysPropValue;
}
+ private static boolean isFilterAllowedForReason(int reason, String filter) {
+ return reason != REASON_SHARED_INDEX || !DexFile.isProfileGuidedCompilerFilter(filter);
+ }
+
// Check that the properties are set and valid.
// Note: this is done in a separate method so this class can be statically initialized.
static void checkProperties() {
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 50e5e7b..5a5471b 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -1,5 +1,7 @@
package com.android.server.policy.keyguard;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -201,7 +203,10 @@
mKeyguardState.reset();
mHandler.post(() -> {
try {
- ActivityManager.getService().setLockScreenShown(true);
+ // There are no longer any keyguard windows on secondary displays, so pass
+ // INVALID_DISPLAY. All that means is that showWhenLocked activities on
+ // secondary displays now get to show.
+ ActivityManager.getService().setLockScreenShown(true, INVALID_DISPLAY);
} catch (RemoteException e) {
// Local call.
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 0c72326..be44607 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -120,7 +120,7 @@
implements Watchdog.Monitor {
private static final String TAG = "PowerManagerService";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private static final boolean DEBUG_SPEW = DEBUG && true;
// Message: Sent when a user activity timeout occurs to update the power state.
@@ -1569,12 +1569,15 @@
return true;
}
- private void setWakefulnessLocked(int wakefulness, int reason) {
+ @VisibleForTesting
+ void setWakefulnessLocked(int wakefulness, int reason) {
if (mWakefulness != wakefulness) {
mWakefulness = wakefulness;
mWakefulnessChanging = true;
mDirty |= DIRTY_WAKEFULNESS;
- mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
+ if (mNotifier != null) {
+ mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
+ }
}
}
@@ -2432,11 +2435,8 @@
return value >= -1.0f && value <= 1.0f;
}
- private int getDesiredScreenPolicyLocked() {
- if (mIsVrModeEnabled) {
- return DisplayPowerRequest.POLICY_VR;
- }
-
+ @VisibleForTesting
+ int getDesiredScreenPolicyLocked() {
if (mWakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
return DisplayPowerRequest.POLICY_OFF;
}
@@ -2452,6 +2452,13 @@
// doze after screen off. This causes the screen off transition to be skipped.
}
+ // It is important that POLICY_VR check happens after the wakefulness checks above so
+ // that VR-mode does not prevent displays from transitioning to the correct state when
+ // dozing or sleeping.
+ if (mIsVrModeEnabled) {
+ return DisplayPowerRequest.POLICY_VR;
+ }
+
if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
|| (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0
|| !mBootCompleted
@@ -3113,6 +3120,11 @@
}
}
+ @VisibleForTesting
+ void setVrModeEnabled(boolean enabled) {
+ mIsVrModeEnabled = enabled;
+ }
+
private void powerHintInternal(int hintId, int data) {
nativeSendPowerHint(hintId, data);
}
@@ -3810,7 +3822,7 @@
synchronized (mLock) {
if (mIsVrModeEnabled != enabled) {
- mIsVrModeEnabled = enabled;
+ setVrModeEnabled(enabled);
mDirty |= DIRTY_VR_MODE_CHANGED;
updatePowerStateLocked();
}
diff --git a/services/core/java/com/android/server/timezone/IntentHelper.java b/services/core/java/com/android/server/timezone/IntentHelper.java
index 0cb9065..5de5432 100644
--- a/services/core/java/com/android/server/timezone/IntentHelper.java
+++ b/services/core/java/com/android/server/timezone/IntentHelper.java
@@ -23,15 +23,22 @@
*/
interface IntentHelper {
- void initialize(String updateAppPackageName, String dataAppPackageName, Listener listener);
+ void initialize(String updateAppPackageName, String dataAppPackageName,
+ PackageTracker packageTracker);
void sendTriggerUpdateCheck(CheckToken checkToken);
- void enableReliabilityTriggering();
+ /**
+ * Schedule a "reliability trigger" after at least minimumDelayMillis, replacing any existing
+ * scheduled one. A reliability trigger ensures that the {@link PackageTracker} can pick up
+ * reliably if a previous update check did not complete for some reason. It can happen when
+ * the device is idle. The trigger is expected to call
+ * {@link PackageTracker#triggerUpdateIfNeeded(boolean)} with a {@code false} value.
+ */
+ void scheduleReliabilityTrigger(long minimumDelayMillis);
- void disableReliabilityTriggering();
-
- interface Listener {
- void triggerUpdateIfNeeded(boolean packageUpdated);
- }
+ /**
+ * Make sure there is no reliability trigger scheduled. No-op if there wasn't one.
+ */
+ void unscheduleReliabilityTrigger();
}
diff --git a/services/core/java/com/android/server/timezone/IntentHelperImpl.java b/services/core/java/com/android/server/timezone/IntentHelperImpl.java
index 6db70cd8..bc0f6e4 100644
--- a/services/core/java/com/android/server/timezone/IntentHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/IntentHelperImpl.java
@@ -36,16 +36,13 @@
private final Context mContext;
private String mUpdaterAppPackageName;
- private boolean mReliabilityReceiverEnabled;
- private Receiver mReliabilityReceiver;
-
IntentHelperImpl(Context context) {
mContext = context;
}
@Override
- public void initialize(
- String updaterAppPackageName, String dataAppPackageName, Listener listener) {
+ public void initialize(String updaterAppPackageName, String dataAppPackageName,
+ PackageTracker packageTracker) {
mUpdaterAppPackageName = updaterAppPackageName;
// Register for events of interest.
@@ -78,10 +75,8 @@
// We do not register for ACTION_PACKAGE_DATA_CLEARED because the updater / data apps are
// not expected to need local data.
- Receiver packageUpdateReceiver = new Receiver(listener, true /* packageUpdated */);
+ Receiver packageUpdateReceiver = new Receiver(packageTracker);
mContext.registerReceiver(packageUpdateReceiver, packageIntentFilter);
-
- mReliabilityReceiver = new Receiver(listener, false /* packageUpdated */);
}
/** Sends an intent to trigger an update check. */
@@ -93,39 +88,26 @@
}
@Override
- public synchronized void enableReliabilityTriggering() {
- if (!mReliabilityReceiverEnabled) {
- // The intent filter that exists to make updates reliable in the event of failures /
- // reboots.
- IntentFilter reliabilityIntentFilter = new IntentFilter();
- reliabilityIntentFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START);
- mContext.registerReceiver(mReliabilityReceiver, reliabilityIntentFilter);
- mReliabilityReceiverEnabled = true;
- }
+ public synchronized void scheduleReliabilityTrigger(long minimumDelayMillis) {
+ TimeZoneUpdateIdler.schedule(mContext, minimumDelayMillis);
}
@Override
- public synchronized void disableReliabilityTriggering() {
- if (mReliabilityReceiverEnabled) {
- mContext.unregisterReceiver(mReliabilityReceiver);
- mReliabilityReceiverEnabled = false;
- }
+ public synchronized void unscheduleReliabilityTrigger() {
+ TimeZoneUpdateIdler.unschedule(mContext);
}
private static class Receiver extends BroadcastReceiver {
- private final Listener mListener;
- private final boolean mPackageUpdated;
+ private final PackageTracker mPackageTracker;
- private Receiver(Listener listener, boolean packageUpdated) {
- mListener = listener;
- mPackageUpdated = packageUpdated;
+ private Receiver(PackageTracker packageTracker) {
+ mPackageTracker = packageTracker;
}
@Override
public void onReceive(Context context, Intent intent) {
Slog.d(TAG, "Received intent: " + intent.toString());
- mListener.triggerUpdateIfNeeded(mPackageUpdated);
+ mPackageTracker.triggerUpdateIfNeeded(true /* packageChanged */);
}
}
-
}
diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java
index 24e0fe4..f0306b9 100644
--- a/services/core/java/com/android/server/timezone/PackageTracker.java
+++ b/services/core/java/com/android/server/timezone/PackageTracker.java
@@ -51,7 +51,7 @@
*/
// Also made non-final so it can be mocked.
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public class PackageTracker implements IntentHelper.Listener {
+public class PackageTracker {
private static final String TAG = "timezone.PackageTracker";
private final PackageManagerHelper mPackageManagerHelper;
@@ -72,6 +72,13 @@
// The number of failed checks in a row before reliability checks should stop happening.
private long mFailedCheckRetryCount;
+ /*
+ * The minimum delay between a successive reliability triggers / other operations. Should to be
+ * larger than mCheckTimeAllowedMillis to avoid reliability triggers happening during package
+ * update checks.
+ */
+ private int mDelayBeforeReliabilityCheckMillis;
+
// Reliability check state: If a check was triggered but not acknowledged within
// mCheckTimeAllowedMillis then another one can be triggered.
private Long mLastTriggerTimestamp = null;
@@ -122,6 +129,7 @@
mDataAppPackageName = mConfigHelper.getDataAppPackageName();
mCheckTimeAllowedMillis = mConfigHelper.getCheckTimeAllowedMillis();
mFailedCheckRetryCount = mConfigHelper.getFailedCheckRetryCount();
+ mDelayBeforeReliabilityCheckMillis = mCheckTimeAllowedMillis + (60 * 1000);
// Validate the device configuration including the application packages.
// The manifest entries in the apps themselves are not validated until use as they can
@@ -135,9 +143,10 @@
// Initialize the intent helper.
mIntentHelper.initialize(mUpdateAppPackageName, mDataAppPackageName, this);
- // Enable the reliability triggering so we will have at least one reliability trigger if
- // a package isn't updated.
- mIntentHelper.enableReliabilityTriggering();
+ // Schedule a reliability trigger so we will have at least one after boot. This will allow
+ // us to catch if a package updated wasn't handled to completion. There's no hurry: it's ok
+ // to delay for a while before doing this even if idle.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
Slog.i(TAG, "Time zone updater / data package tracking enabled");
}
@@ -195,7 +204,6 @@
* @param packageChanged true if this method was called because a known packaged definitely
* changed, false if the cause is a reliability trigger
*/
- @Override
public synchronized void triggerUpdateIfNeeded(boolean packageChanged) {
if (!mTrackingEnabled) {
throw new IllegalStateException("Unexpected call. Tracking is disabled.");
@@ -212,8 +220,8 @@
+ " updaterApp=" + updaterAppManifestValid
+ ", dataApp=" + dataAppManifestValid);
- // There's no point in doing reliability checks if the current packages are bad.
- mIntentHelper.disableReliabilityTriggering();
+ // There's no point in doing any reliability triggers if the current packages are bad.
+ mIntentHelper.unscheduleReliabilityTrigger();
return;
}
@@ -238,7 +246,8 @@
Slog.d(TAG,
"triggerUpdateIfNeeded: checkComplete call is not yet overdue."
+ " Not triggering.");
- // Not doing any work, but also not disabling future reliability triggers.
+ // Don't do any work now but we do schedule a future reliability trigger.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
return;
}
} else if (mCheckFailureCount > mFailedCheckRetryCount) {
@@ -247,13 +256,13 @@
Slog.i(TAG, "triggerUpdateIfNeeded: number of allowed consecutive check failures"
+ " exceeded. Stopping reliability triggers until next reboot or package"
+ " update.");
- mIntentHelper.disableReliabilityTriggering();
+ mIntentHelper.unscheduleReliabilityTrigger();
return;
} else if (mCheckFailureCount == 0) {
// Case 4.
Slog.i(TAG, "triggerUpdateIfNeeded: No reliability check required. Last check was"
+ " successful.");
- mIntentHelper.disableReliabilityTriggering();
+ mIntentHelper.unscheduleReliabilityTrigger();
return;
}
}
@@ -263,7 +272,7 @@
if (currentInstalledVersions == null) {
// This should not happen if the device is configured in a valid way.
Slog.e(TAG, "triggerUpdateIfNeeded: currentInstalledVersions was null");
- mIntentHelper.disableReliabilityTriggering();
+ mIntentHelper.unscheduleReliabilityTrigger();
return;
}
@@ -288,7 +297,7 @@
// The last check succeeded and nothing has changed. Do nothing and disable
// reliability checks.
Slog.i(TAG, "triggerUpdateIfNeeded: Prior check succeeded. No need to trigger.");
- mIntentHelper.disableReliabilityTriggering();
+ mIntentHelper.unscheduleReliabilityTrigger();
return;
}
}
@@ -299,6 +308,8 @@
if (checkToken == null) {
Slog.w(TAG, "triggerUpdateIfNeeded: Unable to generate check token."
+ " Not sending check request.");
+ // Trigger again later: perhaps we'll have better luck.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
return;
}
@@ -309,9 +320,9 @@
// Update the reliability check state in case the update fails.
setCheckInProgress();
- // Enable reliability triggering in case the check doesn't succeed and there is no
- // response at all. Enabling reliability triggering is idempotent.
- mIntentHelper.enableReliabilityTriggering();
+ // Schedule a reliability trigger in case the update check doesn't succeed and there is no
+ // response at all. It will be cancelled if the check is successful in recordCheckResult.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
}
/**
@@ -370,9 +381,9 @@
+ " storage state.");
mPackageStatusStorage.resetCheckState();
- // Enable reliability triggering and reset the failure count so we know that the
+ // Schedule a reliability trigger and reset the failure count so we know that the
// next reliability trigger will do something.
- mIntentHelper.enableReliabilityTriggering();
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
mCheckFailureCount = 0;
} else {
// This is the expected case when tracking is enabled: a check was triggered and it has
@@ -385,13 +396,13 @@
setCheckComplete();
if (success) {
- // Since the check was successful, no more reliability checks are required until
+ // Since the check was successful, no reliability trigger is required until
// there is a package change.
- mIntentHelper.disableReliabilityTriggering();
+ mIntentHelper.unscheduleReliabilityTrigger();
mCheckFailureCount = 0;
} else {
- // Enable reliability triggering to potentially check again in future.
- mIntentHelper.enableReliabilityTriggering();
+ // Enable schedule a reliability trigger to check again in future.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
mCheckFailureCount++;
}
} else {
@@ -400,8 +411,8 @@
Slog.i(TAG, "recordCheckResult: could not update token=" + checkToken
+ " with success=" + success + ". Optimistic lock failure");
- // Enable reliability triggering to potentially try again in future.
- mIntentHelper.enableReliabilityTriggering();
+ // Schedule a reliability trigger to potentially try again in future.
+ mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
mCheckFailureCount++;
}
}
@@ -515,6 +526,7 @@
", mUpdateAppPackageName='" + mUpdateAppPackageName + '\'' +
", mDataAppPackageName='" + mDataAppPackageName + '\'' +
", mCheckTimeAllowedMillis=" + mCheckTimeAllowedMillis +
+ ", mDelayBeforeReliabilityCheckMillis=" + mDelayBeforeReliabilityCheckMillis +
", mFailedCheckRetryCount=" + mFailedCheckRetryCount +
", mLastTriggerTimestamp=" + mLastTriggerTimestamp +
", mCheckTriggered=" + mCheckTriggered +
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 3ad4419..6824a59 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -69,18 +69,22 @@
DistroVersion.CURRENT_FORMAT_MINOR_VERSION);
public static class Lifecycle extends SystemService {
- private RulesManagerService mService;
-
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
- mService = RulesManagerService.create(getContext());
- mService.start();
+ RulesManagerService service = RulesManagerService.create(getContext());
+ service.start();
- publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, mService);
+ // Publish the binder service so it can be accessed from other (appropriately
+ // permissioned) processes.
+ publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, service);
+
+ // Publish the service instance locally so we can use it directly from within the system
+ // server from TimeZoneUpdateIdler.
+ publishLocalService(RulesManagerService.class, service);
}
}
@@ -496,6 +500,16 @@
mPackageTracker.dump(pw);
}
+ /**
+ * Called when the device is considered idle.
+ */
+ void notifyIdle() {
+ // No package has changed: we are just triggering because the device is idle and there
+ // *might* be work to do.
+ final boolean packageChanged = false;
+ mPackageTracker.triggerUpdateIfNeeded(packageChanged);
+ }
+
@Override
public String toString() {
return "RulesManagerService{" +
diff --git a/services/core/java/com/android/server/timezone/TimeZoneUpdateIdler.java b/services/core/java/com/android/server/timezone/TimeZoneUpdateIdler.java
new file mode 100644
index 0000000..a7767a4
--- /dev/null
+++ b/services/core/java/com/android/server/timezone/TimeZoneUpdateIdler.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 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.server.timezone;
+
+import com.android.server.LocalServices;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+/**
+ * A JobService used to trigger time zone rules update work when a device falls idle.
+ */
+public final class TimeZoneUpdateIdler extends JobService {
+
+ private static final String TAG = "timezone.TimeZoneUpdateIdler";
+
+ /** The static job ID used to handle on-idle work. */
+ // Must be unique within UID (system service)
+ private static final int TIME_ZONE_UPDATE_IDLE_JOB_ID = 27042305;
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ RulesManagerService rulesManagerService =
+ LocalServices.getService(RulesManagerService.class);
+
+ Slog.d(TAG, "onStartJob() called");
+
+ // Note: notifyIdle() explicitly handles canceling / re-scheduling so no need to reschedule
+ // here.
+ rulesManagerService.notifyIdle();
+
+ // Everything is handled synchronously. We are done.
+ return false;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ // Reschedule if stopped unless it was cancelled due to unschedule().
+ boolean reschedule = params.getStopReason() != JobParameters.REASON_CANCELED;
+ Slog.d(TAG, "onStopJob() called: Reschedule=" + reschedule);
+ return reschedule;
+ }
+
+ /**
+ * Schedules the TimeZoneUpdateIdler job service to run once.
+ *
+ * @param context Context to use to get a job scheduler.
+ */
+ public static void schedule(Context context, long minimumDelayMillis) {
+ // Request that the JobScheduler tell us when the device falls idle.
+ JobScheduler jobScheduler =
+ (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+ // The TimeZoneUpdateIdler will send an intent that will trigger the Receiver.
+ ComponentName idlerJobServiceName =
+ new ComponentName(context, TimeZoneUpdateIdler.class);
+
+ // We require the device is idle, but also that it is charging to be as non-invasive as
+ // we can.
+ JobInfo.Builder jobInfoBuilder =
+ new JobInfo.Builder(TIME_ZONE_UPDATE_IDLE_JOB_ID, idlerJobServiceName)
+ .setRequiresDeviceIdle(true)
+ .setRequiresCharging(true)
+ .setMinimumLatency(minimumDelayMillis);
+
+ Slog.d(TAG, "schedule() called: minimumDelayMillis=" + minimumDelayMillis);
+ jobScheduler.schedule(jobInfoBuilder.build());
+ }
+
+ /**
+ * Unschedules the TimeZoneUpdateIdler job service.
+ *
+ * @param context Context to use to get a job scheduler.
+ */
+ public static void unschedule(Context context) {
+ JobScheduler jobScheduler =
+ (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ Slog.d(TAG, "unschedule() called");
+ jobScheduler.cancel(TIME_ZONE_UPDATE_IDLE_JOB_ID);
+ }
+}
diff --git a/services/core/java/com/android/server/twilight/TwilightState.java b/services/core/java/com/android/server/twilight/TwilightState.java
index 30a8ccc..71304a7 100644
--- a/services/core/java/com/android/server/twilight/TwilightState.java
+++ b/services/core/java/com/android/server/twilight/TwilightState.java
@@ -18,7 +18,10 @@
import android.text.format.DateFormat;
-import java.util.Calendar;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.TimeZone;
/**
* The twilight state, consisting of the sunrise and sunset times (in millis) for the current
@@ -45,12 +48,11 @@
}
/**
- * Returns a new {@link Calendar} instance initialized to {@link #sunriseTimeMillis()}.
+ * Returns a new {@link LocalDateTime} instance initialized to {@link #sunriseTimeMillis()}.
*/
- public Calendar sunrise() {
- final Calendar sunrise = Calendar.getInstance();
- sunrise.setTimeInMillis(mSunriseTimeMillis);
- return sunrise;
+ public LocalDateTime sunrise() {
+ final ZoneId zoneId = TimeZone.getDefault().toZoneId();
+ return LocalDateTime.ofInstant(Instant.ofEpochMilli(mSunriseTimeMillis), zoneId);
}
/**
@@ -62,12 +64,11 @@
}
/**
- * Returns a new {@link Calendar} instance initialized to {@link #sunsetTimeMillis()}.
+ * Returns a new {@link LocalDateTime} instance initialized to {@link #sunsetTimeMillis()}.
*/
- public Calendar sunset() {
- final Calendar sunset = Calendar.getInstance();
- sunset.setTimeInMillis(mSunsetTimeMillis);
- return sunset;
+ public LocalDateTime sunset() {
+ final ZoneId zoneId = TimeZone.getDefault().toZoneId();
+ return LocalDateTime.ofInstant(Instant.ofEpochMilli(mSunsetTimeMillis), zoneId);
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index ecf9067..6c11d8b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -299,7 +299,7 @@
return new TaskSnapshot(hwBitmap.createGraphicBufferHandle(),
topChild.getConfiguration().orientation, mainWindow.mStableInsets,
- false /* reduced */, 1.0f /* scale */);
+ ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */);
}
/**
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c828acc..4fbd919 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1209,18 +1209,6 @@
traceEnd();
}
- // timezone.RulesManagerService will prevent a device starting up if the chain of trust
- // required for safe time zone updates might be broken. RuleManagerService cannot do
- // this check when mOnlyCore == true, so we don't enable the service in this case.
- final boolean startRulesManagerService =
- !mOnlyCore && context.getResources().getBoolean(
- R.bool.config_enableUpdateableTimeZoneRules);
- if (startRulesManagerService) {
- traceBeginAndSlog("StartTimeZoneRulesManagerService");
- mSystemServiceManager.startService(TIME_ZONE_RULES_MANAGER_SERVICE_CLASS);
- traceEnd();
- }
-
traceBeginAndSlog("StartAudioService");
mSystemServiceManager.startService(AudioService.Lifecycle.class);
traceEnd();
@@ -1361,6 +1349,19 @@
}
traceEnd();
+ // timezone.RulesManagerService will prevent a device starting up if the chain of trust
+ // required for safe time zone updates might be broken. RuleManagerService cannot do
+ // this check when mOnlyCore == true, so we don't enable the service in this case.
+ // This service requires that JobSchedulerService is already started when it starts.
+ final boolean startRulesManagerService =
+ !mOnlyCore && context.getResources().getBoolean(
+ R.bool.config_enableUpdateableTimeZoneRules);
+ if (startRulesManagerService) {
+ traceBeginAndSlog("StartTimeZoneRulesManagerService");
+ mSystemServiceManager.startService(TIME_ZONE_RULES_MANAGER_SERVICE_CLASS);
+ traceEnd();
+ }
+
if (!disableNetwork && !disableNetworkTime) {
traceBeginAndSlog("StartNetworkTimeUpdateService");
try {
diff --git a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
index 58a4456..3a92d63 100644
--- a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
@@ -30,13 +30,14 @@
import android.test.mock.MockContentResolver;
import com.android.internal.app.NightDisplayController;
-import com.android.internal.app.NightDisplayController.LocalTime;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.display.DisplayTransformManager;
import com.android.server.display.NightDisplayService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -45,6 +46,7 @@
import java.util.Calendar;
import java.util.HashMap;
+import java.time.LocalTime;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -926,11 +928,10 @@
*/
private void setActivated(boolean activated, int lastActivatedTimeOffset) {
mNightDisplayController.setActivated(activated);
-
- final Calendar c = Calendar.getInstance();
- c.add(Calendar.MINUTE, lastActivatedTimeOffset);
- Secure.putLongForUser(mContext.getContentResolver(),
- Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, c.getTimeInMillis(), mUserId);
+ Secure.putStringForUser(mContext.getContentResolver(),
+ Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ LocalDateTime.now().plusMinutes(lastActivatedTimeOffset).toString(),
+ mUserId);
}
/**
@@ -969,7 +970,7 @@
private static LocalTime getLocalTimeRelativeToNow(int offsetMinutes) {
final Calendar c = Calendar.getInstance();
c.add(Calendar.MINUTE, offsetMinutes);
- return new LocalTime(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
+ return LocalTime.of(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
}
/**
@@ -984,13 +985,27 @@
final LocalTime sunset = getLocalTimeRelativeToNow(sunsetOffset);
final LocalTime sunrise = getLocalTimeRelativeToNow(sunriseOffset);
- final Calendar now = Calendar.getInstance();
- long sunsetMillis = sunset.getDateTimeBefore(now).getTimeInMillis();
- long sunriseMillis = sunrise.getDateTimeBefore(now).getTimeInMillis();
+ final LocalDateTime now = LocalDateTime.now();
+ final ZoneId zoneId = ZoneId.systemDefault();
+
+ long sunsetMillis = NightDisplayService.getDateTimeBefore(sunset, now)
+ .atZone(zoneId)
+ .toInstant()
+ .toEpochMilli();
+ long sunriseMillis = NightDisplayService.getDateTimeBefore(sunrise, now)
+ .atZone(zoneId)
+ .toInstant()
+ .toEpochMilli();
if (sunsetMillis < sunriseMillis) {
- sunsetMillis = sunset.getDateTimeAfter(now).getTimeInMillis();
+ sunsetMillis = NightDisplayService.getDateTimeAfter(sunset, now)
+ .atZone(zoneId)
+ .toInstant()
+ .toEpochMilli();
} else {
- sunriseMillis = sunrise.getDateTimeAfter(now).getTimeInMillis();
+ sunriseMillis = NightDisplayService.getDateTimeAfter(sunrise, now)
+ .atZone(zoneId)
+ .toInstant()
+ .toEpochMilli();
}
return new TwilightState(sunriseMillis, sunsetMillis);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 2252c85..e51f7a9 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -20,15 +20,19 @@
import static android.view.WindowManagerPolicy.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicy.NAV_BAR_RIGHT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -46,6 +50,8 @@
private final ComponentName testActivityComponent =
ComponentName.unflattenFromString("com.foo/.BarActivity");
+ private final ComponentName secondaryActivityComponent =
+ ComponentName.unflattenFromString("com.foo/.BarActivity2");
@Test
public void testStackCleanupOnClearingTask() throws Exception {
final ActivityManagerService service = createActivityManagerService();
@@ -131,4 +137,45 @@
record.ensureActivityConfigurationLocked(0 /* globalChanges */, false /* preserveWindow */);
assertEquals(expectedActivityBounds, record.getBounds());
}
+
+
+ @Test
+ public void testCanBeLaunchedOnDisplay() throws Exception {
+ testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
+ true /*activityResizeable*/, true /*expected*/);
+
+ testSupportsLaunchingResizeable(false /*taskPresent*/, true /*taskResizeable*/,
+ false /*activityResizeable*/, false /*expected*/);
+
+ testSupportsLaunchingResizeable(true /*taskPresent*/, false /*taskResizeable*/,
+ true /*activityResizeable*/, false /*expected*/);
+
+ testSupportsLaunchingResizeable(true /*taskPresent*/, true /*taskResizeable*/,
+ false /*activityResizeable*/, true /*expected*/);
+ }
+
+ private void testSupportsLaunchingResizeable(boolean taskPresent, boolean taskResizeable,
+ boolean activityResizeable, boolean expected) {
+ final ActivityManagerService service = createActivityManagerService();
+ service.mSupportsMultiWindow = true;
+
+
+ final TaskRecord task = taskPresent
+ ? createTask(service, testActivityComponent, TEST_STACK_ID) : null;
+
+ if (task != null) {
+ task.setResizeMode(taskResizeable ? ActivityInfo.RESIZE_MODE_RESIZEABLE
+ : ActivityInfo.RESIZE_MODE_UNRESIZEABLE);
+ }
+
+ final ActivityRecord record = createActivity(service, secondaryActivityComponent,
+ task);
+ record.info.resizeMode = activityResizeable ? ActivityInfo.RESIZE_MODE_RESIZEABLE
+ : ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+
+ record.canBeLaunchedOnDisplay(Display.DEFAULT_DISPLAY);
+
+ assertEquals(((TestActivityStackSupervisor) service.mStackSupervisor)
+ .getLastResizeableFromCanPlaceEntityOnDisplay(), expected);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index a6c0cf1..04ddae9 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -163,6 +163,7 @@
*/
protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
private final ActivityDisplay mDisplay;
+ private boolean mLastResizeable;
public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
super(service, looper);
@@ -170,6 +171,22 @@
mDisplay = new ActivityDisplay();
}
+ // TODO: Use Mockito spy instead. Currently not possible due to TestActivityStackSupervisor
+ // access to ActivityDisplay
+ @Override
+ boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable, int callingPid,
+ int callingUid, ActivityInfo activityInfo) {
+ mLastResizeable = resizeable;
+ return super.canPlaceEntityOnDisplay(displayId, resizeable, callingPid, callingUid,
+ activityInfo);
+ }
+
+ // TODO: remove and use Mockito verify once {@link #canPlaceEntityOnDisplay} override is
+ // removed.
+ public boolean getLastResizeableFromCanPlaceEntityOnDisplay() {
+ return mLastResizeable;
+ }
+
// No home stack is set.
@Override
void moveHomeStackToFront(String reason) {
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index d12c07a..7c31e80 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -32,6 +32,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
+import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
+import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.eq;
@@ -92,4 +96,33 @@
int reason = mService.getLastShutdownReasonInternal(mTempReason);
assertThat(reason).isEqualTo(PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN);
}
+
+ @SmallTest
+ public void testGetDesiredScreenPolicy_WithVR() throws Exception {
+ // Brighten up the screen
+ mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
+ assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ DisplayPowerRequest.POLICY_BRIGHT);
+
+ // Move to VR
+ mService.setVrModeEnabled(true);
+ assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ DisplayPowerRequest.POLICY_VR);
+
+ // Then take a nap
+ mService.setWakefulnessLocked(WAKEFULNESS_ASLEEP, 0);
+ assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ DisplayPowerRequest.POLICY_OFF);
+
+ // Wake up to VR
+ mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
+ assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ DisplayPowerRequest.POLICY_VR);
+
+ // And back to normal
+ mService.setVrModeEnabled(false);
+ assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ DisplayPowerRequest.POLICY_BRIGHT);
+
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
index 38142d3..7d73e82 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
@@ -107,7 +107,7 @@
mFakeIntentHelper.assertNotInitialized();
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
}
@Test
@@ -119,7 +119,7 @@
mPackageTracker.start();
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
try {
// This call should also not be allowed and will throw an exception if tracking is
@@ -129,7 +129,7 @@
} catch (IllegalStateException expected) {}
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
}
@Test
@@ -141,14 +141,14 @@
mPackageTracker.start();
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
// Receiving a check result when tracking is disabled should cause the storage to be
// reset.
mPackageTracker.recordCheckResult(null /* checkToken */, true /* success */);
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
// Assert the storage was reset.
checkPackageStorageStatusIsInitialOrReset();
@@ -166,13 +166,13 @@
mPackageTracker.start();
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
// Receiving a check result when tracking is disabled should cause the storage to be reset.
mPackageTracker.recordCheckResult(createArbitraryCheckToken(), true /* success */);
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
// Assert the storage was reset.
checkPackageStorageStatusIsInitialOrReset();
@@ -195,7 +195,7 @@
mFakeIntentHelper.assertNotInitialized();
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
}
@Test
@@ -215,7 +215,7 @@
mFakeIntentHelper.assertNotInitialized();
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
}
@Test
@@ -235,7 +235,7 @@
mFakeIntentHelper.assertNotInitialized();
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
}
@Test
@@ -255,7 +255,7 @@
mFakeIntentHelper.assertNotInitialized();
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
}
@Test
@@ -289,7 +289,7 @@
mFakeIntentHelper.assertUpdateNotTriggered();
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
// Assert the storage was not touched.
checkPackageStorageStatusIsInitialOrReset();
@@ -325,7 +325,7 @@
mFakeIntentHelper.assertUpdateNotTriggered();
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
// Assert the storage was not touched.
checkPackageStorageStatusIsInitialOrReset();
@@ -416,7 +416,7 @@
mPackageTracker.recordCheckResult(null /* checkToken */, success);
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ mFakeIntentHelper.assertReliabilityTriggerScheduled();
// Assert the storage was reset.
checkPackageStorageStatusIsInitialOrReset();
@@ -627,7 +627,7 @@
mPackageTracker.recordCheckResult(token1, true /* success */);
// Reliability triggering should still be enabled.
- mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ mFakeIntentHelper.assertReliabilityTriggerScheduled();
// Check the expected storage state.
checkPackageStorageStatus(PackageStatus.CHECK_STARTED, packageVersions2);
@@ -743,7 +743,7 @@
// Under the covers we expect it to fail to update because the storage should recognize that
// the token is no longer valid.
- mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ mFakeIntentHelper.assertReliabilityTriggerScheduled();
// Peek inside the package tracker to make sure it is tracking failure counts properly.
assertEquals(1, mPackageTracker.getCheckFailureCountForTests());
@@ -766,7 +766,7 @@
checkPackageStorageStatusIsInitialOrReset();
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Assert the PackageTracker did trigger an update.
checkUpdateCheckTriggered(packageVersions);
@@ -803,7 +803,7 @@
checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions);
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Assert the PackageTracker did not attempt to trigger an update.
mFakeIntentHelper.assertUpdateNotTriggered();
@@ -843,7 +843,7 @@
checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Assert the PackageTracker did trigger an update.
checkUpdateCheckTriggered(currentPackageVersions);
@@ -890,7 +890,7 @@
for (int i = 0; i < retriesAllowed + 1; i++) {
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Assert the PackageTracker did trigger an update.
checkUpdateCheckTriggered(currentPackageVersions);
@@ -912,9 +912,9 @@
// Check reliability triggering is in the correct state.
if (i <= retriesAllowed) {
- mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ mFakeIntentHelper.assertReliabilityTriggerScheduled();
} else {
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
}
}
}
@@ -950,7 +950,7 @@
// Fail (retries - 1) times.
for (int i = 0; i < retriesAllowed - 1; i++) {
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Assert the PackageTracker did trigger an update.
checkUpdateCheckTriggered(currentPackageVersions);
@@ -971,11 +971,11 @@
currentPackageVersions);
// Check reliability triggering is still enabled.
- mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ mFakeIntentHelper.assertReliabilityTriggerScheduled();
}
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Assert the PackageTracker did trigger an update.
checkUpdateCheckTriggered(currentPackageVersions);
@@ -1023,7 +1023,7 @@
checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, oldPackageVersions);
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Assert the PackageTracker did trigger an update.
checkUpdateCheckTriggered(currentPackageVersions);
@@ -1033,18 +1033,18 @@
mFakeClock.incrementClock(checkDelayMillis - 1);
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Assert the PackageTracker did not trigger an update.
mFakeIntentHelper.assertUpdateNotTriggered();
checkPackageStorageStatus(PackageStatus.CHECK_STARTED, currentPackageVersions);
- mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ mFakeIntentHelper.assertReliabilityTriggerScheduled();
// Increment the clock slightly more. Should now consider the response overdue.
mFakeClock.incrementClock(2);
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Triggering should have happened.
checkUpdateCheckTriggered(currentPackageVersions);
@@ -1096,7 +1096,7 @@
mFakeClock.incrementClock(checkDelayMillis + 1);
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Assert the PackageTracker triggered an update.
checkUpdateCheckTriggered(newPackageVersions);
@@ -1154,18 +1154,18 @@
mFakeClock.incrementClock(checkDelayMillis - 1);
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Assert the PackageTracker did not trigger an update.
mFakeIntentHelper.assertUpdateNotTriggered();
checkPackageStorageStatus(PackageStatus.CHECK_STARTED, newPackageVersions);
- mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ mFakeIntentHelper.assertReliabilityTriggerScheduled();
// Increment the clock slightly more. Should now consider the response overdue.
mFakeClock.incrementClock(2);
// Simulate a reliability trigger.
- mFakeIntentHelper.simulateReliabilityTrigger();
+ mPackageTracker.triggerUpdateIfNeeded(false /* packageChanged */);
// Triggering should have happened.
checkUpdateCheckTriggered(newPackageVersions);
@@ -1202,7 +1202,7 @@
// If an update check was triggered reliability triggering should always be enabled to
// ensure that it can be completed if it fails.
- mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ mFakeIntentHelper.assertReliabilityTriggerScheduled();
// Check the expected storage state.
checkPackageStorageStatus(PackageStatus.CHECK_STARTED, packageVersions);
@@ -1210,7 +1210,7 @@
private void checkUpdateCheckFailed(PackageVersions packageVersions) {
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ mFakeIntentHelper.assertReliabilityTriggerScheduled();
// Assert the storage was updated.
checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_FAILURE, packageVersions);
@@ -1218,7 +1218,7 @@
private void checkUpdateCheckSuccessful(PackageVersions packageVersions) {
// Check reliability triggering state.
- mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+ mFakeIntentHelper.assertReliabilityTriggerNotScheduled();
// Assert the storage was updated.
checkPackageStorageStatus(PackageStatus.CHECK_COMPLETED_SUCCESS, packageVersions);
@@ -1345,7 +1345,7 @@
mFakeIntentHelper.assertInitialized(UPDATE_APP_PACKAGE_NAME, DATA_APP_PACKAGE_NAME);
// Assert that reliability tracking is always enabled after initialization.
- mFakeIntentHelper.assertReliabilityTriggeringEnabled();
+ mFakeIntentHelper.assertReliabilityTriggerScheduled();
}
private void checkPackageStorageStatus(
@@ -1368,34 +1368,34 @@
*/
private static class FakeIntentHelper implements IntentHelper {
- private Listener mListener;
+ private PackageTracker mPackageTracker;
private String mUpdateAppPackageName;
private String mDataAppPackageName;
private CheckToken mLastToken;
- private boolean mReliabilityTriggeringEnabled;
+ private boolean mReliabilityTriggerScheduled;
@Override
public void initialize(String updateAppPackageName, String dataAppPackageName,
- Listener listener) {
+ PackageTracker packageTracker) {
assertNotNull(updateAppPackageName);
assertNotNull(dataAppPackageName);
- assertNotNull(listener);
- mListener = listener;
+ assertNotNull(packageTracker);
+ mPackageTracker = packageTracker;
mUpdateAppPackageName = updateAppPackageName;
mDataAppPackageName = dataAppPackageName;
}
public void assertInitialized(
String expectedUpdateAppPackageName, String expectedDataAppPackageName) {
- assertNotNull(mListener);
+ assertNotNull(mPackageTracker);
assertEquals(expectedUpdateAppPackageName, mUpdateAppPackageName);
assertEquals(expectedDataAppPackageName, mDataAppPackageName);
}
public void assertNotInitialized() {
- assertNull(mListener);
+ assertNull(mPackageTracker);
}
@Override
@@ -1407,21 +1407,21 @@
}
@Override
- public void enableReliabilityTriggering() {
- mReliabilityTriggeringEnabled = true;
+ public void scheduleReliabilityTrigger(long minimumDelayMillis) {
+ mReliabilityTriggerScheduled = true;
}
@Override
- public void disableReliabilityTriggering() {
- mReliabilityTriggeringEnabled = false;
+ public void unscheduleReliabilityTrigger() {
+ mReliabilityTriggerScheduled = false;
}
- public void assertReliabilityTriggeringEnabled() {
- assertTrue(mReliabilityTriggeringEnabled);
+ public void assertReliabilityTriggerScheduled() {
+ assertTrue(mReliabilityTriggerScheduled);
}
- public void assertReliabilityTriggeringDisabled() {
- assertFalse(mReliabilityTriggeringEnabled);
+ public void assertReliabilityTriggerNotScheduled() {
+ assertFalse(mReliabilityTriggerScheduled);
}
public void assertUpdateTriggered() {
@@ -1440,11 +1440,7 @@
}
public void simulatePackageUpdatedEvent() {
- mListener.triggerUpdateIfNeeded(true);
- }
-
- public void simulateReliabilityTrigger() {
- mListener.triggerUpdateIfNeeded(false);
+ mPackageTracker.triggerUpdateIfNeeded(true /* packageChanged */);
}
}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index eff04ab..2624176 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
+import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
import static com.android.server.connectivity.MetricsTestUtil.aBool;
import static com.android.server.connectivity.MetricsTestUtil.aByteArray;
import static com.android.server.connectivity.MetricsTestUtil.aLong;
@@ -31,29 +33,41 @@
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ETHERNET;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.MULTIPLE;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.WIFI;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
+import android.net.metrics.ConnectStats;
import android.net.metrics.DefaultNetworkEvent;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.DnsEvent;
+import android.net.metrics.DnsEvent;
import android.net.metrics.IpManagerEvent;
import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.NetworkEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
+import android.net.metrics.WakeupStats;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+
import java.util.Arrays;
import java.util.List;
-import junit.framework.TestCase;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
-public class IpConnectivityEventBuilderTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpConnectivityEventBuilderTest {
- @SmallTest
+ @Test
public void testLinkLayerInferrence() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(IpReachabilityEvent.class),
@@ -182,7 +196,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testDefaultNetworkEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(DefaultNetworkEvent.class),
@@ -223,7 +237,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testDhcpClientEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(DhcpClientEvent.class),
@@ -249,7 +263,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testDhcpErrorEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(DhcpErrorEvent.class),
@@ -274,7 +288,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testIpManagerEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(IpManagerEvent.class),
@@ -300,7 +314,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testIpReachabilityEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(IpReachabilityEvent.class),
@@ -324,7 +338,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testNetworkEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(NetworkEvent.class),
@@ -353,7 +367,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testValidationProbeEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(ValidationProbeEvent.class),
@@ -380,7 +394,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testApfProgramEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(ApfProgramEvent.class),
@@ -414,7 +428,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testApfStatsSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(ApfStats.class),
@@ -457,7 +471,7 @@
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testRaEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(RaEvent.class),
@@ -490,11 +504,49 @@
verifySerialization(want, ev);
}
+ @Test
+ public void testWakeupStatsSerialization() {
+ WakeupStats stats = new WakeupStats("wlan0");
+ stats.totalWakeups = 14;
+ stats.applicationWakeups = 5;
+ stats.nonApplicationWakeups = 1;
+ stats.rootWakeups = 2;
+ stats.systemWakeups = 3;
+ stats.noUidWakeups = 3;
+
+ IpConnectivityEvent got = IpConnectivityEventBuilder.toProto(stats);
+ String want = String.join("\n",
+ "dropped_events: 0",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 5",
+ " duration_sec: 0",
+ " no_uid_wakeups: 3",
+ " non_application_wakeups: 1",
+ " root_wakeups: 2",
+ " system_wakeups: 3",
+ " total_wakeups: 14",
+ " >",
+ ">",
+ "version: 2\n");
+
+ verifySerialization(want, got);
+ }
+
static void verifySerialization(String want, ConnectivityMetricsEvent... input) {
+ List<IpConnectivityEvent> protoInput =
+ IpConnectivityEventBuilder.toProto(Arrays.asList(input));
+ verifySerialization(want, protoInput.toArray(new IpConnectivityEvent[0]));
+ }
+
+ static void verifySerialization(String want, IpConnectivityEvent... input) {
try {
- List<IpConnectivityEvent> proto =
- IpConnectivityEventBuilder.toProto(Arrays.asList(input));
- byte[] got = IpConnectivityEventBuilder.serialize(0, proto);
+ byte[] got = IpConnectivityEventBuilder.serialize(0, Arrays.asList(input));
IpConnectivityLog log = IpConnectivityLog.parseFrom(got);
assertEquals(want, log.toString());
} catch (Exception e) {
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index cc18b7f..a395c48 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -224,6 +224,15 @@
dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34);
+ // iface, uid
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10123);
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10008);
+ wakeupEvent("wlan0", -1);
+ wakeupEvent("wlan0", 10008);
+ wakeupEvent("rmnet0", 1000);
+
String want = String.join("\n",
"dropped_events: 0",
"events <",
@@ -405,6 +414,38 @@
" return_codes: 0",
" >",
">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 2",
+ " duration_sec: 0",
+ " no_uid_wakeups: 0",
+ " non_application_wakeups: 0",
+ " root_wakeups: 0",
+ " system_wakeups: 1",
+ " total_wakeups: 3",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 1",
+ " duration_sec: 0",
+ " no_uid_wakeups: 1",
+ " non_application_wakeups: 0",
+ " root_wakeups: 0",
+ " system_wakeups: 2",
+ " total_wakeups: 4",
+ " >",
+ ">",
"version: 2\n");
verifySerialization(want, getdump("flush"));
@@ -425,6 +466,11 @@
mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
}
+ void wakeupEvent(String iface, int uid) throws Exception {
+ String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
+ mNetdListener.onWakeupEvent(prefix, uid, uid, 0);
+ }
+
List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
ArgumentCaptor<ConnectivityMetricsEvent> captor =
ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 46f395e..6723601 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -19,6 +19,7 @@
import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -37,9 +38,11 @@
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Base64;
+
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.DNSLookupBatch;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
+
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -47,6 +50,7 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -75,6 +79,118 @@
}
@Test
+ public void testWakeupEventLogging() throws Exception {
+ final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
+
+ // Assert no events
+ String[] events1 = listNetdEvent();
+ assertEquals(new String[]{""}, events1);
+
+ long now = System.currentTimeMillis();
+ String prefix = "iface:wlan0";
+ int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
+ for (int uid : uids) {
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
+ }
+
+ String[] events2 = listNetdEvent();
+ int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
+ assertEquals(expectedLength2, events2.length);
+ assertContains(events2[0], "WakeupStats");
+ assertContains(events2[0], "wlan0");
+ for (int i = 0; i < uids.length; i++) {
+ String got = events2[i+1];
+ assertContains(got, "WakeupEvent");
+ assertContains(got, "wlan0");
+ assertContains(got, "uid: " + uids[i]);
+ }
+
+ int uid = 20000;
+ for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
+ long ts = now + 10;
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
+ }
+
+ String[] events3 = listNetdEvent();
+ int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
+ assertEquals(expectedLength3, events3.length);
+ assertContains(events2[0], "WakeupStats");
+ assertContains(events2[0], "wlan0");
+ for (int i = 1; i < expectedLength3; i++) {
+ String got = events3[i];
+ assertContains(got, "WakeupEvent");
+ assertContains(got, "wlan0");
+ assertContains(got, "uid: " + uid);
+ }
+
+ uid = 45678;
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
+
+ String[] events4 = listNetdEvent();
+ String lastEvent = events4[events4.length - 1];
+ assertContains(lastEvent, "WakeupEvent");
+ assertContains(lastEvent, "wlan0");
+ assertContains(lastEvent, "uid: " + uid);
+ }
+
+ @Test
+ public void testWakeupStatsLogging() throws Exception {
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10123);
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10008);
+ wakeupEvent("wlan0", -1);
+ wakeupEvent("wlan0", 10008);
+ wakeupEvent("rmnet0", 1000);
+ wakeupEvent("wlan0", 10004);
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("wlan0", 0);
+ wakeupEvent("wlan0", -1);
+ wakeupEvent("rmnet0", 10052);
+ wakeupEvent("wlan0", 0);
+ wakeupEvent("rmnet0", 1000);
+ wakeupEvent("wlan0", 1010);
+
+ String got = flushStatistics();
+ String want = String.join("\n",
+ "dropped_events: 0",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 3",
+ " duration_sec: 0",
+ " no_uid_wakeups: 0",
+ " non_application_wakeups: 0",
+ " root_wakeups: 0",
+ " system_wakeups: 2",
+ " total_wakeups: 5",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 2",
+ " duration_sec: 0",
+ " no_uid_wakeups: 2",
+ " non_application_wakeups: 1",
+ " root_wakeups: 2",
+ " system_wakeups: 3",
+ " total_wakeups: 10",
+ " >",
+ ">",
+ "version: 2\n");
+ assertEquals(want, got);
+ }
+
+ @Test
public void testDnsLogging() throws Exception {
asyncDump(100);
@@ -297,6 +413,11 @@
mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
}
+ void wakeupEvent(String iface, int uid) throws Exception {
+ String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, 0);
+ }
+
void asyncDump(long durationMs) throws Exception {
final long stop = System.currentTimeMillis() + durationMs;
final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
@@ -329,4 +450,15 @@
}
return log.toString();
}
+
+ String[] listNetdEvent() throws Exception {
+ StringWriter buffer = new StringWriter();
+ PrintWriter writer = new PrintWriter(buffer);
+ mNetdEventListenerService.list(writer);
+ return buffer.toString().split("\\n");
+ }
+
+ static void assertContains(String got, String want) {
+ assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
+ }
}