Merge "Properly clean up broadcast-receiver ANR" into oc-mr1-dev
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index ca5fa6b..93338bb 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1096,12 +1096,12 @@
}
/** @hide */
- public void dump(Printer pw, String prefix, int flags) {
+ public void dump(Printer pw, String prefix, int dumpFlags) {
super.dumpFront(pw, prefix);
if (permission != null) {
pw.println(prefix + "permission=" + permission);
}
- if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
pw.println(prefix + "taskAffinity=" + taskAffinity
+ " targetActivity=" + targetActivity
+ " persistableMode=" + persistableModeToString());
@@ -1120,7 +1120,7 @@
if (uiOptions != 0) {
pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
}
- if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
pw.println(prefix + "lockTaskLaunchMode="
+ lockTaskLaunchModeToString(lockTaskLaunchMode));
}
@@ -1136,7 +1136,7 @@
if (maxAspectRatio != 0) {
pw.println(prefix + "maxAspectRatio=" + maxAspectRatio);
}
- super.dumpBack(pw, prefix, flags);
+ super.dumpBack(pw, prefix, dumpFlags);
}
public String toString() {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2aa3d09..ad7a5ab 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1046,22 +1046,22 @@
}
/** @hide */
- public void dump(Printer pw, String prefix, int flags) {
+ public void dump(Printer pw, String prefix, int dumpFlags) {
super.dumpFront(pw, prefix);
- if ((flags&DUMP_FLAG_DETAILS) != 0 && className != null) {
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && className != null) {
pw.println(prefix + "className=" + className);
}
if (permission != null) {
pw.println(prefix + "permission=" + permission);
}
pw.println(prefix + "processName=" + processName);
- if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
pw.println(prefix + "taskAffinity=" + taskAffinity);
}
pw.println(prefix + "uid=" + uid + " flags=0x" + Integer.toHexString(flags)
+ " privateFlags=0x" + Integer.toHexString(privateFlags)
+ " theme=0x" + Integer.toHexString(theme));
- if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
pw.println(prefix + "requiresSmallestWidthDp=" + requiresSmallestWidthDp
+ " compatibleWidthLimitDp=" + compatibleWidthLimitDp
+ " largestWidthLimitDp=" + largestWidthLimitDp);
@@ -1080,12 +1080,12 @@
if (resourceDirs != null) {
pw.println(prefix + "resourceDirs=" + Arrays.toString(resourceDirs));
}
- if ((flags&DUMP_FLAG_DETAILS) != 0 && seInfo != null) {
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0 && seInfo != null) {
pw.println(prefix + "seinfo=" + seInfo);
pw.println(prefix + "seinfoUser=" + seInfoUser);
}
pw.println(prefix + "dataDir=" + dataDir);
- if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
pw.println(prefix + "deviceProtectedDataDir=" + deviceProtectedDataDir);
pw.println(prefix + "credentialProtectedDataDir=" + credentialProtectedDataDir);
if (sharedLibraryFiles != null) {
@@ -1104,7 +1104,7 @@
+ " targetSdkVersion=" + targetSdkVersion
+ " versionCode=" + versionCode
+ " targetSandboxVersion=" + targetSandboxVersion);
- if ((flags&DUMP_FLAG_DETAILS) != 0) {
+ if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
if (manageSpaceActivityName != null) {
pw.println(prefix + "manageSpaceActivityName=" + manageSpaceActivityName);
}
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index 53be953..6b1222f 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -183,12 +183,12 @@
protected void dumpBack(Printer pw, String prefix) {
dumpBack(pw, prefix, DUMP_FLAG_ALL);
}
-
- void dumpBack(Printer pw, String prefix, int flags) {
- if ((flags&DUMP_FLAG_APPLICATION) != 0) {
+
+ void dumpBack(Printer pw, String prefix, int dumpFlags) {
+ if ((dumpFlags & DUMP_FLAG_APPLICATION) != 0) {
if (applicationInfo != null) {
pw.println(prefix + "ApplicationInfo:");
- applicationInfo.dump(pw, prefix + " ", flags);
+ applicationInfo.dump(pw, prefix + " ", dumpFlags);
} else {
pw.println(prefix + "ApplicationInfo: null");
}
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index 91dc06e..379b783 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -125,11 +125,11 @@
}
/** @hide */
- public void dump(Printer pw, String prefix, int flags) {
+ public void dump(Printer pw, String prefix, int dumpFlags) {
super.dumpFront(pw, prefix);
pw.println(prefix + "authority=" + authority);
pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
- super.dumpBack(pw, prefix, flags);
+ super.dumpBack(pw, prefix, dumpFlags);
}
public int describeContents() {
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index f312204..7993167 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -282,7 +282,7 @@
}
/** @hide */
- public void dump(Printer pw, String prefix, int flags) {
+ public void dump(Printer pw, String prefix, int dumpFlags) {
if (filter != null) {
pw.println(prefix + "Filter:");
filter.dump(pw, prefix + " ");
@@ -302,13 +302,13 @@
}
if (activityInfo != null) {
pw.println(prefix + "ActivityInfo:");
- activityInfo.dump(pw, prefix + " ", flags);
+ activityInfo.dump(pw, prefix + " ", dumpFlags);
} else if (serviceInfo != null) {
pw.println(prefix + "ServiceInfo:");
- serviceInfo.dump(pw, prefix + " ", flags);
+ serviceInfo.dump(pw, prefix + " ", dumpFlags);
} else if (providerInfo != null) {
pw.println(prefix + "ProviderInfo:");
- providerInfo.dump(pw, prefix + " ", flags);
+ providerInfo.dump(pw, prefix + " ", dumpFlags);
}
}
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index c683ea5..91f884c 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -91,13 +91,13 @@
}
/** @hide */
- void dump(Printer pw, String prefix, int flags) {
+ void dump(Printer pw, String prefix, int dumpFlags) {
super.dumpFront(pw, prefix);
pw.println(prefix + "permission=" + permission);
pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
- super.dumpBack(pw, prefix, flags);
+ super.dumpBack(pw, prefix, dumpFlags);
}
-
+
public String toString() {
return "ServiceInfo{"
+ Integer.toHexString(System.identityHashCode(this))
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 59fb269..df0e262 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -79,8 +79,9 @@
private static final int EVENT_DB_CORRUPT = 75004;
+ // By default idle connections are not closed
private static final boolean DEBUG_CLOSE_IDLE_CONNECTIONS = SystemProperties
- .getBoolean("persist.debug.sqlite.close_idle_connections", true);
+ .getBoolean("persist.debug.sqlite.close_idle_connections", false);
// Stores reference to all databases opened in the current process.
// (The referent Object is not used at this time.)
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index fe9e8c6..da0ed54 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -149,14 +149,43 @@
* provide a better experience than you could otherwise build using the generic building
* blocks.
*
+ * This will fallback to a generic pattern if one exists and there does not exist a
+ * hardware-specific implementation of the effect.
+ *
* @param effectId The ID of the effect to perform:
- * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}.
+ * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
*
* @return The desired effect.
* @hide
*/
public static VibrationEffect get(int effectId) {
- VibrationEffect effect = new Prebaked(effectId);
+ return get(effectId, true);
+ }
+
+ /**
+ * Get a predefined vibration effect.
+ *
+ * Predefined effects are a set of common vibration effects that should be identical, regardless
+ * of the app they come from, in order to provide a cohesive experience for users across
+ * the entire device. They also may be custom tailored to the device hardware in order to
+ * provide a better experience than you could otherwise build using the generic building
+ * blocks.
+ *
+ * Some effects you may only want to play if there's a hardware specific implementation because
+ * they may, for example, be too disruptive to the user without tuning. The {@code fallback}
+ * parameter allows you to decide whether you want to fallback to the generic implementation or
+ * only play if there's a tuned, hardware specific one available.
+ *
+ * @param effectId The ID of the effect to perform:
+ * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
+ * @param fallback Whether to fallback to a generic pattern if a hardware specific
+ * implementation doesn't exist.
+ *
+ * @return The desired effect.
+ * @hide
+ */
+ public static VibrationEffect get(int effectId, boolean fallback) {
+ VibrationEffect effect = new Prebaked(effectId, fallback);
effect.validate();
return effect;
}
@@ -374,19 +403,29 @@
/** @hide */
public static class Prebaked extends VibrationEffect implements Parcelable {
private int mEffectId;
+ private boolean mFallback;
public Prebaked(Parcel in) {
- this(in.readInt());
+ this(in.readInt(), in.readByte() != 0);
}
- public Prebaked(int effectId) {
+ public Prebaked(int effectId, boolean fallback) {
mEffectId = effectId;
+ mFallback = fallback;
}
public int getId() {
return mEffectId;
}
+ /**
+ * Whether the effect should fall back to a generic pattern if there's no hardware specific
+ * implementation of it.
+ */
+ public boolean shouldFallback() {
+ return mFallback;
+ }
+
@Override
public void validate() {
switch (mEffectId) {
@@ -406,7 +445,7 @@
return false;
}
VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
- return mEffectId == other.mEffectId;
+ return mEffectId == other.mEffectId && mFallback == other.mFallback;
}
@Override
@@ -416,7 +455,7 @@
@Override
public String toString() {
- return "Prebaked{mEffectId=" + mEffectId + "}";
+ return "Prebaked{mEffectId=" + mEffectId + ", mFallback=" + mFallback + "}";
}
@@ -424,6 +463,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_EFFECT);
out.writeInt(mEffectId);
+ out.writeByte((byte) (mFallback ? 1 : 0));
}
public static final Parcelable.Creator<Prebaked> CREATOR =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ada7b2b..86c9246 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9332,6 +9332,25 @@
public static final String ANOMALY_DETECTION_CONSTANTS = "anomaly_detection_constants";
/**
+ * Always on display(AOD) specific settings
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "prox_screen_off_delay=10000,screen_brightness_array=0:1:2:3:4"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * screen_brightness_array (string)
+ * dimming_scrim_array (string)
+ * prox_screen_off_delay (long)
+ * prox_cooldown_trigger (long)
+ * prox_cooldown_period (long)
+ * </pre>
+ * @hide
+ */
+ public static final String ALWAYS_ON_DISPLAY_CONSTANTS = "always_on_display_constants";
+
+ /**
* App standby (app idle) specific settings.
* This is encoded as a key=value list, separated by commas. Ex:
*
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index b6a9a26..3b09c67 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -302,7 +302,7 @@
// TODO: create a dump() method instead
return new StringBuilder(
"FillResponse : [mRequestId=" + mRequestId)
- .append(", datasets=").append(mDatasets)
+ .append(", datasets=").append(mDatasets == null ? "N/A" : mDatasets.getList())
.append(", saveInfo=").append(mSaveInfo)
.append(", clientState=").append(mClientState != null)
.append(", hasPresentation=").append(mPresentation != null)
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 81ab407..f918cad 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1656,14 +1656,13 @@
* Each rule should take one of these:
* <table>
* <tr><th> Rule </th> <th> Example </th> <th> Matches Subdomain</th> </tr>
- * <tr><th> HOSTNAME </th> <th> example.com </th> <th> Yes </th> </tr>
- * <tr><th>.HOSTNAME</th> <th> .example.com </th> <th> No </th> </tr>
- * <tr><th> IPV4_LITERAL </th> <th> 192.168.1.1 </th> <th> No </th></tr>
- * <tr><th> IPV6_LITERAL_WITH_BRACKETS</th><th>[10:20:30:40:50:60:70:80]</th><th>No</th></tr>
+ * <tr><td> HOSTNAME </td> <td> example.com </td> <td> Yes </td> </tr>
+ * <tr><td> .HOSTNAME </td> <td> .example.com </td> <td> No </td> </tr>
+ * <tr><td> IPV4_LITERAL </td> <td> 192.168.1.1 </td> <td> No </td></tr>
+ * <tr><td> IPV6_LITERAL_WITH_BRACKETS </td><td>[10:20:30:40:50:60:70:80]</td><td>No</td></tr>
* </table>
* <p>
* All other rules, including wildcards, are invalid.
- * <p>
*
* @param urls the list of URLs
* @param callback will be called with true if URLs are successfully added to the whitelist.
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 80f4b99..ceb06f5 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1941,7 +1941,8 @@
final int checkedPos = mAdapterView.getCheckedItemPosition();
final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
if (!useLayoutWithDefault()
- && (!hasValidSelection || mLastSelected != checkedPos)) {
+ && (!hasValidSelection || mLastSelected != checkedPos)
+ && mAlwaysButton != null) {
setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
mOnceButton.setEnabled(hasValidSelection);
if (hasValidSelection) {
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 378826d..77cfc2fc 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -337,11 +337,13 @@
final ResolverTarget rhsTarget = mTargetsDict.get(new ComponentName(
rhs.activityInfo.packageName, rhs.activityInfo.name));
- final int selectProbabilityDiff = Float.compare(
+ if (lhsTarget != null && rhsTarget != null) {
+ final int selectProbabilityDiff = Float.compare(
rhsTarget.getSelectProbability(), lhsTarget.getSelectProbability());
- if (selectProbabilityDiff != 0) {
- return selectProbabilityDiff > 0 ? 1 : -1;
+ if (selectProbabilityDiff != 0) {
+ return selectProbabilityDiff > 0 ? 1 : -1;
+ }
}
}
}
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 17a598a..4b80a5f 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -28,7 +28,6 @@
import android.graphics.Point;
import android.net.Uri;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.FileObserver;
@@ -74,7 +73,6 @@
private Handler mHandler;
- private static final String MIMETYPE_PDF = "application/pdf";
private static final String MIMETYPE_JPEG = "image/jpeg";
@@ -425,13 +423,6 @@
String documentId, Point sizeHint, CancellationSignal signal)
throws FileNotFoundException {
final File file = getFileForDocId(documentId);
- if (getTypeForFile(file).equals(MIMETYPE_PDF)) {
- try {
- return PdfUtils.openPdfThumbnail(file, sizeHint);
- } catch (Exception e) {
- Log.v(TAG, "Could not load PDF's thumbnail", e);
- }
- }
return DocumentsContract.openImageThumbnail(file);
}
@@ -461,10 +452,7 @@
final String mimeType = getTypeForFile(file);
final String displayName = file.getName();
- // As of right now, we aren't sure on the performance affect of loading all PDF Thumbnails
- // Until a solution is found, it will be behind a debuggable flag.
- if (mimeType.startsWith("image/")
- || (mimeType.equals(MIMETYPE_PDF) && Build.IS_DEBUGGABLE)) {
+ if (mimeType.startsWith("image/")) {
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 51cf2ea..872b465 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -205,13 +205,17 @@
private static final String TAG_ARRAYITEM = "value";
private static final String ATTR_NAME = "name";
+ private static final Object sLock = new Object();
+
public PowerProfile(Context context) {
// Read the XML file for the given profile (normally only one per
// device)
- if (sPowerMap.size() == 0) {
- readPowerValuesFromXml(context);
+ synchronized (sLock) {
+ if (sPowerMap.size() == 0) {
+ readPowerValuesFromXml(context);
+ }
+ initCpuClusters();
}
- initCpuClusters();
}
private void readPowerValuesFromXml(Context context) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 60fbbe9..0d962eb 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1812,7 +1812,7 @@
mFloatingActionMode.finish();
}
cleanupFloatingActionModeViews();
- mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
+ mFloatingToolbar = new FloatingToolbar(mWindow);
final FloatingActionMode mode =
new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
mFloatingActionModeOriginatingView = originatingView;
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 1d56e1a..f63b5a2 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -120,8 +120,10 @@
/**
* Initializes a floating toolbar.
*/
- public FloatingToolbar(Context context, Window window) {
- mContext = applyDefaultTheme(Preconditions.checkNotNull(context));
+ public FloatingToolbar(Window window) {
+ // TODO(b/65172902): Pass context in constructor when DecorView (and other callers)
+ // supports multi-display.
+ mContext = applyDefaultTheme(window.getContext());
mWindow = Preconditions.checkNotNull(window);
mPopup = new FloatingToolbarPopup(mContext, window.getDecorView());
}
diff --git a/core/res/res/drawable/ic_corp_icon.xml b/core/res/res/drawable/ic_corp_icon.xml
index a6b68f1..48531dd 100644
--- a/core/res/res/drawable/ic_corp_icon.xml
+++ b/core/res/res/drawable/ic_corp_icon.xml
@@ -1,12 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<path
- android:pathData="M24,24m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
- android:fillColor="#FF6D00"/>
- <path
- android:pathData="M35.2,15.6h-5.6v-2.8c0,-1.55 -1.25,-2.8 -2.8,-2.8h-5.6c-1.55,0 -2.8,1.25 -2.8,2.8v2.8h-5.6c-1.55,0 -2.79,1.25 -2.79,2.8L10,33.8c0,1.55 1.25,2.8 2.8,2.8h22.4c1.55,0 2.8,-1.25 2.8,-2.8V18.4C38,16.85 36.75,15.6 35.2,15.6zM24,28.2c-1.54,0 -2.8,-1.26 -2.8,-2.8s1.26,-2.8 2.8,-2.8c1.54,0 2.8,1.26 2.8,2.8S25.54,28.2 24,28.2zM26.8,15.6h-5.6v-2.8h5.6V15.6z"
- android:fillColor="#FFFFFF"/>
+ android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM14,6h-4L10,4h4v2z"
+ android:fillColor="#000000"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 172cae9..5efc226 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -882,6 +882,18 @@
<!-- Maximum color temperature, in Kelvin, supported by Night display. -->
<integer name="config_nightDisplayColorTemperatureMax">4082</integer>
+ <string-array name="config_nightDisplayColorTemperatureCoefficients">
+ <!-- R a-coefficient --> <item>0.0</item>
+ <!-- R b-coefficient --> <item>0.0</item>
+ <!-- R y-intercept --> <item>1.0</item>
+ <!-- G a-coefficient --> <item>-0.00000000962353339</item>
+ <!-- G b-coefficient --> <item>0.000153045476</item>
+ <!-- G y-intercept --> <item>0.390782778</item>
+ <!-- B a-coefficient --> <item>-0.0000000189359041</item>
+ <!-- B b-coefficient --> <item>0.000302412211</item>
+ <!-- B y-intercept --> <item>-0.198650895</item>
+ </string-array>
+
<!-- Indicate whether to allow the device to suspend when the screen is off
due to the proximity sensor. This resource should only be set to true
if the sensor HAL correctly handles the proximity sensor as a wake-up source.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4cfc1ac..8c10a8d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2813,6 +2813,7 @@
<java-symbol type="integer" name="config_nightDisplayColorTemperatureDefault" />
<java-symbol type="integer" name="config_nightDisplayColorTemperatureMin" />
<java-symbol type="integer" name="config_nightDisplayColorTemperatureMax" />
+ <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" />
<!-- Default first user restrictions -->
<java-symbol type="array" name="config_defaultFirstUserRestrictions" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 24a4bae..6756e34 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -99,6 +99,7 @@
Settings.Global.ALARM_MANAGER_CONSTANTS,
Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED,
Settings.Global.ALWAYS_FINISH_ACTIVITIES,
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS,
Settings.Global.ANIMATOR_DURATION_SCALE,
Settings.Global.ANOMALY_DETECTION_CONSTANTS,
Settings.Global.APN_DB_UPDATE_CONTENT_URL,
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 4e59baa..3c3b317 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -216,10 +216,7 @@
.setTransform(Matrix4::identity(), TransformFlags::None)
.setModelViewIdentityEmptyBounds()
.build();
- // Disable blending if this is the first draw to the main framebuffer, in case app has defined
- // transparency where it doesn't make sense - as first draw in opaque window.
- bool overrideDisableBlending = !mHasDrawn && mOpaque && !mRenderTarget.frameBufferId;
- mRenderState.render(glop, mRenderTarget.orthoMatrix, overrideDisableBlending);
+ mRenderState.render(glop, mRenderTarget.orthoMatrix, false);
mHasDrawn = true;
}
@@ -350,8 +347,14 @@
const Glop& glop) {
prepareRender(dirtyBounds, clip);
// Disable blending if this is the first draw to the main framebuffer, in case app has defined
- // transparency where it doesn't make sense - as first draw in opaque window.
- bool overrideDisableBlending = !mHasDrawn && mOpaque && !mRenderTarget.frameBufferId;
+ // transparency where it doesn't make sense - as first draw in opaque window. Note that we only
+ // apply this improvement when the blend mode is SRC_OVER - other modes (e.g. CLEAR) can be
+ // valid draws that affect other content (e.g. draw CLEAR, then draw DST_OVER)
+ bool overrideDisableBlending = !mHasDrawn
+ && mOpaque
+ && !mRenderTarget.frameBufferId
+ && glop.blend.src == GL_ONE
+ && glop.blend.dst == GL_ONE_MINUS_SRC_ALPHA;
mRenderState.render(glop, mRenderTarget.orthoMatrix, overrideDisableBlending);
if (!mRenderTarget.frameBufferId) mHasDrawn = true;
}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 59597af..bbeaa4b 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -442,6 +442,10 @@
private static final int RAF_INFO_SIZE = 160;
private static final int RAF_JPEG_LENGTH_VALUE_SIZE = 4;
+ private static final byte[] HEIF_TYPE_FTYP = new byte[] {'f', 't', 'y', 'p'};
+ private static final byte[] HEIF_BRAND_MIF1 = new byte[] {'m', 'i', 'f', '1'};
+ private static final byte[] HEIF_BRAND_HEIC = new byte[] {'h', 'e', 'i', 'c'};
+
// See http://fileformats.archiveteam.org/wiki/Olympus_ORF
private static final short ORF_SIGNATURE_1 = 0x4f52;
private static final short ORF_SIGNATURE_2 = 0x5352;
@@ -1264,6 +1268,7 @@
private static final int IMAGE_TYPE_RAF = 9;
private static final int IMAGE_TYPE_RW2 = 10;
private static final int IMAGE_TYPE_SRW = 11;
+ private static final int IMAGE_TYPE_HEIF = 12;
static {
sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
@@ -1691,6 +1696,10 @@
getRafAttributes(inputStream);
break;
}
+ case IMAGE_TYPE_HEIF: {
+ getHeifAttributes(inputStream);
+ break;
+ }
case IMAGE_TYPE_ORF: {
getOrfAttributes(inputStream);
break;
@@ -2107,6 +2116,8 @@
return IMAGE_TYPE_JPEG;
} else if (isRafFormat(signatureCheckBytes)) {
return IMAGE_TYPE_RAF;
+ } else if (isHeifFormat(signatureCheckBytes)) {
+ return IMAGE_TYPE_HEIF;
} else if (isOrfFormat(signatureCheckBytes)) {
return IMAGE_TYPE_ORF;
} else if (isRw2Format(signatureCheckBytes)) {
@@ -2145,6 +2156,78 @@
return true;
}
+ private boolean isHeifFormat(byte[] signatureCheckBytes) throws IOException {
+ ByteOrderedDataInputStream signatureInputStream = null;
+ try {
+ signatureInputStream = new ByteOrderedDataInputStream(signatureCheckBytes);
+ signatureInputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
+
+ long chunkSize = signatureInputStream.readInt();
+ byte[] chunkType = new byte[4];
+ signatureInputStream.read(chunkType);
+
+ if (!Arrays.equals(chunkType, HEIF_TYPE_FTYP)) {
+ return false;
+ }
+
+ long chunkDataOffset = 8;
+ if (chunkSize == 1) {
+ // This indicates that the next 8 bytes represent the chunk size,
+ // and chunk data comes after that.
+ chunkSize = signatureInputStream.readLong();
+ if (chunkSize < 16) {
+ // The smallest valid chunk is 16 bytes long in this case.
+ return false;
+ }
+ chunkDataOffset += 8;
+ }
+
+ // only sniff up to signatureCheckBytes.length
+ if (chunkSize > signatureCheckBytes.length) {
+ chunkSize = signatureCheckBytes.length;
+ }
+
+ long chunkDataSize = chunkSize - chunkDataOffset;
+
+ // It should at least have major brand (4-byte) and minor version (4-byte).
+ // The rest of the chunk (if any) is a list of (4-byte) compatible brands.
+ if (chunkDataSize < 8) {
+ return false;
+ }
+
+ byte[] brand = new byte[4];
+ boolean isMif1 = false;
+ boolean isHeic = false;
+ for (long i = 0; i < chunkDataSize / 4; ++i) {
+ if (signatureInputStream.read(brand) != brand.length) {
+ return false;
+ }
+ if (i == 1) {
+ // Skip this index, it refers to the minorVersion, not a brand.
+ continue;
+ }
+ if (Arrays.equals(brand, HEIF_BRAND_MIF1)) {
+ isMif1 = true;
+ } else if (Arrays.equals(brand, HEIF_BRAND_HEIC)) {
+ isHeic = true;
+ }
+ if (isMif1 && isHeic) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ if (DEBUG) {
+ Log.d(TAG, "Exception parsing HEIF file type box.", e);
+ }
+ } finally {
+ if (signatureInputStream != null) {
+ signatureInputStream.close();
+ signatureInputStream = null;
+ }
+ }
+ return false;
+ }
+
/**
* ORF has a similar structure to TIFF but it contains a different signature at the TIFF Header.
* This method looks at the 2 bytes following the Byte Order bytes to determine if this file is
@@ -2437,6 +2520,101 @@
}
}
+ private void getHeifAttributes(ByteOrderedDataInputStream in) throws IOException {
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ try {
+ if (mSeekableFileDescriptor != null) {
+ retriever.setDataSource(mSeekableFileDescriptor);
+ } else {
+ retriever.setDataSource(new MediaDataSource() {
+ long mPosition;
+
+ @Override
+ public void close() throws IOException {}
+
+ @Override
+ public int readAt(long position, byte[] buffer, int offset, int size)
+ throws IOException {
+ if (size == 0) {
+ return 0;
+ }
+ if (position < 0) {
+ return -1;
+ }
+ if (mPosition != position) {
+ in.seek(position);
+ mPosition = position;
+ }
+
+ int bytesRead = in.read(buffer, offset, size);
+ if (bytesRead < 0) {
+ mPosition = -1; // need to seek on next read
+ return -1;
+ }
+
+ mPosition += bytesRead;
+ return bytesRead;
+ }
+
+ @Override
+ public long getSize() throws IOException {
+ return -1;
+ }
+ });
+ }
+
+ String hasVideo = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
+
+ final String METADATA_HAS_VIDEO_VALUE_YES = "yes";
+ if (METADATA_HAS_VIDEO_VALUE_YES.equals(hasVideo)) {
+ String width = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
+ String height = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+
+ if (width != null) {
+ mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
+ ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder));
+ }
+
+ if (height != null) {
+ mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
+ ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
+ }
+
+ // Note that the rotation angle from MediaMetadataRetriever for heif images
+ // are CCW, while rotation in ExifInterface orientations are CW.
+ String rotation = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+ if (rotation != null) {
+ int orientation = ExifInterface.ORIENTATION_NORMAL;
+
+ switch (Integer.parseInt(rotation)) {
+ case 90:
+ orientation = ExifInterface.ORIENTATION_ROTATE_270;
+ break;
+ case 180:
+ orientation = ExifInterface.ORIENTATION_ROTATE_180;
+ break;
+ case 270:
+ orientation = ExifInterface.ORIENTATION_ROTATE_90;
+ break;
+ }
+
+ mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
+ ExifAttribute.createUShort(orientation, mExifByteOrder));
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation);
+ }
+ }
+ } finally {
+ retriever.release();
+ }
+ }
+
/**
* ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail
* images. Both data takes the form of IFDs and can therefore be read with the
@@ -2677,7 +2855,7 @@
}
if (getAttribute(TAG_ORIENTATION) == null) {
mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
- ExifAttribute.createULong(0, mExifByteOrder));
+ ExifAttribute.createUShort(0, mExifByteOrder));
}
if (getAttribute(TAG_LIGHT_SOURCE) == null) {
mAttributes[IFD_TYPE_EXIF].put(TAG_LIGHT_SOURCE,
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 3308fc9..dc7fa8c 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -28,6 +28,7 @@
MediaRouterClientState getState(IMediaRouterClient client);
boolean isPlaybackActive(IMediaRouterClient client);
+ boolean isGlobalBluetoothA2doOn();
void setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan);
void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit);
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 5a16c36..0d99473 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -3408,7 +3408,7 @@
private static void postEventFromNative(Object mediaplayer_ref,
int what, int arg1, int arg2, Object obj)
{
- MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
+ final MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
if (mp == null) {
return;
}
@@ -3416,8 +3416,14 @@
switch (what) {
case MEDIA_INFO:
if (arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
- // this acquires the wakelock if needed, and sets the client side state
- mp.start();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ // this acquires the wakelock if needed, and sets the client side state
+ mp.start();
+ }
+ }).start();
+ Thread.yield();
}
break;
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 29b88a2..2894e89 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -88,7 +88,6 @@
RouteInfo mBluetoothA2dpRoute;
RouteInfo mSelectedRoute;
- RouteInfo mSystemAudioRoute;
final boolean mCanConfigureWifiDisplays;
boolean mActivelyScanningWifiDisplays;
@@ -150,7 +149,6 @@
}
addRouteStatic(mDefaultAudioVideo);
- mSystemAudioRoute = mDefaultAudioVideo;
// This will select the active wifi display route if there is one.
updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());
@@ -185,7 +183,7 @@
}
void updateAudioRoutes(AudioRoutesInfo newRoutes) {
- boolean updated = false;
+ boolean audioRoutesChanged = false;
if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) {
mCurAudioRoutesInfo.mainType = newRoutes.mainType;
int name;
@@ -201,11 +199,10 @@
}
mDefaultAudioVideo.mNameResId = name;
dispatchRouteChanged(mDefaultAudioVideo);
- updated = true;
+ audioRoutesChanged = true;
}
final int mainType = mCurAudioRoutesInfo.mainType;
-
if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
if (mCurAudioRoutesInfo.bluetoothName != null) {
@@ -219,8 +216,6 @@
info.mDeviceType = RouteInfo.DEVICE_TYPE_BLUETOOTH;
mBluetoothA2dpRoute = info;
addRouteStatic(mBluetoothA2dpRoute);
- mSystemAudioRoute = mBluetoothA2dpRoute;
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mSystemAudioRoute, false);
} else {
mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.bluetoothName;
dispatchRouteChanged(mBluetoothA2dpRoute);
@@ -229,30 +224,32 @@
// BT disconnected
removeRouteStatic(mBluetoothA2dpRoute);
mBluetoothA2dpRoute = null;
- mSystemAudioRoute = mDefaultAudioVideo;
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mSystemAudioRoute, false);
}
- updated = true;
+ audioRoutesChanged = true;
}
- if (mBluetoothA2dpRoute != null) {
- final boolean a2dpEnabled = isBluetoothA2dpOn();
- if (mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
- // A2DP off
- mSystemAudioRoute = mDefaultAudioVideo;
- updated = true;
- } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
- a2dpEnabled) {
- // A2DP on or BT connected
- mSystemAudioRoute = mBluetoothA2dpRoute;
- updated = true;
- }
- }
- if (updated) {
+ if (audioRoutesChanged) {
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, getDefaultSystemAudioRoute(), false);
Log.v(TAG, "Audio routes updated: " + newRoutes + ", a2dp=" + isBluetoothA2dpOn());
}
}
+ RouteInfo getDefaultSystemAudioRoute() {
+ boolean globalBluetoothA2doOn = false;
+ try {
+ globalBluetoothA2doOn = mMediaRouterService.isGlobalBluetoothA2doOn();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to call isSystemBluetoothA2doOn.", ex);
+ }
+ return (globalBluetoothA2doOn && mBluetoothA2dpRoute != null)
+ ? mBluetoothA2dpRoute : mDefaultAudioVideo;
+ }
+
+ RouteInfo getCurrentSystemAudioRoute() {
+ return (isBluetoothA2dpOn() && mBluetoothA2dpRoute != null)
+ ? mBluetoothA2dpRoute : mDefaultAudioVideo;
+ }
+
boolean isBluetoothA2dpOn() {
try {
return mAudioService.isBluetoothA2dpOn();
@@ -603,15 +600,13 @@
@Override
public void onRestoreRoute() {
+ // Skip restoring route if the selected route is not a system audio route, or
+ // MediaRouter is initializing.
if ((mSelectedRoute != mDefaultAudioVideo && mSelectedRoute != mBluetoothA2dpRoute)
- || mSelectedRoute == mSystemAudioRoute) {
+ || mSelectedRoute == null) {
return;
}
- try {
- sStatic.mAudioService.setBluetoothA2dpOn(mSelectedRoute == mBluetoothA2dpRoute);
- } catch (RemoteException e) {
- Log.e(TAG, "Error changing Bluetooth A2DP state", e);
- }
+ mSelectedRoute.select();
}
}
}
@@ -946,7 +941,7 @@
boolean wasDefaultOrBluetoothRoute = (oldRoute == sStatic.mDefaultAudioVideo
|| oldRoute == sStatic.mBluetoothA2dpRoute);
if (oldRoute == route
- && (!wasDefaultOrBluetoothRoute || oldRoute == sStatic.mSystemAudioRoute)) {
+ && (!wasDefaultOrBluetoothRoute || route == sStatic.getCurrentSystemAudioRoute())) {
return;
}
if (!route.matchesTypes(types)) {
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index b0052cc..a61881f 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -82,6 +82,7 @@
private MyWebViewClient mWebViewClient;
private boolean mLaunchBrowser = false;
private Thread mTestingThread = null;
+ private boolean mReload = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -290,9 +291,13 @@
mCm.bindProcessToNetwork(network);
mNetwork = network;
runOnUiThreadIfNotFinishing(() -> {
- // Start initial page load so WebView finishes loading proxy settings.
- // Actual load of mUrl is initiated by MyWebViewClient.
- mWebView.loadData("", "text/html", null);
+ if (mReload) {
+ mWebView.reload();
+ } else {
+ // Start initial page load so WebView finishes loading proxy settings.
+ // Actual load of mUrl is initiated by MyWebViewClient.
+ mWebView.loadData("", "text/html", null);
+ }
});
}
@@ -305,6 +310,12 @@
mWebView.loadUrl(mUrl.toString());
});
}
+
+ @Override
+ public void onLost(Network lostNetwork) {
+ if (DBG) logd("Network lost");
+ mReload = true;
+ }
};
logd("request Network for captive portal");
mCm.requestNetwork(request, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MS);
diff --git a/packages/SettingsLib/res/drawable/ic_info_outline_24dp.xml b/packages/SettingsLib/res/drawable/ic_info_outline_24dp.xml
index 3fe1e9e..5a7f954 100644
--- a/packages/SettingsLib/res/drawable/ic_info_outline_24dp.xml
+++ b/packages/SettingsLib/res/drawable/ic_info_outline_24dp.xml
@@ -21,6 +21,12 @@
android:viewportHeight="24.0"
android:tint="?android:attr/textColorSecondary">
<path
- android:fillColor="#000000"
- android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
+ android:fillColor="#FF000000"
+ android:pathData="M12,17L12,17c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1l0,0c-0.55,0 -1,0.45 -1,1v4C11,16.55 11.45,17 12,17z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2c-5.52,0 -10,4.48 -10,10s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8s8,3.59 8,8S16.41,20 12,20z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,9.1L12,9.1c0.61,0 1.1,-0.49 1.1,-1.1l0,0c0,-0.61 -0.49,-1.1 -1.1,-1.1l0,0c-0.61,0 -1.1,0.49 -1.1,1.1l0,0C10.9,8.61 11.39,9.1 12,9.1z"/>
</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 0a53c78..64ec16a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -99,7 +99,9 @@
public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
final int iconSize = UserIconDrawable.getSizeForList(context);
if (user.isManagedProfile()) {
- return context.getDrawable(com.android.internal.R.drawable.ic_corp_icon);
+ Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_icon);
+ drawable.setBounds(0, 0, iconSize, iconSize);
+ return drawable;
}
if (user.iconPath != null) {
Bitmap icon = um.getUserIcon(user.id);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 2c4f9c4..c24cdae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -76,10 +76,10 @@
public static String generatePreferenceKey(AccessPoint accessPoint) {
StringBuilder builder = new StringBuilder();
- if (TextUtils.isEmpty(accessPoint.getBssid())) {
- builder.append(accessPoint.getSsidStr());
- } else {
+ if (TextUtils.isEmpty(accessPoint.getSsidStr())) {
builder.append(accessPoint.getBssid());
+ } else {
+ builder.append(accessPoint.getSsidStr());
}
builder.append(',').append(accessPoint.getSecurity());
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index 7fe69a7..6cfdc28 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -36,26 +36,28 @@
private Context mContext = RuntimeEnvironment.application;
@Test
- public void generatePreferenceKey_shouldReturnSsidPlusSecurity() {
+ public void generatePreferenceKey_returnsSsidPlusSecurity() {
String ssid = "ssid";
+ String bssid = "00:00:00:00:00:00";
int security = AccessPoint.SECURITY_WEP;
String expectedKey = ssid + ',' + security;
TestAccessPointBuilder builder = new TestAccessPointBuilder(mContext);
- builder.setSsid(ssid).setSecurity(security);
+ builder.setBssid(bssid).setSsid(ssid).setSecurity(security);
assertThat(AccessPointPreference.generatePreferenceKey(builder.build()))
.isEqualTo(expectedKey);
}
@Test
- public void generatePreferenceKey_shouldReturnBssidPlusSecurity() {
- String bssid = "bssid";
+ public void generatePreferenceKey_emptySsidReturnsBssidPlusSecurity() {
+ String ssid = "";
+ String bssid = "00:00:00:00:00:00";
int security = AccessPoint.SECURITY_WEP;
String expectedKey = bssid + ',' + security;
TestAccessPointBuilder builder = new TestAccessPointBuilder(mContext);
- builder.setBssid(bssid).setSecurity(security);
+ builder.setBssid(bssid).setSsid(ssid).setSecurity(security);
assertThat(AccessPointPreference.generatePreferenceKey(builder.build()))
.isEqualTo(expectedKey);
diff --git a/packages/SystemUI/res/drawable/car_qs_background_primary.xml b/packages/SystemUI/res/drawable/car_qs_background_primary.xml
deleted file mode 100644
index 0f77987..0000000
--- a/packages/SystemUI/res/drawable/car_qs_background_primary.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<inset xmlns:android="http://schemas.android.com/apk/res/android">
- <shape>
- <solid android:color="?android:attr/colorPrimaryDark"/>
- </shape>
-</inset>
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index deb494f..59c0957 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -26,4 +26,5 @@
android:textColor="?android:attr/textColorPrimary"
android:gravity="center_vertical|start"
android:paddingEnd="@dimen/battery_level_padding_start"
+ android:importantForAccessibility="no"
/>
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/SystemUI/res/layout/car_qs_panel.xml
index 0b46b0b..4cb0fd5 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/SystemUI/res/layout/car_qs_panel.xml
@@ -18,12 +18,13 @@
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/car_qs_background_primary"
+ android:background="@color/car_qs_background_primary"
android:orientation="vertical"
- android:elevation="4dp">
+ android:elevation="4dp"
+ android:theme="@android:style/Theme">
- <include layout="@layout/car_status_bar_header" />
- <include layout="@layout/car_qs_footer" />
+ <include layout="@layout/car_status_bar_header"/>
+ <include layout="@layout/car_qs_footer"/>
<com.android.systemui.statusbar.car.UserGridView
android:id="@+id/user_grid"
diff --git a/packages/SystemUI/res/layout/home.xml b/packages/SystemUI/res/layout/home.xml
index 53ef2ab..7b67b79 100644
--- a/packages/SystemUI/res/layout/home.xml
+++ b/packages/SystemUI/res/layout/home.xml
@@ -23,8 +23,8 @@
systemui:keyCode="3"
android:scaleType="fitCenter"
android:contentDescription="@string/accessibility_home"
- android:paddingTop="13dp"
- android:paddingBottom="13dp"
+ android:paddingTop="@dimen/home_padding"
+ android:paddingBottom="@dimen/home_padding"
android:paddingStart="@dimen/navigation_key_padding"
android:paddingEnd="@dimen/navigation_key_padding"
/>
diff --git a/packages/SystemUI/res/layout/navigation_layout.xml b/packages/SystemUI/res/layout/navigation_layout.xml
index 53f5dfe..3e60794 100644
--- a/packages/SystemUI/res/layout/navigation_layout.xml
+++ b/packages/SystemUI/res/layout/navigation_layout.xml
@@ -21,8 +21,8 @@
android:layout_height="match_parent"
android:layout_marginStart="@dimen/rounded_corner_content_padding"
android:layout_marginEnd="@dimen/rounded_corner_content_padding"
- android:paddingStart="8dp"
- android:paddingEnd="8dp">
+ android:paddingStart="@dimen/nav_content_padding"
+ android:paddingEnd="@dimen/nav_content_padding">
<com.android.systemui.statusbar.phone.NearestTouchFrame
android:id="@+id/nav_buttons"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 849bea4..2cf3e4a 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -37,6 +37,7 @@
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"
+ android:textDirection="locale"
android:singleLine="true" />
<com.android.systemui.BatteryMeterView android:id="@+id/battery"
diff --git a/packages/SystemUI/res/values-sw372dp/dimens.xml b/packages/SystemUI/res/values-sw372dp/dimens.xml
new file mode 100644
index 0000000..635185d
--- /dev/null
+++ b/packages/SystemUI/res/values-sw372dp/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2006, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+ <dimen name="nav_content_padding">8dp</dimen>
+ <dimen name="rounded_corner_content_padding">8dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values/colors_car.xml b/packages/SystemUI/res/values/colors_car.xml
index 9593fe51..1b8c2fa 100644
--- a/packages/SystemUI/res/values/colors_car.xml
+++ b/packages/SystemUI/res/values/colors_car.xml
@@ -17,6 +17,7 @@
*/
-->
<resources>
+ <color name="car_qs_background_primary">#263238</color> <!-- Blue Gray 900 -->
<color name="car_user_switcher_progress_bgcolor">#00000000</color> <!-- Transparent -->
<color name="car_user_switcher_progress_fgcolor">#80CBC4</color> <!-- Teal 200 -->
<color name="car_user_switcher_no_user_image_bgcolor">#FAFAFA</color> <!-- Grey 50 -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c8e6021..bcaafcc 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -846,11 +846,15 @@
<dimen name="edge_margin">16dp</dimen>
<dimen name="rounded_corner_radius">0dp</dimen>
- <dimen name="rounded_corner_content_padding">8dp</dimen>
+ <dimen name="rounded_corner_content_padding">0dp</dimen>
+ <dimen name="nav_content_padding">0dp</dimen>
<!-- Intended corner radius when drawing the mobile signal -->
<dimen name="stat_sys_mobile_signal_corner_radius">0.75dp</dimen>
<!-- How far to inset the rounded edges -->
<dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen>
+ <!-- Home button padding for sizing -->
+ <dimen name="home_padding">15dp</dimen>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index c4de63b..fd2447b 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -18,6 +18,7 @@
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
import android.animation.ArgbEvaluator;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -40,6 +41,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.graph.BatteryMeterDrawableBase;
+import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -58,6 +60,7 @@
private final BatteryMeterDrawableBase mDrawable;
private final String mSlotBattery;
private final ImageView mBatteryIconView;
+ private final CurrentUserTracker mUserTracker;
private TextView mBatteryPercentView;
private BatteryController mBatteryController;
@@ -72,6 +75,7 @@
private int mLightModeBackgroundColor;
private int mLightModeFillColor;
private float mDarkIntensity;
+ private int mUser;
public BatteryMeterView(Context context) {
this(context, null, 0);
@@ -120,6 +124,16 @@
// Init to not dark at all.
onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
+ mUserTracker = new CurrentUserTracker(mContext) {
+ @Override
+ public void onUserSwitched(int newUserId) {
+ mUser = newUserId;
+ getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
+ getContext().getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
+ newUserId);
+ }
+ };
}
public void setForceShowPercent(boolean show) {
@@ -145,16 +159,19 @@
super.onAttachedToWindow();
mBatteryController = Dependency.get(BatteryController.class);
mBatteryController.addCallback(this);
+ mUser = ActivityManager.getCurrentUser();
getContext().getContentResolver().registerContentObserver(
- Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver);
+ Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
updateShowPercent();
Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
Dependency.get(ConfigurationController.class).addCallback(this);
+ mUserTracker.startTracking();
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
+ mUserTracker.stopTracking();
mBatteryController.removeCallback(this);
getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
Dependency.get(TunerService.class).removeTunable(this);
@@ -191,8 +208,8 @@
private void updateShowPercent() {
final boolean showing = mBatteryPercentView != null;
- if (0 != Settings.System.getInt(getContext().getContentResolver(),
- SHOW_BATTERY_PERCENT, 0) || mForceShowPercent) {
+ if (0 != Settings.System.getIntForUser(getContext().getContentResolver(),
+ SHOW_BATTERY_PERCENT, 0, mUser) || mForceShowPercent) {
if (!showing) {
mBatteryPercentView = loadPercentView();
if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
new file mode 100644
index 0000000..d1d1808
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -0,0 +1,122 @@
+/*
+ * 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.systemui.doze;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+import android.text.format.DateUtils;
+import android.util.KeyValueListParser;
+import android.util.Log;
+
+import com.android.systemui.R;
+
+import java.util.Arrays;
+
+/**
+ * Class to store the policy for AOD, which comes from
+ * {@link android.provider.Settings.Global}
+ */
+public class AlwaysOnDisplayPolicy {
+ public static final String TAG = "AlwaysOnDisplayPolicy";
+
+ static final String KEY_SCREEN_BRIGHTNESS_ARRAY = "screen_brightness_array";
+ static final String KEY_DIMMING_SCRIM_ARRAY = "dimming_scrim_array";
+ static final String KEY_PROX_SCREEN_OFF_DELAY_MS = "prox_screen_off_delay";
+ static final String KEY_PROX_COOLDOWN_TRIGGER_MS = "prox_cooldown_trigger";
+ static final String KEY_PROX_COOLDOWN_PERIOD_MS = "prox_cooldown_period";
+
+ /**
+ * Integer array to map ambient brightness type to real screen brightness.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_SCREEN_BRIGHTNESS_ARRAY
+ */
+ public final int[] screenBrightnessArray;
+
+ /**
+ * Integer array to map ambient brightness type to dimming scrim.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_DIMMING_SCRIM_ARRAY
+ */
+ public final int[] dimmingScrimArray;
+
+ /**
+ * Delay time(ms) from covering the prox to turning off the screen.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_PROX_SCREEN_OFF_DELAY_MS
+ */
+ public final long proxScreenOffDelayMs;
+
+ /**
+ * The threshold time(ms) to trigger the cooldown timer, which will
+ * turn off prox sensor for a period.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_PROX_COOLDOWN_TRIGGER_MS
+ */
+ public final long proxCooldownTriggerMs;
+
+ /**
+ * The period(ms) to turning off the prox sensor if
+ * {@link #KEY_PROX_COOLDOWN_TRIGGER_MS} is triggered.
+ *
+ * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS
+ * @see #KEY_PROX_COOLDOWN_PERIOD_MS
+ */
+ public final long proxCooldownPeriodMs;
+
+ private final KeyValueListParser mParser;
+
+ public AlwaysOnDisplayPolicy(Context context) {
+ final Resources resources = context.getResources();
+ mParser = new KeyValueListParser(',');
+
+ final String value = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
+
+ try {
+ mParser.setString(value);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Bad AOD constants");
+ }
+
+ proxScreenOffDelayMs = mParser.getLong(KEY_PROX_SCREEN_OFF_DELAY_MS,
+ 10 * DateUtils.MINUTE_IN_MILLIS);
+ proxCooldownTriggerMs = mParser.getLong(KEY_PROX_COOLDOWN_TRIGGER_MS,
+ 2 * DateUtils.MINUTE_IN_MILLIS);
+ proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
+ 5 * DateUtils.MINUTE_IN_MILLIS);
+ screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
+ resources.getIntArray(R.array.config_doze_brightness_sensor_to_brightness));
+ dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY,
+ resources.getIntArray(R.array.config_doze_brightness_sensor_to_scrim_opacity));
+ }
+
+ private int[] parseIntArray(final String key, final int[] defaultArray) {
+ final String value = mParser.getString(key, null);
+ if (value != null) {
+ return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
+ Integer::parseInt).toArray();
+ } else {
+ return defaultArray;
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index d374d68a..6f8bcff 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -59,7 +59,7 @@
DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock);
machine.setParts(new DozeMachine.Part[]{
- new DozePauser(handler, machine, alarmManager),
+ new DozePauser(handler, machine, alarmManager, new AlwaysOnDisplayPolicy(context)),
new DozeFalsingManagerAdapter(FalsingManager.getInstance(context)),
createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
handler, wakeLock, machine),
@@ -76,7 +76,8 @@
Handler handler) {
Sensor sensor = DozeSensors.findSensorWithType(sensorManager,
context.getString(R.string.doze_brightness_sensor_type));
- return new DozeScreenBrightness(context, service, sensorManager, sensor, host, handler);
+ return new DozeScreenBrightness(context, service, sensorManager, sensor, host, handler,
+ new AlwaysOnDisplayPolicy(context));
}
private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
index a33b454c..76a1902 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozePauser.java
@@ -26,20 +26,22 @@
*/
public class DozePauser implements DozeMachine.Part {
public static final String TAG = DozePauser.class.getSimpleName();
- private static final long TIMEOUT = 10 * 1000;
private final AlarmTimeout mPauseTimeout;
private final DozeMachine mMachine;
+ private final long mTimeoutMs;
- public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager) {
+ public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager,
+ AlwaysOnDisplayPolicy policy) {
mMachine = machine;
mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler);
+ mTimeoutMs = policy.proxScreenOffDelayMs;
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case DOZE_AOD_PAUSING:
- mPauseTimeout.schedule(TIMEOUT, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ mPauseTimeout.schedule(mTimeoutMs, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
break;
default:
mPauseTimeout.cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 30420529..11b4b0e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -42,7 +42,7 @@
public DozeScreenBrightness(Context context, DozeMachine.Service service,
SensorManager sensorManager, Sensor lightSensor, DozeHost host,
- Handler handler) {
+ Handler handler, AlwaysOnDisplayPolicy policy) {
mContext = context;
mDozeService = service;
mSensorManager = sensorManager;
@@ -50,10 +50,8 @@
mDozeHost = host;
mHandler = handler;
- mSensorToBrightness = context.getResources().getIntArray(
- R.array.config_doze_brightness_sensor_to_brightness);
- mSensorToScrimOpacity = context.getResources().getIntArray(
- R.array.config_doze_brightness_sensor_to_scrim_opacity);
+ mSensorToBrightness = policy.screenBrightnessArray;
+ mSensorToScrimOpacity = policy.dimmingScrimArray;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 566353c..91cde37 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -72,7 +72,7 @@
public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
DozeParameters dozeParameters,
AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback,
- Consumer<Boolean> proxCallback) {
+ Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
mContext = context;
mAlarmManager = alarmManager;
mSensorManager = sensorManager;
@@ -112,7 +112,7 @@
true /* touchscreen */),
};
- mProxSensor = new ProxSensor();
+ mProxSensor = new ProxSensor(policy);
mCallback = callback;
}
@@ -206,17 +206,16 @@
private class ProxSensor implements SensorEventListener {
- static final long COOLDOWN_TRIGGER = 2 * 1000;
- static final long COOLDOWN_PERIOD = 5 * 1000;
-
boolean mRequested;
boolean mRegistered;
Boolean mCurrentlyFar;
long mLastNear;
final AlarmTimeout mCooldownTimer;
+ final AlwaysOnDisplayPolicy mPolicy;
- public ProxSensor() {
+ public ProxSensor(AlwaysOnDisplayPolicy policy) {
+ mPolicy = policy;
mCooldownTimer = new AlarmTimeout(mAlarmManager, this::updateRegistered,
"prox_cooldown", mHandler);
}
@@ -264,11 +263,12 @@
// Sensor has been unregistered by the proxCallback. Do nothing.
} else if (!mCurrentlyFar) {
mLastNear = now;
- } else if (mCurrentlyFar && now - mLastNear < COOLDOWN_TRIGGER) {
+ } else if (mCurrentlyFar && now - mLastNear < mPolicy.proxCooldownTriggerMs) {
// If the last near was very recent, we might be using more power for prox
// wakeups than we're saving from turning of the screen. Instead, turn it off
// for a while.
- mCooldownTimer.schedule(COOLDOWN_PERIOD, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ mCooldownTimer.schedule(mPolicy.proxCooldownPeriodMs,
+ AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
updateRegistered();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 4583160..f7a258a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -84,7 +84,8 @@
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar);
+ config, wakeLock, this::onSensor, this::onProximityFar,
+ new AlwaysOnDisplayPolicy(context));
mUiModeManager = mContext.getSystemService(UiModeManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index ca58080..e8c1295 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -127,6 +127,10 @@
private PipNotification mPipNotification;
private ParceledListSlice mCustomActions;
+ // Keeps track of the IME visibility to adjust the PiP when the IME is visible
+ private boolean mImeVisible;
+ private int mImeHeightAdjustment;
+
private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
private final Runnable mResizePinnedStackRunnable = new Runnable() {
@@ -175,7 +179,22 @@
public void onListenerRegistered(IPinnedStackController controller) {}
@Override
- public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+ if (mState == STATE_PIP) {
+ if (mImeVisible != imeVisible) {
+ if (imeVisible) {
+ // Save the IME height adjustment, and offset to not occlude the IME
+ mPipBounds.offset(0, -imeHeight);
+ mImeHeightAdjustment = imeHeight;
+ } else {
+ // Apply the inverse adjustment when the IME is hidden
+ mPipBounds.offset(0, mImeHeightAdjustment);
+ }
+ mImeVisible = imeVisible;
+ resizePinnedStack(STATE_PIP);
+ }
+ }
+ }
@Override
public void onMinimizedStateChanged(boolean isMinimized) {}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
index f663315..82c0128 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -136,11 +136,12 @@
return disableAny;
}
- public void disableAll() {
+ public boolean disableAll() {
ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
for (int i = 0; i < plugins.size(); i++) {
disable(plugins.get(i));
}
+ return plugins.size() != 0;
}
private void disable(PluginInfo info) {
@@ -182,6 +183,7 @@
if (DEBUG) Log.d(TAG, "onPluginConnected");
PluginPrefs.setHasPlugins(mContext);
PluginInfo<T> info = (PluginInfo<T>) msg.obj;
+ mManager.handleWtfs();
if (!(msg.obj instanceof PluginFragment)) {
// Only call onDestroy for plugins that aren't fragments, as fragments
// will get the onCreate as part of the fragment lifecycle.
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
index 493d244..03747d5 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
@@ -36,6 +36,9 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
+import android.util.Log.TerribleFailure;
+import android.util.Log.TerribleFailureHandler;
import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,10 +74,11 @@
private boolean mListening;
private boolean mHasOneShot;
private Looper mLooper;
+ private boolean mWtfsSet;
public PluginManagerImpl(Context context) {
this(context, new PluginInstanceManagerFactory(),
- Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
+ Build.IS_DEBUGGABLE, Thread.getUncaughtExceptionPreHandler());
}
@VisibleForTesting
@@ -88,7 +92,7 @@
PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
defaultHandler);
- Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
+ Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
if (isDebuggable) {
new Handler(mLooper).post(() -> {
// Plugin dependencies that don't have another good home can go here, but
@@ -290,6 +294,15 @@
return false;
}
+ public void handleWtfs() {
+ if (!mWtfsSet) {
+ mWtfsSet = true;
+ Log.setWtfHandler((tag, what, system) -> {
+ throw new CrashWhilePluginActiveException(what);
+ });
+ }
+ }
+
@VisibleForTesting
public static class PluginInstanceManagerFactory {
public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
@@ -339,9 +352,12 @@
// disable all the plugins, so we can be sure that SysUI is running as
// best as possible.
for (PluginInstanceManager manager : mPluginMap.values()) {
- manager.disableAll();
+ disabledAny |= manager.disableAll();
}
}
+ if (disabledAny) {
+ throwable = new CrashWhilePluginActiveException(throwable);
+ }
// Run the normal exception handler so we can crash and cleanup our state.
mHandler.uncaughtException(thread, throwable);
@@ -358,4 +374,10 @@
return disabledAny | checkStack(throwable.getCause());
}
}
+
+ private class CrashWhilePluginActiveException extends RuntimeException {
+ public CrashWhilePluginActiveException(Throwable throwable) {
+ super(throwable);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 10514a7..8b434a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -323,7 +323,7 @@
post(new Runnable() {
@Override
public void run() {
- handleShowingDetail(detail, x, y, true /* toggleQs */);
+ handleShowingDetail(detail, x, y, false /* toggleQs */);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index f91aa9a..b4cc4b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -184,7 +184,11 @@
}
view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
final ImageView iv = (ImageView) view.findViewById(android.R.id.icon);
- iv.setImageResource(item.icon);
+ if (item.iconDrawable != null) {
+ iv.setImageDrawable(item.iconDrawable);
+ } else {
+ iv.setImageResource(item.icon);
+ }
iv.getOverlay().clear();
if (item.overlay != null) {
item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize);
@@ -254,6 +258,7 @@
public static class Item {
public int icon;
+ public Drawable iconDrawable;
public Drawable overlay;
public CharSequence line1;
public CharSequence line2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 12fccda..bc6233d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles;
+import static com.android.settingslib.graph.BluetoothDeviceLayerDrawable.createLayerDrawable;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
@@ -32,8 +34,10 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.R.drawable;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -127,6 +131,15 @@
if (connected) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connected);
state.label = mController.getLastDeviceName();
+ CachedBluetoothDevice lastDevice = mController.getLastDevice();
+ if (lastDevice != null) {
+ int batteryLevel = lastDevice.getBatteryLevel();
+ if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+ BluetoothDeviceLayerDrawable drawable = createLayerDrawable(mContext,
+ R.drawable.ic_qs_bluetooth_connected, batteryLevel);
+ state.icon = new DrawableIcon(drawable);
+ }
+ }
state.contentDescription = mContext.getString(
R.string.accessibility_bluetooth_name, state.label);
} else if (state.isTransient) {
@@ -278,6 +291,8 @@
item.icon = R.drawable.ic_qs_bluetooth_connected;
int batteryLevel = device.getBatteryLevel();
if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+ item.iconDrawable = createLayerDrawable(mContext, item.icon,
+ batteryLevel);
item.line2 = mContext.getString(
R.string.quick_settings_connected_battery_level,
Utils.formatPercentage(batteryLevel));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 4a8b43e..3e1522d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -565,6 +565,13 @@
mImpl.showPrevAffiliatedTask();
}
+ @Override
+ public void appTransitionFinished() {
+ // Fallback, reset the flag once an app transition ends
+ EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(
+ false /* waitingForTransitionStart */));
+ }
+
/**
* Updates on configuration change.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 79558a3..aecf95f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -132,6 +132,11 @@
// Preloads the next task
RecentsConfiguration config = Recents.getConfiguration();
if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
+ Rect windowRect = getWindowRect(null /* windowRectOverride */);
+ if (windowRect.isEmpty()) {
+ return;
+ }
+
// Load the next task only if we aren't svelte
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
@@ -146,8 +151,7 @@
// This callback is made when a new activity is launched and the old one is
// paused so ignore the current activity and try and preload the thumbnail for
// the previous one.
- updateDummyStackViewLayout(mBackgroundLayoutAlgorithm, stack,
- getWindowRect(null /* windowRectOverride */));
+ updateDummyStackViewLayout(mBackgroundLayoutAlgorithm, stack, windowRect);
// Launched from app is always the worst case (in terms of how many
// thumbnails/tasks visible)
@@ -376,6 +380,12 @@
}
public void toggleRecents(int growTarget) {
+ // Skip preloading if the task is locked
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.isScreenPinningActive()) {
+ return;
+ }
+
// Skip this toggle if we are already waiting to trigger recents via alt-tab
if (mFastAltTabTrigger.isDozing()) {
return;
@@ -391,7 +401,6 @@
mTriggeredFromAltTab = false;
try {
- SystemServicesProxy ssp = Recents.getSystemServices();
MutableBoolean isHomeStackVisible = new MutableBoolean(true);
long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
@@ -454,11 +463,16 @@
}
public void preloadRecents() {
+ // Skip preloading if the task is locked
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.isScreenPinningActive()) {
+ return;
+ }
+
// Preload only the raw task list into a new load plan (which will be consumed by the
// RecentsActivity) only if there is a task to animate to. Post this to ensure that we
// don't block the touch feedback on the nav bar button which triggers this.
mHandler.post(() -> {
- SystemServicesProxy ssp = Recents.getSystemServices();
MutableBoolean isHomeStackVisible = new MutableBoolean(true);
if (!ssp.isRecentsActivityVisible(isHomeStackVisible)) {
ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 74737c4..569e58d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -184,8 +184,15 @@
mVisible = visible;
mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
if (visible) {
- hideTransientIndication();
+ // If this is called after an error message was already shown, we should not clear it.
+ // Otherwise the error message won't be shown
+ if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
+ hideTransientIndication();
+ }
updateIndication();
+ } else if (!visible) {
+ // If we unlock and return to keyguard quickly, previous error should not be shown
+ hideTransientIndication();
}
}
@@ -389,7 +396,6 @@
hideTransientIndication();
} else if (msg.what == MSG_CLEAR_FP_MSG) {
mLockIcon.setTransientFpError(false);
- hideTransientIndication();
}
}
};
@@ -443,10 +449,10 @@
int errorColor = Utils.getColorError(mContext);
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
- } else if (updateMonitor.isDeviceInteractive()
- || mDozing && updateMonitor.isScreenOn()) {
+ } else if (updateMonitor.isScreenOn()) {
mLockIcon.setTransientFpError(true);
showTransientIndication(helpString, errorColor);
+ hideTransientIndicationDelayed(TRANSIENT_FP_ERROR_TIMEOUT);
mHandler.removeMessages(MSG_CLEAR_FP_MSG);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
TRANSIENT_FP_ERROR_TIMEOUT);
@@ -459,7 +465,8 @@
@Override
public void onFingerprintError(int msgId, String errString) {
KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
- if (!updateMonitor.isUnlockingWithFingerprintAllowed()
+ if ((!updateMonitor.isUnlockingWithFingerprintAllowed()
+ && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
return;
}
@@ -472,7 +479,7 @@
if (mLastSuccessiveErrorMessage != msgId) {
mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
}
- } else if (updateMonitor.isDeviceInteractive()) {
+ } else if (updateMonitor.isScreenOn()) {
showTransientIndication(errString, errorColor);
// We want to keep this message around in case the screen was off
hideTransientIndicationDelayed(HIDE_DELAY_MS);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 5c04058..8c923cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -14,6 +14,9 @@
package com.android.systemui.statusbar.phone;
+import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
+import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
+
import static com.android.systemui.statusbar.phone.StatusBar.reinflateSignalCluster;
import android.annotation.Nullable;
@@ -144,35 +147,39 @@
final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
mDisabled1 = state1;
- if ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
- if ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
+ if ((diff1 & DISABLE_SYSTEM_INFO) != 0) {
+ if ((state1 & DISABLE_SYSTEM_INFO) != 0) {
hideSystemIconArea(animate);
} else {
showSystemIconArea(animate);
}
}
- if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
- if ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((state1 & DISABLE_NOTIFICATION_ICONS) != 0) {
hideNotificationIconArea(animate);
} else {
showNotificationIconArea(animate);
}
}
+ if (!BarTransitions.HIGH_END) {
+ int mask = DISABLE_NOTIFICATION_ICONS | DISABLE_SYSTEM_INFO;
+ getView().setVisibility((mDisabled1 & mask) == mask ? View.GONE : View.VISIBLE);
+ }
}
protected int adjustDisableFlags(int state) {
if (!mStatusBarComponent.isLaunchTransitionFadingAway()
&& !mKeyguardMonitor.isKeyguardFadingAway()
&& shouldHideNotificationIcons()) {
- state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
- state |= StatusBarManager.DISABLE_SYSTEM_INFO;
+ state |= DISABLE_NOTIFICATION_ICONS;
+ state |= DISABLE_SYSTEM_INFO;
}
if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
if (mNetworkController.hasEmergencyCryptKeeperText()) {
- state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
+ state |= DISABLE_NOTIFICATION_ICONS;
}
if (!mNetworkController.isRadioOn()) {
- state |= StatusBarManager.DISABLE_SYSTEM_INFO;
+ state |= DISABLE_SYSTEM_INFO;
}
}
return state;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index b0ac6ec..d3a6280 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -127,6 +127,11 @@
}
public void setIconsDark(boolean dark, boolean animate) {
+ if (!BarTransitions.HIGH_END) {
+ setIconTintInternal(0.0f);
+ mNextDarkIntensity = 0.0f;
+ return;
+ }
if (!animate) {
setIconTintInternal(dark ? 1.0f : 0.0f);
mNextDarkIntensity = dark ? 1.0f : 0.0f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 46f9c04..afe5c91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -404,8 +404,8 @@
false /* collapseWhenFinished */);
notifyBarPanelExpansionChanged();
if (mVibrateOnOpening && !isHapticFeedbackDisabled(mContext)) {
- AsyncTask.execute(
- () -> mVibrator.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_TICK)));
+ AsyncTask.execute(() ->
+ mVibrator.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_TICK, false)));
}
}
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 0ce2bcf..7aebfdc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -5382,7 +5382,6 @@
@Override
public void appTransitionFinished() {
- touchAutoDim();
EventBus.getDefault().send(new AppTransitionFinishedEvent());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 03f42a6..d7f11f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -422,7 +422,7 @@
mFloatingActionMode.finish();
}
cleanupFloatingActionModeViews();
- mFloatingToolbar = new FloatingToolbar(mContext, mFakeWindow);
+ mFloatingToolbar = new FloatingToolbar(mFakeWindow);
final FloatingActionMode mode =
new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
mFloatingActionModeOriginatingView = originatingView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index 7e92edf..1411a54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -154,6 +154,11 @@
public void onStrongAuthStateChanged(int userId) {
update(false /* updateAlways */);
}
+
+ @Override
+ public void onScreenTurnedOff() {
+ update(false /* updateAlways */);
+ }
};
public boolean isTrustManaged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
index 9daa199..b693ebb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java
@@ -39,6 +39,7 @@
int getMaxConnectionState(CachedBluetoothDevice device);
int getBondState(CachedBluetoothDevice device);
+ CachedBluetoothDevice getLastDevice();
public interface Callback {
void onBluetoothStateChange(boolean enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 5b24f9c..3b15c2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -121,6 +121,11 @@
}
@Override
+ public CachedBluetoothDevice getLastDevice() {
+ return mLastDevice;
+ }
+
+ @Override
public int getMaxConnectionState(CachedBluetoothDevice device) {
return getCachedState(device).mMaxConnectionState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
index 1ea23bb..4d95969 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
@@ -22,9 +22,11 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.widget.Checkable;
import android.widget.LinearLayout;
import android.widget.TextView;
+
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -65,6 +67,11 @@
mDone = mButtons.findViewById(android.R.id.button1);
mDone.setOnClickListener(this);
((TextView) mDone).setText(R.string.quick_settings_done);
+ // Hide the resizing space because it causes issues in the volume panel.
+ ViewGroup detail_header = findViewById(R.id.tuner_zen_switch);
+ detail_header.getChildAt(0).setVisibility(View.GONE);
+ // No background so it can blend with volume panel.
+ findViewById(R.id.edit_container).setBackground(null);
}
@Override
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 5e71dd4..27c16d5 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -54,7 +54,8 @@
SystemUI-proto \
SystemUI-tags \
legacy-android-test \
- testables
+ testables \
+ truth-prebuilt \
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java
new file mode 100644
index 0000000..abc2d0e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/AlwaysOnDisplayPolicyTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.systemui.doze;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.format.DateUtils;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AlwaysOnDisplayPolicyTest extends SysuiTestCase {
+ private static final String ALWAYS_ON_DISPLAY_CONSTANTS_VALUE = "prox_screen_off_delay=1000"
+ + ",prox_cooldown_trigger=2000"
+ + ",prox_cooldown_period=3000"
+ + ",screen_brightness_array=1:2:3:4:5"
+ + ",dimming_scrim_array=5:4:3:2:1";
+
+ private String mPreviousConfig;
+
+ @Before
+ public void setUp() {
+ mPreviousConfig = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
+ }
+
+ @After
+ public void tearDown() {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS, mPreviousConfig);
+ }
+
+ @Test
+ public void testPolicy_valueNull_containsDefaultValue() throws Exception {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS, null);
+
+ AlwaysOnDisplayPolicy policy = new AlwaysOnDisplayPolicy(mContext);
+
+ assertThat(policy.proxScreenOffDelayMs).isEqualTo(10 * DateUtils.MINUTE_IN_MILLIS);
+ assertThat(policy.proxCooldownTriggerMs).isEqualTo(2 * DateUtils.MINUTE_IN_MILLIS);
+ assertThat(policy.proxCooldownPeriodMs).isEqualTo(5 * DateUtils.MINUTE_IN_MILLIS);
+ assertThat(policy.screenBrightnessArray).isEqualTo(mContext.getResources().getIntArray(
+ R.array.config_doze_brightness_sensor_to_brightness));
+ assertThat(policy.dimmingScrimArray).isEqualTo(mContext.getResources().getIntArray(
+ R.array.config_doze_brightness_sensor_to_scrim_opacity));
+ }
+
+ @Test
+ public void testPolicy_valueNotNull_containsValue() throws Exception {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS, ALWAYS_ON_DISPLAY_CONSTANTS_VALUE);
+
+ AlwaysOnDisplayPolicy policy = new AlwaysOnDisplayPolicy(mContext);
+
+ assertThat(policy.proxScreenOffDelayMs).isEqualTo(1000);
+ assertThat(policy.proxCooldownTriggerMs).isEqualTo(2000);
+ assertThat(policy.proxCooldownPeriodMs).isEqualTo(3000);
+ assertThat(policy.screenBrightnessArray).isEqualTo(new int[]{1, 2, 3, 4, 5});
+ assertThat(policy.dimmingScrimArray).isEqualTo(new int[]{5, 4, 3, 2, 1});
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index c275806..46e1d55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -60,7 +60,8 @@
mSensorManager = new FakeSensorManager(mContext);
mSensor = mSensorManager.getFakeLightSensor();
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- mSensor.getSensor(), mHostFake, null /* handler */);
+ mSensor.getSensor(), mHostFake, null /* handler */,
+ new AlwaysOnDisplayPolicy(mContext));
}
@Test
@@ -135,7 +136,8 @@
@Test
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- null /* sensor */, mHostFake, null /* handler */);
+ null /* sensor */, mHostFake, null /* handler */,
+ new AlwaysOnDisplayPolicy(mContext));
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
index b8e9fcd..94dbc2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
@@ -67,7 +67,7 @@
public void setup() throws Exception {
mDependency.injectTestDependency(Dependency.BG_LOOPER,
TestableLooper.get(this).getLooper());
- mRealExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
+ mRealExceptionHandler = Thread.getUncaughtExceptionPreHandler();
mMockExceptionHandler = mock(UncaughtExceptionHandler.class);
mMockFactory = mock(PluginInstanceManagerFactory.class);
mMockPluginInstance = mock(PluginInstanceManager.class);
@@ -167,9 +167,9 @@
}
private void resetExceptionHandler() {
- mPluginExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
+ mPluginExceptionHandler = Thread.getUncaughtExceptionPreHandler();
// Set back the real exception handler so the test can crash if it wants to.
- Thread.setDefaultUncaughtExceptionHandler(mRealExceptionHandler);
+ Thread.setUncaughtExceptionPreHandler(mRealExceptionHandler);
}
@ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
index 9ec096a..44c4983 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -93,4 +93,9 @@
public int getBondState(CachedBluetoothDevice device) {
return 0;
}
+
+ @Override
+ public CachedBluetoothDevice getLastDevice() {
+ return null;
+ }
}
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index c4dc506..28bf856 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -312,6 +312,9 @@
// Counts the number of AllSingleScanLister.onResult calls with a partial (channels) scan result
optional int32 partial_all_single_scan_listener_results = 75;
+
+ // Pno scan metrics
+ optional PnoScanMetrics pno_scan_metrics = 76;
}
// Information that gets logged for every WiFi connection.
@@ -927,3 +930,23 @@
// Number of scan results with num_connectable_networks
optional int32 count = 2 [default = 0];
}
+
+// Pno scan metrics
+// Here "Pno Scan" refers to the session of offloaded scans, these metrics count the result of a
+// single session, and not the individual scans within that session.
+message PnoScanMetrics {
+ // Total number of attempts to offload pno scans
+ optional int32 num_pno_scan_attempts = 1;
+
+ // Total number of pno scans failed
+ optional int32 num_pno_scan_failed = 2;
+
+ // Number of pno scans started successfully over offload
+ optional int32 num_pno_scan_started_over_offload = 3;
+
+ // Number of pno scans failed over offload
+ optional int32 num_pno_scan_failed_over_offload = 4;
+
+ // Total number of pno scans that found any network
+ optional int32 num_pno_found_network_events = 5;
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index a97e16b..83dfccb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2397,6 +2397,7 @@
public static final int MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS = 12;
public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13;
public static final int MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER = 14;
+ public static final int MSG_INIT_SERVICE = 15;
public MainHandler(Looper looper) {
super(looper);
@@ -2495,6 +2496,11 @@
case MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER: {
showAccessibilityButtonTargetSelection();
} break;
+
+ case MSG_INIT_SERVICE: {
+ final Service service = (Service) msg.obj;
+ service.initializeService();
+ } break;
}
}
@@ -2950,20 +2956,31 @@
if (userState.mBindingServices.contains(mComponentName) || mWasConnectedAndDied) {
userState.mBindingServices.remove(mComponentName);
mWasConnectedAndDied = false;
- try {
- mServiceInterface.init(this, mId, mOverlayWindowToken);
- onUserStateChangedLocked(userState);
- } catch (RemoteException re) {
- Slog.w(LOG_TAG, "Error while setting connection for service: "
- + service, re);
- binderDied();
- }
+ onUserStateChangedLocked(userState);
+ // Initialize the service on the main handler after we're done setting up for
+ // the new configuration (for example, initializing the input filter).
+ mMainHandler.obtainMessage(MainHandler.MSG_INIT_SERVICE, this).sendToTarget();
} else {
binderDied();
}
}
}
+ private void initializeService() {
+ final IAccessibilityServiceClient serviceInterface;
+ synchronized (mLock) {
+ serviceInterface = mServiceInterface;
+ }
+ if (serviceInterface == null) return;
+ try {
+ serviceInterface.init(this, mId, mOverlayWindowToken);
+ } catch (RemoteException re) {
+ Slog.w(LOG_TAG, "Error while setting connection for service: "
+ + serviceInterface, re);
+ binderDied();
+ }
+ }
+
private boolean isCalledForCurrentUserLocked() {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -3313,8 +3330,8 @@
}
if (mMotionEventInjector != null) {
List<GestureDescription.GestureStep> steps = gestureSteps.getList();
- mMotionEventInjector.injectEvents(steps, mServiceInterface, sequence);
- return;
+ mMotionEventInjector.injectEvents(steps, mServiceInterface, sequence);
+ return;
} else {
Slog.e(LOG_TAG, "MotionEventInjector installation timed out");
}
@@ -3456,18 +3473,15 @@
return region;
}
MagnificationController magnificationController = getMagnificationController();
- boolean forceRegistration = mSecurityPolicy.canControlMagnification(this);
- boolean initiallyRegistered = magnificationController.isRegisteredLocked();
- if (!initiallyRegistered && forceRegistration) {
- magnificationController.register();
- }
+ boolean registeredJustForThisCall =
+ registerMagnificationIfNeeded(magnificationController);
final long identity = Binder.clearCallingIdentity();
try {
magnificationController.getMagnificationRegion(region);
return region;
} finally {
Binder.restoreCallingIdentity(identity);
- if (!initiallyRegistered && forceRegistration) {
+ if (registeredJustForThisCall) {
magnificationController.unregister();
}
}
@@ -3481,11 +3495,17 @@
return 0.0f;
}
}
+ MagnificationController magnificationController = getMagnificationController();
+ boolean registeredJustForThisCall =
+ registerMagnificationIfNeeded(magnificationController);
final long identity = Binder.clearCallingIdentity();
try {
- return getMagnificationController().getCenterX();
+ return magnificationController.getCenterX();
} finally {
Binder.restoreCallingIdentity(identity);
+ if (registeredJustForThisCall) {
+ magnificationController.unregister();
+ }
}
}
@@ -3496,14 +3516,30 @@
return 0.0f;
}
}
+ MagnificationController magnificationController = getMagnificationController();
+ boolean registeredJustForThisCall =
+ registerMagnificationIfNeeded(magnificationController);
final long identity = Binder.clearCallingIdentity();
try {
- return getMagnificationController().getCenterY();
+ return magnificationController.getCenterY();
} finally {
Binder.restoreCallingIdentity(identity);
+ if (registeredJustForThisCall) {
+ magnificationController.unregister();
+ }
}
}
+ private boolean registerMagnificationIfNeeded(
+ MagnificationController magnificationController) {
+ if (!magnificationController.isRegisteredLocked()
+ && mSecurityPolicy.canControlMagnification(this)) {
+ magnificationController.register();
+ return true;
+ }
+ return false;
+ }
+
@Override
public boolean resetMagnification(boolean animate) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 17292b4..adf536b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2011,7 +2011,16 @@
break;
}
case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
- handleUpdateLinkProperties(nai, (LinkProperties) msg.obj);
+ if (VDBG) {
+ log("Update of LinkProperties for " + nai.name() +
+ "; created=" + nai.created +
+ "; everConnected=" + nai.everConnected);
+ }
+ LinkProperties oldLp = nai.linkProperties;
+ synchronized (nai) {
+ nai.linkProperties = (LinkProperties)msg.obj;
+ }
+ if (nai.everConnected) updateLinkProperties(nai, oldLp);
break;
}
case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
@@ -2260,7 +2269,7 @@
}
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
- nai.maybeStopClat();
+ maybeStopClat(nai);
synchronized (mNetworkForNetId) {
// Remove the NetworkAgent, but don't mark the netId as
// available until we've told netd to delete it below.
@@ -4374,7 +4383,7 @@
updateDnses(newLp, oldLp, netId);
// Start or stop clat accordingly to network state.
- networkAgent.updateClat(mNetd);
+ updateClat(networkAgent);
if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
} else {
@@ -4389,6 +4398,32 @@
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
}
+ private void updateClat(NetworkAgentInfo nai) {
+ if (Nat464Xlat.requiresClat(nai)) {
+ maybeStartClat(nai);
+ } else {
+ maybeStopClat(nai);
+ }
+ }
+
+ /** Ensure clat has started for this network. */
+ private void maybeStartClat(NetworkAgentInfo nai) {
+ if (nai.clatd != null && nai.clatd.isStarted()) {
+ return;
+ }
+ nai.clatd = new Nat464Xlat(mNetd, mTrackerHandler, nai);
+ nai.clatd.start();
+ }
+
+ /** Ensure clat has stopped for this network. */
+ private void maybeStopClat(NetworkAgentInfo nai) {
+ if (nai.clatd == null) {
+ return;
+ }
+ nai.clatd.stop();
+ nai.clatd = null;
+ }
+
private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
// Marks are only available on WiFi interaces. Checking for
// marks on unsupported interfaces is harmless.
@@ -4623,21 +4658,6 @@
}
}
- public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
- if (VDBG) {
- log("Update of LinkProperties for " + nai.name() +
- "; created=" + nai.created +
- "; everConnected=" + nai.everConnected);
- }
- LinkProperties oldLp = nai.linkProperties;
- synchronized (nai) {
- nai.linkProperties = newLp;
- }
- if (nai.everConnected) {
- updateLinkProperties(nai, oldLp);
- }
- }
-
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 4733840..046eb76 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -728,6 +728,9 @@
return timeout;
}
}
+ if (!prebaked.shouldFallback()) {
+ return 0;
+ }
final int id = prebaked.getId();
if (id < 0 || id >= mFallbackEffects.length || mFallbackEffects[id] == null) {
Slog.w(TAG, "Failed to play prebaked effect, no fallback");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9727092..e7d1ee8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -466,7 +466,6 @@
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
- private static final String TAG_LOCKSCREEN = TAG + POSTFIX_LOCKSCREEN;
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
private static final String TAG_LRU = TAG + POSTFIX_LRU;
private static final String TAG_MU = TAG + POSTFIX_MU;
@@ -484,7 +483,6 @@
private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION;
private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
- private static final String TAG_VISIBLE_BEHIND = TAG + POSTFIX_VISIBLE_BEHIND;
// Mock "pretend we're idle now" broadcast action to the job scheduler; declared
// here so that while the job scheduler can depend on AMS, the other way around
@@ -1562,6 +1560,13 @@
final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>();
final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>();
+ OomAdjObserver mCurOomAdjObserver;
+ int mCurOomAdjUid;
+
+ interface OomAdjObserver {
+ void onOomAdjMessage(String msg);
+ }
+
/**
* Runtime CPU use collection thread. This object's lock is used to
* perform synchronization with the thread (notifying it to run).
@@ -1683,6 +1688,7 @@
static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
+ static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
static final int START_USER_SWITCH_FG_MSG = 712;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
@@ -1918,6 +1924,9 @@
case DISPATCH_UIDS_CHANGED_UI_MSG: {
dispatchUidsChanged();
} break;
+ case DISPATCH_OOM_ADJ_OBSERVER_MSG: {
+ dispatchOomAdjObserver((String)msg.obj);
+ } break;
case PUSH_TEMP_WHITELIST_UI_MSG: {
pushTempWhitelist();
} break;
@@ -4456,6 +4465,38 @@
}
}
+ void dispatchOomAdjObserver(String msg) {
+ OomAdjObserver observer;
+ synchronized (this) {
+ observer = mCurOomAdjObserver;
+ }
+
+ if (observer != null) {
+ observer.onOomAdjMessage(msg);
+ }
+ }
+
+ void setOomAdjObserver(int uid, OomAdjObserver observer) {
+ synchronized (this) {
+ mCurOomAdjUid = uid;
+ mCurOomAdjObserver = observer;
+ }
+ }
+
+ void clearOomAdjObserver() {
+ synchronized (this) {
+ mCurOomAdjUid = -1;
+ mCurOomAdjObserver = null;
+ }
+ }
+
+ void reportOomAdjMessageLocked(String tag, String msg) {
+ Slog.d(tag, msg);
+ if (mCurOomAdjObserver != null) {
+ mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget();
+ }
+ }
+
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
@@ -13350,7 +13391,7 @@
synchronized (this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- final ActivityOptions activityOptions = r.pendingOptions;
+ final ActivityOptions activityOptions = r.takeOptionsLocked();
return activityOptions == null ? null : activityOptions.toBundle();
}
return null;
@@ -21937,10 +21978,12 @@
int changes = 0;
if (app.curAdj != app.setAdj) {
- ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj);
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Set " + app.pid + " " + app.processName + " adj " + app.curAdj + ": "
- + app.adjType);
+ ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.info.uid) {
+ String msg = "Set " + app.pid + " " + app.processName + " adj "
+ + app.curAdj + ": " + app.adjType;
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
+ }
app.setAdj = app.curAdj;
app.verifiedAdj = ProcessList.INVALID_ADJ;
}
@@ -21948,9 +21991,11 @@
if (app.setSchedGroup != app.curSchedGroup) {
int oldSchedGroup = app.setSchedGroup;
app.setSchedGroup = app.curSchedGroup;
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Setting sched group of " + app.processName
- + " to " + app.curSchedGroup);
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
+ String msg = "Setting sched group of " + app.processName
+ + " to " + app.curSchedGroup;
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
+ }
if (app.waitingToKill != null && app.curReceivers.isEmpty()
&& app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
app.kill(app.waitingToKill, true);
@@ -22087,9 +22132,11 @@
"Not requesting PSS of " + app + ": next=" + (app.nextPssTime-now));
}
if (app.setProcState != app.curProcState) {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Proc state change of " + app.processName
- + " to " + app.curProcState);
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) {
+ String msg = "Proc state change of " + app.processName
+ + " to " + app.curProcState;
+ reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
+ }
boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE;
boolean curImportant = app.curProcState < ActivityManager.PROCESS_STATE_SERVICE;
if (setImportant && !curImportant) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 45357cb..8488e52 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -55,7 +55,6 @@
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.DisplayMetrics;
-import android.view.IWindowManager;
import com.android.internal.util.HexDump;
import com.android.internal.util.Preconditions;
@@ -1296,19 +1295,24 @@
return 0;
}
- static final class MyUidObserver extends IUidObserver.Stub {
+ static final class MyUidObserver extends IUidObserver.Stub
+ implements ActivityManagerService.OomAdjObserver {
final IActivityManager mInterface;
+ final ActivityManagerService mInternal;
final PrintWriter mPw;
final InputStream mInput;
+ final int mUid;
static final int STATE_NORMAL = 0;
int mState;
- MyUidObserver(IActivityManager iam, PrintWriter pw, InputStream input) {
- mInterface = iam;
+ MyUidObserver(ActivityManagerService service, PrintWriter pw, InputStream input, int uid) {
+ mInterface = service;
+ mInternal = service;
mPw = pw;
mInput = input;
+ mUid = uid;
}
@Override
@@ -1367,6 +1371,15 @@
}
}
+ @Override
+ public void onOomAdjMessage(String msg) {
+ synchronized (this) {
+ mPw.print("# ");
+ mPw.println(msg);
+ mPw.flush();
+ }
+ }
+
void printMessageForState() {
switch (mState) {
case STATE_NORMAL:
@@ -1385,6 +1398,9 @@
| ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_PROCSTATE
| ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_CACHED,
ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ if (mUid >= 0) {
+ mInternal.setOomAdjObserver(mUid, this);
+ }
mState = STATE_NORMAL;
InputStreamReader converter = new InputStreamReader(mInput);
@@ -1414,6 +1430,9 @@
e.printStackTrace(mPw);
mPw.flush();
} finally {
+ if (mUid >= 0) {
+ mInternal.clearOomAdjObserver();
+ }
mInterface.unregisterUidObserver(this);
}
}
@@ -1421,12 +1440,18 @@
int runWatchUids(PrintWriter pw) throws RemoteException {
String opt;
+ int uid = -1;
while ((opt=getNextOption()) != null) {
- getErrPrintWriter().println("Error: Unknown option: " + opt);
- return -1;
+ if (opt.equals("--oom")) {
+ uid = Integer.parseInt(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ return -1;
+
+ }
}
- MyUidObserver controller = new MyUidObserver(mInterface, pw, getRawInputStream());
+ MyUidObserver controller = new MyUidObserver(mInternal, pw, getRawInputStream(), uid);
controller.run();
return 0;
}
@@ -1858,8 +1883,12 @@
level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
break;
default:
- getErrPrintWriter().println("Error: Unknown level option: " + levelArg);
- return -1;
+ try {
+ level = Integer.parseInt(levelArg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: Unknown level option: " + levelArg);
+ return -1;
+ }
}
if (!mInterface.setProcessMemoryTrimLevel(proc, userId, level)) {
getErrPrintWriter().println("Unknown error: failed to set trim level");
@@ -2744,8 +2773,9 @@
pw.println(" monitor [--gdb <port>]");
pw.println(" Start monitoring for crashes or ANRs.");
pw.println(" --gdb: start gdbserv on the given port at crash/ANR");
- pw.println(" watch-uids [--gdb <port>]");
+ pw.println(" watch-uids [--oom <uid>");
pw.println(" Start watching for and reporting uid state changes.");
+ pw.println(" --oom: specify a uid for which to report detailed change messages.");
pw.println(" hang [--allow-restart]");
pw.println(" Hang the system.");
pw.println(" --allow-restart: allow watchdog to perform normal system restart");
@@ -2804,7 +2834,7 @@
pw.println(" Returns the inactive state of an app.");
pw.println(" send-trim-memory [--user <USER_ID>] <PROCESS>");
pw.println(" [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]");
- pw.println(" Send a memory trim event to a <PROCESS>.");
+ pw.println(" Send a memory trim event to a <PROCESS>. May also supply a raw trim int level.");
pw.println(" display [COMMAND] [...]: sub-commands for operating on displays.");
pw.println(" move-stack <STACK_ID> <DISPLAY_ID>");
pw.println(" Move <STACK_ID> from its current display to <DISPLAY_ID>.");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 871ccb9..5b27c9c 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -659,14 +659,14 @@
}
}
- void updatePictureInPictureMode(Rect targetStackBounds) {
+ void updatePictureInPictureMode(Rect targetStackBounds, boolean forceUpdate) {
if (task == null || task.getStack() == null || app == null || app.thread == null) {
return;
}
final boolean inPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID) &&
(targetStackBounds != null);
- if (inPictureInPictureMode != mLastReportedPictureInPictureMode) {
+ if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
// Picture-in-picture mode changes also trigger a multi-window mode change as well, so
// update that here in order
mLastReportedPictureInPictureMode = inPictureInPictureMode;
@@ -681,8 +681,7 @@
private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
try {
app.thread.schedulePictureInPictureModeChanged(appToken,
- mLastReportedPictureInPictureMode,
- overrideConfig);
+ mLastReportedPictureInPictureMode, overrideConfig);
} catch (Exception e) {
// If process died, no one cares.
}
@@ -1592,7 +1591,12 @@
}
void notifyUnknownVisibilityLaunched() {
- mWindowContainerController.notifyUnknownVisibilityLaunched();
+
+ // No display activities never add a window, so there is no point in waiting them for
+ // relayout.
+ if (!noDisplay) {
+ mWindowContainerController.notifyUnknownVisibilityLaunched();
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d16ae185..7bbc6cc 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1336,6 +1336,10 @@
r.app = app;
+ if (mKeyguardController.isKeyguardLocked()) {
+ r.notifyUnknownVisibilityLaunched();
+ }
+
// Have the window manager re-evaluate the orientation of the screen based on the new
// activity order. Note that as a result of this, it can call back into the activity
// manager with a new orientation. We don't care about that, because the activity is
@@ -1362,9 +1366,6 @@
r.setVisibility(true);
}
- if (mKeyguardController.isKeyguardLocked()) {
- r.notifyUnknownVisibilityLaunched();
- }
final int applicationInfoUid =
(r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
@@ -4410,31 +4411,29 @@
return;
}
- scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds, false /* immediate */);
+ scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds);
}
- void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds,
- boolean immediate) {
-
- if (immediate) {
- mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
- for (int i = task.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = task.mActivities.get(i);
- if (r.app != null && r.app.thread != null) {
- r.updatePictureInPictureMode(targetStackBounds);
- }
+ void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
+ for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = task.mActivities.get(i);
+ if (r.app != null && r.app.thread != null) {
+ mPipModeChangedActivities.add(r);
}
- } else {
- for (int i = task.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = task.mActivities.get(i);
- if (r.app != null && r.app.thread != null) {
- mPipModeChangedActivities.add(r);
- }
- }
- mPipModeChangedTargetStackBounds = targetStackBounds;
+ }
+ mPipModeChangedTargetStackBounds = targetStackBounds;
- if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
- mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
+ if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
+ mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
+ }
+ }
+
+ void updatePictureInPictureMode(TaskRecord task, Rect targetStackBounds, boolean forceUpdate) {
+ mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
+ for (int i = task.mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = task.mActivities.get(i);
+ if (r.app != null && r.app.thread != null) {
+ r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
}
}
}
@@ -4496,7 +4495,8 @@
synchronized (mService) {
for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mPipModeChangedActivities.remove(i);
- r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds);
+ r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
+ false /* forceUpdate */);
}
}
} break;
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index 392fbb2..a601ee1 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -91,15 +91,16 @@
return mWindowContainerController.deferScheduleMultiWindowModeChanged();
}
- public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
+ public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+ boolean forceUpdate) {
// It is guaranteed that the activities requiring the update will be in the pinned stack at
// this point (either reparented before the animation into PiP, or before reparenting after
// the animation out of PiP)
synchronized(this) {
ArrayList<TaskRecord> tasks = getAllTasks();
for (int i = 0; i < tasks.size(); i++ ) {
- mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(tasks.get(i),
- targetStackBounds, true /* immediate */);
+ mStackSupervisor.updatePictureInPictureMode(tasks.get(i), targetStackBounds,
+ forceUpdate);
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index faf4729..91b1591 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -801,12 +801,23 @@
public void systemReady() {
sendMsg(mAudioHandler, MSG_SYSTEM_READY, SENDMSG_QUEUE,
0, 0, null, 0);
- try {
- ActivityManager.getService().registerUidObserver(mUidObserver,
- ActivityManager.UID_OBSERVER_CACHED | ActivityManager.UID_OBSERVER_GONE,
- ActivityManager.PROCESS_STATE_UNKNOWN, null);
- } catch (RemoteException e) {
- // ignored; both services live in system_server
+ if (false) {
+ // This is turned off for now, because it is racy and thus causes apps to break.
+ // Currently banning a uid means that if an app tries to start playing an audio
+ // stream, that will be preventing, and unbanning it will not allow that stream
+ // to resume. However these changes in uid state are racy with what the app is doing,
+ // so that after taking a process out of the cached state we can't guarantee that
+ // we will unban the uid before the app actually tries to start playing audio.
+ // (To do that, the activity manager would need to wait until it knows for sure
+ // that the ban has been removed, before telling the app to do whatever it is
+ // supposed to do that caused it to go out of the cached state.)
+ try {
+ ActivityManager.getService().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_CACHED | ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
}
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 10c8b8b..f8d23d4 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -20,21 +20,22 @@
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NetworkAgent;
import android.net.RouteInfo;
+import android.os.Handler;
+import android.os.Message;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.internal.util.ArrayUtils;
import com.android.server.net.BaseNetworkObserver;
+import com.android.internal.util.ArrayUtils;
import java.net.Inet4Address;
import java.util.Objects;
/**
- * Class to manage a 464xlat CLAT daemon. Nat464Xlat is not thread safe and should be manipulated
- * from a consistent and unique thread context. It is the responsability of ConnectivityService to
- * call into this class from its own Handler thread.
+ * Class to manage a 464xlat CLAT daemon.
*
* @hide
*/
@@ -54,6 +55,9 @@
private final INetworkManagementService mNMService;
+ // ConnectivityService Handler for LinkProperties updates.
+ private final Handler mHandler;
+
// The network we're running on, and its type.
private final NetworkAgentInfo mNetwork;
@@ -63,12 +67,16 @@
RUNNING; // start() called, and the stacked iface is known to be up.
}
+ // Once mIface is non-null and isStarted() is true, methods called by ConnectivityService on
+ // its handler thread must not modify any internal state variables; they are only updated by the
+ // interface observers, called on the notification threads.
private String mBaseIface;
private String mIface;
- private State mState = State.IDLE;
+ private volatile State mState = State.IDLE;
- public Nat464Xlat(INetworkManagementService nmService, NetworkAgentInfo nai) {
+ public Nat464Xlat(INetworkManagementService nmService, Handler handler, NetworkAgentInfo nai) {
mNMService = nmService;
+ mHandler = handler;
mNetwork = nai;
}
@@ -97,13 +105,6 @@
}
/**
- * @return true if clatd has been started but the stacked interface is not yet up.
- */
- public boolean isStarting() {
- return mState == State.STARTING;
- }
-
- /**
* @return true if clatd has been started and the stacked interface is up.
*/
public boolean isRunning() {
@@ -120,7 +121,7 @@
}
/**
- * Clears internal state.
+ * Clears internal state. Must not be called by ConnectivityService.
*/
private void enterIdleState() {
mIface = null;
@@ -129,7 +130,7 @@
}
/**
- * Starts the clat daemon.
+ * Starts the clat daemon. Called by ConnectivityService on the handler thread.
*/
public void start() {
if (isStarted()) {
@@ -166,7 +167,7 @@
}
/**
- * Stops the clat daemon.
+ * Stops the clat daemon. Called by ConnectivityService on the handler thread.
*/
public void stop() {
if (!isStarted()) {
@@ -180,8 +181,15 @@
} catch(RemoteException|IllegalStateException e) {
Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
}
- // When clatd stops and its interface is deleted, handleInterfaceRemoved() will trigger
- // ConnectivityService#handleUpdateLinkProperties and call enterIdleState().
+ // When clatd stops and its interface is deleted, interfaceRemoved() will notify
+ // ConnectivityService and call enterIdleState().
+ }
+
+ private void updateConnectivityService(LinkProperties lp) {
+ Message msg = mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, lp);
+ msg.replyTo = mNetwork.messenger;
+ Slog.i(TAG, "sending message to ConnectivityService: " + msg);
+ msg.sendToTarget();
}
/**
@@ -249,15 +257,19 @@
}
/**
- * Adds stacked link on base link and transitions to RUNNING state.
+ * Adds stacked link on base link and transitions to Running state
+ * This is called by the InterfaceObserver on its own thread, so can race with stop().
*/
- private void handleInterfaceLinkStateChanged(String iface, boolean up) {
- if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
+ @Override
+ public void interfaceLinkStateChanged(String iface, boolean up) {
+ if (!isStarted() || !up || !Objects.equals(mIface, iface)) {
+ return;
+ }
+ if (isRunning()) {
return;
}
LinkAddress clatAddress = getLinkAddress(iface);
if (clatAddress == null) {
- Slog.e(TAG, "cladAddress was null for stacked iface " + iface);
return;
}
mState = State.RUNNING;
@@ -267,14 +279,15 @@
maybeSetIpv6NdOffload(mBaseIface, false);
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
lp.addStackedLink(makeLinkProperties(clatAddress));
- mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
+ updateConnectivityService(lp);
}
- /**
- * Removes stacked link on base link and transitions to IDLE state.
- */
- private void handleInterfaceRemoved(String iface) {
- if (!isRunning() || !Objects.equals(mIface, iface)) {
+ @Override
+ public void interfaceRemoved(String iface) {
+ if (!isStarted() || !Objects.equals(mIface, iface)) {
+ return;
+ }
+ if (!isRunning()) {
return;
}
@@ -282,28 +295,21 @@
// The interface going away likely means clatd has crashed. Ask netd to stop it,
// because otherwise when we try to start it again on the same base interface netd
// will complain that it's already started.
+ //
+ // Note that this method can be called by the interface observer at the same time
+ // that ConnectivityService calls stop(). In this case, the second call to
+ // stopClatd() will just throw IllegalStateException, which we'll ignore.
try {
mNMService.unregisterObserver(this);
- // TODO: add STOPPING state to avoid calling stopClatd twice.
mNMService.stopClatd(mBaseIface);
- } catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
+ } catch (RemoteException|IllegalStateException e) {
+ // Well, we tried.
}
maybeSetIpv6NdOffload(mBaseIface, true);
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
lp.removeStackedLink(mIface);
enterIdleState();
- mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
- }
-
- @Override
- public void interfaceLinkStateChanged(String iface, boolean up) {
- mNetwork.handler.post(() -> { handleInterfaceLinkStateChanged(iface, up); });
- }
-
- @Override
- public void interfaceRemoved(String iface) {
- mNetwork.handler.post(() -> { handleInterfaceRemoved(iface); });
+ updateConnectivityService(lp);
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 7c4ef0f..872923a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -27,9 +27,7 @@
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.os.Handler;
-import android.os.INetworkManagementService;
import android.os.Messenger;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -249,9 +247,9 @@
private static final String TAG = ConnectivityService.class.getSimpleName();
private static final boolean VDBG = false;
- public final ConnectivityService connService;
+ private final ConnectivityService mConnService;
private final Context mContext;
- final Handler handler;
+ private final Handler mHandler;
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
@@ -263,10 +261,10 @@
linkProperties = lp;
networkCapabilities = nc;
currentScore = score;
- this.connService = connService;
+ mConnService = connService;
mContext = context;
- this.handler = handler;
- networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
+ mHandler = handler;
+ networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
}
@@ -432,7 +430,7 @@
private boolean ignoreWifiUnvalidationPenalty() {
boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- boolean avoidBadWifi = connService.avoidBadWifi() || avoidUnvalidated;
+ boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated;
return isWifi && !avoidBadWifi && everValidated;
}
@@ -516,8 +514,8 @@
}
if (newExpiry > 0) {
- mLingerMessage = connService.makeWakeupMessage(
- mContext, handler,
+ mLingerMessage = mConnService.makeWakeupMessage(
+ mContext, mHandler,
"NETWORK_LINGER_COMPLETE." + network.netId,
EVENT_NETWORK_LINGER_COMPLETE, this);
mLingerMessage.schedule(newExpiry);
@@ -553,32 +551,6 @@
for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
}
- public void updateClat(INetworkManagementService netd) {
- if (Nat464Xlat.requiresClat(this)) {
- maybeStartClat(netd);
- } else {
- maybeStopClat();
- }
- }
-
- /** Ensure clat has started for this network. */
- public void maybeStartClat(INetworkManagementService netd) {
- if (clatd != null && clatd.isStarted()) {
- return;
- }
- clatd = new Nat464Xlat(netd, this);
- clatd.start();
- }
-
- /** Ensure clat has stopped for this network. */
- public void maybeStopClat() {
- if (clatd == null) {
- return;
- }
- clatd.stop();
- clatd = null;
- }
-
public String toString() {
return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
"network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java
index 026921d..c4911b8 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/NightDisplayService.java
@@ -52,6 +52,8 @@
import java.util.Calendar;
import java.util.TimeZone;
+import com.android.internal.R;
+
import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
/**
@@ -110,19 +112,7 @@
private float[] mMatrixNight = new float[16];
- /**
- * The 3x3 color transformation matrix is formatted like so:
- * <table>
- * <tr><td>R: a coefficient</td><td>G: a coefficient</td><td>B: a coefficient</td></tr>
- * <tr><td>R: b coefficient</td><td>G: b coefficient</td><td>B: b coefficient</td></tr>
- * <tr><td>R: y-intercept</td><td>G: y-intercept</td><td>B: y-intercept</td></tr>
- * </table>
- */
- private static final float[] mColorTempCoefficients = new float[] {
- 0.0f, -0.000000014365268757f, -0.000000000910931179f,
- 0.0f, 0.000255092801250106f, 0.000207598323269139f,
- 1.0f, -0.064156942434907716f, -0.349361641294833436f
- };
+ private final float[] mColorTempCoefficients = new float[9];
private int mCurrentUser = UserHandle.USER_NULL;
private ContentObserver mUserSetupObserver;
@@ -136,6 +126,12 @@
public NightDisplayService(Context context) {
super(context);
mHandler = new Handler(Looper.getMainLooper());
+
+ final String[] coefficients = context.getResources().getStringArray(
+ R.array.config_nightDisplayColorTemperatureCoefficients);
+ for (int i = 0; i < 9 && i < coefficients.length; i++) {
+ mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
+ }
}
@Override
@@ -410,11 +406,11 @@
final float squareTemperature = colorTemperature * colorTemperature;
final float red = squareTemperature * mColorTempCoefficients[0]
- + colorTemperature * mColorTempCoefficients[3] + mColorTempCoefficients[6];
- final float green = squareTemperature * mColorTempCoefficients[1]
- + colorTemperature * mColorTempCoefficients[4] + mColorTempCoefficients[7];
- final float blue = squareTemperature * mColorTempCoefficients[2]
- + colorTemperature * mColorTempCoefficients[5] + mColorTempCoefficients[8];
+ + colorTemperature * mColorTempCoefficients[1] + mColorTempCoefficients[2];
+ final float green = squareTemperature * mColorTempCoefficients[3]
+ + colorTemperature * mColorTempCoefficients[4] + mColorTempCoefficients[5];
+ final float blue = squareTemperature * mColorTempCoefficients[6]
+ + colorTemperature * mColorTempCoefficients[7] + mColorTempCoefficients[8];
outTemp[0] = red;
outTemp[5] = green;
outTemp[10] = blue;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 3fc76c0..4dab32c 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -501,11 +501,12 @@
if (DEBUG) {
Slog.d(TAG, "Receieved: " + action);
}
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+
if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
// Purge the app's jobs if the whole package was just disabled. When this is
// the case the component name will be a bare package name.
- final String pkgName = getPackageName(intent);
- final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (pkgName != null && pkgUid != -1) {
final String[] changedComponents = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
@@ -525,7 +526,8 @@
Slog.d(TAG, "Removing jobs for package " + pkgName
+ " in user " + userId);
}
- cancelJobsForUid(pkgUid, "app package state changed");
+ cancelJobsForPackageAndUid(pkgName, pkgUid,
+ "app disabled");
}
} catch (RemoteException|IllegalArgumentException e) {
/*
@@ -554,7 +556,7 @@
if (DEBUG) {
Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
}
- cancelJobsForUid(uidRemoved, "app uninstalled");
+ cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
}
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -565,8 +567,6 @@
} else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
// Has this package scheduled any jobs, such that we will take action
// if it were to be force-stopped?
- final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- final String pkgName = intent.getData().getSchemeSpecificPart();
if (pkgUid != -1) {
List<JobStatus> jobsForUid;
synchronized (mLock) {
@@ -585,13 +585,11 @@
}
} else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
// possible force-stop
- final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- final String pkgName = intent.getData().getSchemeSpecificPart();
if (pkgUid != -1) {
if (DEBUG) {
Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
}
- cancelJobsForPackageAndUid(pkgName, pkgUid);
+ cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
}
}
}
@@ -762,13 +760,17 @@
}
}
- void cancelJobsForPackageAndUid(String pkgName, int uid) {
+ void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
+ if ("android".equals(pkgName)) {
+ Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
+ return;
+ }
synchronized (mLock) {
final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
for (int i = jobsForUid.size() - 1; i >= 0; i--) {
final JobStatus job = jobsForUid.get(i);
if (job.getSourcePackageName().equals(pkgName)) {
- cancelJobImplLocked(job, null, "app force stopped");
+ cancelJobImplLocked(job, null, reason);
}
}
}
@@ -783,8 +785,7 @@
*/
public void cancelJobsForUid(int uid, String reason) {
if (uid == Process.SYSTEM_UID) {
- // This really shouldn't happen.
- Slog.wtfStack(TAG, "cancelJobsForUid() called for system uid");
+ Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
return;
}
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 031bdd0..d3fd3a9 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -261,6 +261,13 @@
return mRunningJob;
}
+ /**
+ * Used only for debugging. Will return <code>"<null>"</code> if there is no job running.
+ */
+ private String getRunningJobNameLocked() {
+ return mRunningJob != null ? mRunningJob.toShortString() : "<null>";
+ }
+
/** Called externally when a job that was scheduled for execution should be cancelled. */
void cancelExecutingJobLocked(int reason, String debugReason) {
doCancelLocked(reason, debugReason);
@@ -522,7 +529,7 @@
/** Start the job on the service. */
private void handleServiceBoundLocked() {
if (DEBUG) {
- Slog.d(TAG, "handleServiceBound for " + mRunningJob.toShortString());
+ Slog.d(TAG, "handleServiceBound for " + getRunningJobNameLocked());
}
if (mVerb != VERB_BINDING) {
Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
@@ -639,36 +646,34 @@
private void handleOpTimeoutLocked() {
switch (mVerb) {
case VERB_BINDING:
- Slog.w(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
- ", dropping.");
+ Slog.w(TAG, "Time-out while trying to bind " + getRunningJobNameLocked()
+ + ", dropping.");
closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while binding");
break;
case VERB_STARTING:
// Client unresponsive - wedged or failed to respond in time. We don't really
// know what happened so let's log it and notify the JobScheduler
// FINISHED/NO-RETRY.
- Slog.w(TAG, "No response from client for onStartJob " +
- mRunningJob != null ? mRunningJob.toShortString() : "<null>");
+ Slog.w(TAG, "No response from client for onStartJob "
+ + getRunningJobNameLocked());
closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting");
break;
case VERB_STOPPING:
// At least we got somewhere, so fail but ask the JobScheduler to reschedule.
- Slog.w(TAG, "No response from client for onStopJob " +
- mRunningJob != null ? mRunningJob.toShortString() : "<null>");
+ Slog.w(TAG, "No response from client for onStopJob "
+ + getRunningJobNameLocked());
closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
break;
case VERB_EXECUTING:
// Not an error - client ran out of time.
Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
- "sending onStop: " +
- mRunningJob != null ? mRunningJob.toShortString() : "<null>");
+ "sending onStop: " + getRunningJobNameLocked());
mParams.setStopReason(JobParameters.REASON_TIMEOUT);
sendStopMessageLocked("timeout while executing");
break;
default:
- Slog.e(TAG, "Handling timeout for an invalid job state: " +
- mRunningJob != null ? mRunningJob.toShortString() : "<null>"
- + ", dropping.");
+ Slog.e(TAG, "Handling timeout for an invalid job state: "
+ + getRunningJobNameLocked() + ", dropping.");
closeAndCleanupJobLocked(false /* needsReschedule */, "invalid timeout");
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 5927b2f..a1b8456 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -74,6 +74,7 @@
import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.security.keystore.UserNotAuthenticatedException;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
@@ -503,12 +504,34 @@
maybeShowEncryptionNotificationForUser(userId);
}
+ /**
+ * Check if profile got unlocked but the keystore is still locked. This happens on full disk
+ * encryption devices since the profile may not yet be running when we consider unlocking it
+ * during the normal flow. In this case unlock the keystore for the profile.
+ */
+ private void ensureProfileKeystoreUnlocked(int userId) {
+ final KeyStore ks = KeyStore.getInstance();
+ if (ks.state(userId) == KeyStore.State.LOCKED
+ && tiedManagedProfileReadyToUnlock(mUserManager.getUserInfo(userId))) {
+ Slog.i(TAG, "Managed profile got unlocked, will unlock its keystore");
+ try {
+ // If boot took too long and the password in vold got expired, parent keystore will
+ // be still locked, we ignore this case since the user will be prompted to unlock
+ // the device after boot.
+ unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unlock child profile");
+ }
+ }
+ }
+
public void onUnlockUser(final int userId) {
// Perform tasks which require locks in LSS on a handler, as we are callbacks from
// ActivityManager.unlockUser()
mHandler.post(new Runnable() {
@Override
public void run() {
+ ensureProfileKeystoreUnlocked(userId);
// Hide notification first, as tie managed profile lock takes time
hideEncryptionNotification(new UserHandle(userId));
@@ -1027,7 +1050,8 @@
return new String(decryptionResult, StandardCharsets.UTF_8);
}
- private void unlockChildProfile(int profileHandle) throws RemoteException {
+ private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated)
+ throws RemoteException {
try {
doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
@@ -1038,6 +1062,8 @@
| BadPaddingException | CertificateException | IOException e) {
if (e instanceof FileNotFoundException) {
Slog.i(TAG, "Child profile key not found");
+ } else if (ignoreUserNotAuthenticated && e instanceof UserNotAuthenticatedException) {
+ Slog.i(TAG, "Parent keystore seems locked, ignoring");
} else {
Slog.e(TAG, "Failed to decrypt child profile key", e);
}
@@ -1081,11 +1107,8 @@
final List<UserInfo> profiles = mUserManager.getProfiles(userId);
for (UserInfo pi : profiles) {
// Unlock managed profile with unified lock
- if (pi.isManagedProfile()
- && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id)
- && mStorage.hasChildProfileLock(pi.id)
- && mUserManager.isUserRunning(pi.id)) {
- unlockChildProfile(pi.id);
+ if (tiedManagedProfileReadyToUnlock(pi)) {
+ unlockChildProfile(pi.id, false /* ignoreUserNotAuthenticated */);
}
}
}
@@ -1094,6 +1117,13 @@
}
}
+ private boolean tiedManagedProfileReadyToUnlock(UserInfo userInfo) {
+ return userInfo.isManagedProfile()
+ && !mLockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id)
+ && mStorage.hasChildProfileLock(userInfo.id)
+ && mUserManager.isUserRunning(userInfo.id);
+ }
+
private Map<Integer, String> getDecryptedPasswordsForAllTiedProfiles(int userId) {
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
return null;
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 922df1e..3795b7f 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -18,9 +18,7 @@
import com.android.internal.util.DumpUtils;
import com.android.server.Watchdog;
-import com.android.server.media.AudioPlaybackMonitor.OnAudioPlayerActiveStateChangedListener;
-import android.Manifest;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -96,9 +94,10 @@
private final ArrayMap<IBinder, ClientRecord> mAllClientRecords =
new ArrayMap<IBinder, ClientRecord>();
private int mCurrentUserId = -1;
- private boolean mHasBluetoothRoute = false;
+ private boolean mGlobalBluetoothA2dpOn = false;
private final IAudioService mAudioService;
private final AudioPlaybackMonitor mAudioPlaybackMonitor;
+ private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
public MediaRouterService(Context context) {
mContext = context;
@@ -137,13 +136,39 @@
audioRoutes = mAudioService.startWatchingRoutes(new IAudioRoutesObserver.Stub() {
@Override
public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
- mHasBluetoothRoute = newRoutes.bluetoothName != null;
+ synchronized (mLock) {
+ if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) {
+ if ((newRoutes.mainType & (AudioRoutesInfo.MAIN_HEADSET
+ | AudioRoutesInfo.MAIN_HEADPHONES
+ | AudioRoutesInfo.MAIN_USB)) == 0) {
+ // headset was plugged out.
+ mGlobalBluetoothA2dpOn = newRoutes.bluetoothName != null;
+ } else {
+ // headset was plugged in.
+ mGlobalBluetoothA2dpOn = false;
+ }
+ mCurAudioRoutesInfo.mainType = newRoutes.mainType;
+ }
+ if (!TextUtils.equals(
+ newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
+ if (newRoutes.bluetoothName == null) {
+ // BT was disconnected.
+ mGlobalBluetoothA2dpOn = false;
+ } else {
+ // BT was connected or changed.
+ mGlobalBluetoothA2dpOn = true;
+ }
+ mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
+ }
+ }
}
});
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in the audio service.");
}
- mHasBluetoothRoute = (audioRoutes != null && audioRoutes.bluetoothName != null);
+ synchronized (mLock) {
+ mGlobalBluetoothA2dpOn = (audioRoutes != null && audioRoutes.bluetoothName != null);
+ }
}
public void systemRunning() {
@@ -246,6 +271,14 @@
// Binder call
@Override
+ public boolean isGlobalBluetoothA2doOn() {
+ synchronized (mLock) {
+ return mGlobalBluetoothA2dpOn;
+ }
+ }
+
+ // Binder call
+ @Override
public void setDiscoveryRequest(IMediaRouterClient client,
int routeTypes, boolean activeScan) {
if (client == null) {
@@ -346,7 +379,12 @@
void restoreBluetoothA2dp() {
try {
- mAudioService.setBluetoothA2dpOn(mHasBluetoothRoute);
+ boolean a2dpOn = false;
+ synchronized (mLock) {
+ a2dpOn = mGlobalBluetoothA2dpOn;
+ }
+ Slog.v(TAG, "restoreBluetoothA2dp( " + a2dpOn + ")");
+ mAudioService.setBluetoothA2dpOn(a2dpOn);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException while calling setBluetoothA2dpOn.");
}
@@ -354,12 +392,14 @@
void restoreRoute(int uid) {
ClientRecord clientRecord = null;
- UserRecord userRecord = mUserRecords.get(UserHandle.getUserId(uid));
- if (userRecord != null && userRecord.mClientRecords != null) {
- for (ClientRecord cr : userRecord.mClientRecords) {
- if (validatePackageName(uid, cr.mPackageName)) {
- clientRecord = cr;
- break;
+ synchronized (mLock) {
+ UserRecord userRecord = mUserRecords.get(UserHandle.getUserId(uid));
+ if (userRecord != null && userRecord.mClientRecords != null) {
+ for (ClientRecord cr : userRecord.mClientRecords) {
+ if (validatePackageName(uid, cr.mPackageName)) {
+ clientRecord = cr;
+ break;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f88a074..07ab417 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -33,6 +33,7 @@
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -192,8 +193,6 @@
import libcore.io.IoUtils;
-import com.google.android.collect.Lists;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -370,13 +369,14 @@
private final boolean mSuppressDefaultPolicy;
/** Defined network policies. */
+ @GuardedBy("mNetworkPoliciesSecondLock")
final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>();
- /** Currently active network rules for ifaces. */
- final ArrayMap<NetworkPolicy, String[]> mNetworkRules = new ArrayMap<>();
/** Map from subId to subscription plans. */
+ @GuardedBy("mNetworkPoliciesSecondLock")
final SparseArray<SubscriptionPlan[]> mSubscriptionPlans = new SparseArray<>();
/** Map from subId to package name that owns subscription plans. */
+ @GuardedBy("mNetworkPoliciesSecondLock")
final SparseArray<String> mSubscriptionPlansOwner = new SparseArray<>();
/** Defined UID policies. */
@@ -972,7 +972,8 @@
/**
* Receiver that watches for {@link WifiConfiguration} to be loaded so that
- * we can perform upgrade logic.
+ * we can perform upgrade logic. After initial upgrade logic, it updates
+ * {@link #mMeteredIfaces} based on configuration changes.
*/
final private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() {
@Override
@@ -980,10 +981,9 @@
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
upgradeWifiMeteredOverrideAL();
+ updateNetworkRulesNL();
}
}
- // Only need to perform upgrade logic once
- mContext.unregisterReceiver(this);
}
};
@@ -1490,6 +1490,22 @@
}
/**
+ * Collect all ifaces from a {@link NetworkState} into the given set.
+ */
+ private static void collectIfaces(ArraySet<String> ifaces, NetworkState state) {
+ final String baseIface = state.linkProperties.getInterfaceName();
+ if (baseIface != null) {
+ ifaces.add(baseIface);
+ }
+ for (LinkProperties stackedLink : state.linkProperties.getStackedLinks()) {
+ final String stackedIface = stackedLink.getInterfaceName();
+ if (stackedIface != null) {
+ ifaces.add(stackedIface);
+ }
+ }
+ }
+
+ /**
* Examine all connected {@link NetworkState}, looking for
* {@link NetworkPolicy} that need to be enforced. When matches found, set
* remaining quota based on usage cycle and historical stats.
@@ -1507,60 +1523,33 @@
// First, generate identities of all connected networks so we can
// quickly compare them against all defined policies below.
- final ArrayList<Pair<String, NetworkIdentity>> connIdents = new ArrayList<>(states.length);
- final ArraySet<String> connIfaces = new ArraySet<String>(states.length);
+ final ArrayMap<NetworkState, NetworkIdentity> identified = new ArrayMap<>();
for (NetworkState state : states) {
if (state.networkInfo != null && state.networkInfo.isConnected()) {
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
-
- final String baseIface = state.linkProperties.getInterfaceName();
- if (baseIface != null) {
- connIdents.add(Pair.create(baseIface, ident));
- }
-
- // Stacked interfaces are considered to have same identity as
- // their parent network.
- final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
- for (LinkProperties stackedLink : stackedLinks) {
- final String stackedIface = stackedLink.getInterfaceName();
- if (stackedIface != null) {
- connIdents.add(Pair.create(stackedIface, ident));
- }
- }
+ identified.put(state, ident);
}
}
- // Apply policies against all connected interfaces found above
- mNetworkRules.clear();
- final ArrayList<String> ifaceList = Lists.newArrayList();
- for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
- final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
-
- ifaceList.clear();
- for (int j = connIdents.size() - 1; j >= 0; j--) {
- final Pair<String, NetworkIdentity> ident = connIdents.get(j);
- if (policy.template.matches(ident.second)) {
- ifaceList.add(ident.first);
- }
- }
-
- if (ifaceList.size() > 0) {
- final String[] ifaces = ifaceList.toArray(new String[ifaceList.size()]);
- mNetworkRules.put(policy, ifaces);
- }
- }
-
+ final ArraySet<String> newMeteredIfaces = new ArraySet<>();
long lowestRule = Long.MAX_VALUE;
- final ArraySet<String> newMeteredIfaces = new ArraySet<String>(states.length);
- // apply each policy that we found ifaces for; compute remaining data
- // based on current cycle and historical stats, and push to kernel.
- for (int i = mNetworkRules.size()-1; i >= 0; i--) {
- final NetworkPolicy policy = mNetworkRules.keyAt(i);
- final String[] ifaces = mNetworkRules.valueAt(i);
+ // For every well-defined policy, compute remaining data based on
+ // current cycle and historical stats, and push to kernel.
+ final ArraySet<String> matchingIfaces = new ArraySet<>();
+ for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
+
+ // Collect all ifaces that match this policy
+ matchingIfaces.clear();
+ for (int j = identified.size() - 1; j >= 0; j--) {
+ if (policy.template.matches(identified.valueAt(j))) {
+ collectIfaces(matchingIfaces, identified.keyAt(j));
+ }
+ }
if (LOGD) {
- Slog.d(TAG, "applying policy " + policy + " to ifaces " + Arrays.toString(ifaces));
+ Slog.d(TAG, "Applying " + policy + " to ifaces " + matchingIfaces);
}
final boolean hasWarning = policy.warningBytes != LIMIT_DISABLED;
@@ -1590,16 +1579,14 @@
quotaBytes = Long.MAX_VALUE;
}
- if (ifaces.length > 1) {
+ if (matchingIfaces.size() > 1) {
// TODO: switch to shared quota once NMS supports
Slog.w(TAG, "shared quota unsupported; generating rule for each iface");
}
- for (String iface : ifaces) {
- // long quotaBytes split up into two ints to fit in message
- mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA,
- (int) (quotaBytes >> 32), (int) (quotaBytes & 0xFFFFFFFF), iface)
- .sendToTarget();
+ for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
+ final String iface = matchingIfaces.valueAt(j);
+ setInterfaceQuotaAsync(iface, quotaBytes);
newMeteredIfaces.add(iface);
}
}
@@ -1613,29 +1600,36 @@
}
}
- for (int i = connIfaces.size()-1; i >= 0; i--) {
- String iface = connIfaces.valueAt(i);
- // long quotaBytes split up into two ints to fit in message
- mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA,
- (int) (Long.MAX_VALUE >> 32), (int) (Long.MAX_VALUE & 0xFFFFFFFF), iface)
- .sendToTarget();
- newMeteredIfaces.add(iface);
+ // One final pass to catch any metered ifaces that don't have explicitly
+ // defined policies; typically Wi-Fi networks.
+ for (NetworkState state : states) {
+ if (state.networkInfo != null && state.networkInfo.isConnected()
+ && !state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+ matchingIfaces.clear();
+ collectIfaces(matchingIfaces, state);
+ for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
+ final String iface = matchingIfaces.valueAt(j);
+ if (!newMeteredIfaces.contains(iface)) {
+ setInterfaceQuotaAsync(iface, Long.MAX_VALUE);
+ newMeteredIfaces.add(iface);
+ }
+ }
+ }
}
- mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget();
-
- // remove quota on any trailing interfaces
+ // Remove quota from any interfaces that are no longer metered.
for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
final String iface = mMeteredIfaces.valueAt(i);
if (!newMeteredIfaces.contains(iface)) {
- mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTA, iface)
- .sendToTarget();
+ removeInterfaceQuotaAsync(iface);
}
}
mMeteredIfaces = newMeteredIfaces;
final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget();
+
+ mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget();
}
/**
@@ -2776,20 +2770,18 @@
return plans.toArray(new SubscriptionPlan[plans.size()]);
}
- synchronized (mUidRulesFirstLock) {
- synchronized (mNetworkPoliciesSecondLock) {
- // Only give out plan details to the package that defined them,
- // so that we don't risk leaking plans between apps. We always
- // let in core system components (like the Settings app).
- final String ownerPackage = mSubscriptionPlansOwner.get(subId);
- if (Objects.equals(ownerPackage, callingPackage)
- || (UserHandle.getCallingAppId() == android.os.Process.SYSTEM_UID)) {
- return mSubscriptionPlans.get(subId);
- } else {
- Log.w(TAG, "Not returning plans because caller " + callingPackage
- + " doesn't match owner " + ownerPackage);
- return null;
- }
+ synchronized (mNetworkPoliciesSecondLock) {
+ // Only give out plan details to the package that defined them,
+ // so that we don't risk leaking plans between apps. We always
+ // let in core system components (like the Settings app).
+ final String ownerPackage = mSubscriptionPlansOwner.get(subId);
+ if (Objects.equals(ownerPackage, callingPackage)
+ || (UserHandle.getCallingAppId() == android.os.Process.SYSTEM_UID)) {
+ return mSubscriptionPlans.get(subId);
+ } else {
+ Log.w(TAG, "Not returning plans because caller " + callingPackage
+ + " doesn't match owner " + ownerPackage);
+ return null;
}
}
}
@@ -4021,6 +4013,12 @@
}
}
+ private void setInterfaceQuotaAsync(String iface, long quotaBytes) {
+ // long quotaBytes split up into two ints to fit in message
+ mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA, (int) (quotaBytes >> 32),
+ (int) (quotaBytes & 0xFFFFFFFF), iface).sendToTarget();
+ }
+
private void setInterfaceQuota(String iface, long quotaBytes) {
try {
mNetworkManager.setInterfaceQuota(iface, quotaBytes);
@@ -4031,6 +4029,10 @@
}
}
+ private void removeInterfaceQuotaAsync(String iface) {
+ mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTA, iface).sendToTarget();
+ }
+
private void removeInterfaceQuota(String iface) {
try {
mNetworkManager.removeInterfaceQuota(iface);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 4bd927d..3af5265 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -124,6 +124,7 @@
import android.util.TrustedTime;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
@@ -241,12 +242,17 @@
private final DropBoxNonMonotonicObserver mNonMonotonicObserver =
new DropBoxNonMonotonicObserver();
+ @GuardedBy("mStatsLock")
private NetworkStatsRecorder mDevRecorder;
+ @GuardedBy("mStatsLock")
private NetworkStatsRecorder mXtRecorder;
+ @GuardedBy("mStatsLock")
private NetworkStatsRecorder mUidRecorder;
+ @GuardedBy("mStatsLock")
private NetworkStatsRecorder mUidTagRecorder;
/** Cached {@link #mXtRecorder} stats. */
+ @GuardedBy("mStatsLock")
private NetworkStatsCollection mXtStatsCached;
/** Current counter sets for each UID. */
@@ -328,15 +334,15 @@
return;
}
- // create data recorders along with historical rotators
- mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false);
- mXtRecorder = buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false);
- mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false);
- mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true);
-
- updatePersistThresholds();
-
synchronized (mStatsLock) {
+ // create data recorders along with historical rotators
+ mDevRecorder = buildRecorder(PREFIX_DEV, mSettings.getDevConfig(), false);
+ mXtRecorder = buildRecorder(PREFIX_XT, mSettings.getXtConfig(), false);
+ mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false);
+ mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true);
+
+ updatePersistThresholdsLocked();
+
// upgrade any legacy stats, migrating them to rotated files
maybeUpgradeLegacyStatsLocked();
@@ -674,9 +680,12 @@
int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid) {
// We've been using pure XT stats long enough that we no longer need to
// splice DEV and XT together.
- return mXtStatsCached.getHistory(template, resolveSubscriptionPlan(template, flags),
- UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE,
- accessLevel, callingUid);
+ final SubscriptionPlan augmentPlan = resolveSubscriptionPlan(template, flags);
+ synchronized (mStatsLock) {
+ return mXtStatsCached.getHistory(template, augmentPlan,
+ UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE,
+ accessLevel, callingUid);
+ }
}
@Override
@@ -813,7 +822,7 @@
synchronized (mStatsLock) {
if (!mSystemReady) return;
- updatePersistThresholds();
+ updatePersistThresholdsLocked();
mDevRecorder.maybePersistLocked(currentTime);
mXtRecorder.maybePersistLocked(currentTime);
@@ -869,7 +878,7 @@
* reflect current {@link #mPersistThreshold} value. Always defers to
* {@link Global} values when defined.
*/
- private void updatePersistThresholds() {
+ private void updatePersistThresholdsLocked() {
mDevRecorder.setPersistThreshold(mSettings.getDevPersistBytes(mPersistThreshold));
mXtRecorder.setPersistThreshold(mSettings.getXtPersistBytes(mPersistThreshold));
mUidRecorder.setPersistThreshold(mSettings.getUidPersistBytes(mPersistThreshold));
@@ -1311,7 +1320,7 @@
synchronized (mStatsLock) {
if (args.length > 0 && "--proto".equals(args[0])) {
// In this case ignore all other arguments.
- dumpProto(fd);
+ dumpProtoLocked(fd);
return;
}
@@ -1387,7 +1396,7 @@
}
}
- private void dumpProto(FileDescriptor fd) {
+ private void dumpProtoLocked(FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
// TODO Right now it writes all history. Should it limit to the "since-boot" log?
diff --git a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
index 0a8635d..55c582e 100644
--- a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
+++ b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
@@ -24,6 +24,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.media.AudioAttributes;
import android.media.Ringtone;
@@ -138,13 +139,18 @@
final int userId = ActivityManager.getCurrentUser();
final int dialogAlreadyShown = Settings.Secure.getIntForUser(
cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId);
+ // Use USAGE_ASSISTANCE_ACCESSIBILITY for TVs to ensure that TVs play the ringtone as they
+ // have less ways of providing feedback like vibration.
+ final int audioAttributesUsage = hasFeatureLeanback()
+ ? AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
+ : AudioAttributes.USAGE_NOTIFICATION_EVENT;
// Play a notification tone
final Ringtone tone =
RingtoneManager.getRingtone(mContext, Settings.System.DEFAULT_NOTIFICATION_URI);
if (tone != null) {
tone.setAudioAttributes(new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
+ .setUsage(audioAttributesUsage)
.build());
tone.play();
}
@@ -254,6 +260,10 @@
AccessibilityServiceInfo.FEEDBACK_ALL_MASK).contains(serviceInfo);
}
+ private boolean hasFeatureLeanback() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ }
+
// Class to allow mocking of static framework calls
public static class FrameworkObjectProvider {
public AccessibilityManager getAccessibilityManagerInstance(Context context) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4c2e469..27159fa 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3398,11 +3398,6 @@
if (!down) {
cancelPreloadRecentApps();
- if (mHasFeatureLeanback) {
- // Clear flags
- mAccessibilityTvKey2Pressed = down;
- }
-
mHomePressed = false;
if (mHomeConsumed) {
mHomeConsumed = false;
@@ -3457,13 +3452,6 @@
preloadRecentApps();
}
} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
- if (mHasFeatureLeanback) {
- mAccessibilityTvKey2Pressed = down;
- if (interceptAccessibilityGestureTv()) {
- return -1;
- }
- }
-
if (!keyguardOn) {
handleLongPressOnHome(event.getDeviceId());
}
@@ -3630,11 +3618,8 @@
return 0;
} else if (mHasFeatureLeanback && interceptBugreportGestureTv(keyCode, down)) {
return -1;
- } else if (mHasFeatureLeanback && keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
- mAccessibilityTvKey1Pressed = down;
- if (interceptAccessibilityGestureTv()) {
- return -1;
- }
+ } else if (mHasFeatureLeanback && interceptAccessibilityGestureTv(keyCode, down)) {
+ return -1;
}
// Toggle Caps Lock on META-ALT.
@@ -3855,20 +3840,28 @@
/**
* TV only: recognizes a remote control gesture as Accessibility shortcut.
- * Shortcut: Long press (HOME + DPAD_CENTER)
+ * Shortcut: Long press (BACK + DPAD_DOWN)
*/
- private boolean interceptAccessibilityGestureTv() {
+ private boolean interceptAccessibilityGestureTv(int keyCode, boolean down) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ mAccessibilityTvKey1Pressed = down;
+ } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+ mAccessibilityTvKey2Pressed = down;
+ }
+
if (mAccessibilityTvKey1Pressed && mAccessibilityTvKey2Pressed) {
if (!mAccessibilityTvScheduled) {
mAccessibilityTvScheduled = true;
Message msg = Message.obtain(mHandler, MSG_ACCESSIBILITY_TV);
msg.setAsynchronous(true);
- mHandler.sendMessage(msg);
+ mHandler.sendMessageDelayed(msg,
+ ViewConfiguration.get(mContext).getAccessibilityShortcutKeyTimeout());
}
} else if (mAccessibilityTvScheduled) {
mHandler.removeMessages(MSG_ACCESSIBILITY_TV);
mAccessibilityTvScheduled = false;
}
+
return mAccessibilityTvScheduled;
}
@@ -7824,13 +7817,13 @@
case HapticFeedbackConstants.VIRTUAL_KEY:
return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
case HapticFeedbackConstants.KEYBOARD_PRESS: // == HapticFeedbackConstants.KEYBOARD_TAP
return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
case HapticFeedbackConstants.KEYBOARD_RELEASE:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
default:
return null;
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 805250a..6484a13 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -429,6 +429,8 @@
}
public void getMagnificationRegionLocked(Region outMagnificationRegion) {
+ // Make sure we're working with the most current bounds
+ mMagnifedViewport.recomputeBoundsLocked();
mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index c2edc04..c76b905 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -450,7 +450,7 @@
return isAnimating;
}
- void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+ void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
pw.print(prefix); pw.print("mAnimator="); pw.println(mAnimator);
pw.print(prefix); pw.print("freezingScreen="); pw.print(freezingScreen);
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index f142ff6..b083aee 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -366,7 +366,6 @@
// if made visible again.
wtoken.removeDeadWindows();
wtoken.setVisibleBeforeClientHidden();
- mService.mUnknownAppVisibilityController.appRemovedOrHidden(wtoken);
} else {
if (!mService.mAppTransition.isTransitionSet()
&& mService.mAppTransition.isReady()) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index f4ac961..0a7d1c1 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -368,10 +368,10 @@
boolean runningAppAnimation = false;
+ if (mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
+ mAppAnimator.setNullAnimation();
+ }
if (transit != AppTransition.TRANSIT_UNSET) {
- if (mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
- mAppAnimator.setNullAnimation();
- }
if (mService.applyAnimationLocked(this, lp, transit, visible, isVoiceInteraction)) {
delayed = runningAppAnimation = true;
}
@@ -1759,6 +1759,9 @@
if (mRemovingFromDisplay) {
pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
}
+ if (mAppAnimator.isAnimating()) {
+ mAppAnimator.dump(pw, prefix + " ");
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index cff2fad..7953ee4 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -21,7 +21,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.animation.AnimationHandler;
-import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -32,13 +31,11 @@
import android.os.Debug;
import android.util.ArrayMap;
import android.util.Slog;
-import android.view.Choreographer;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.WindowManagerInternal;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -142,9 +139,6 @@
// True if this this animation was canceled and will be replaced the another animation from
// the same {@link #BoundsAnimationTarget} target.
private boolean mSkipFinalResize;
- // True if this animation replaced a previous animation of the same
- // {@link #BoundsAnimationTarget} target.
- private final boolean mSkipAnimationStart;
// True if this animation was canceled by the user, not as a part of a replacing animation
private boolean mSkipAnimationEnd;
@@ -159,6 +153,7 @@
// Whether to schedule PiP mode changes on animation start/end
private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
+ private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState;
// Depending on whether we are animating from
// a smaller to a larger size
@@ -171,14 +166,14 @@
BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
@SchedulePipModeChangedState int schedulePipModeChangedState,
- boolean moveFromFullscreen, boolean moveToFullscreen,
- boolean replacingExistingAnimation) {
+ @SchedulePipModeChangedState int prevShedulePipModeChangedState,
+ boolean moveFromFullscreen, boolean moveToFullscreen) {
super();
mTarget = target;
mFrom.set(from);
mTo.set(to);
- mSkipAnimationStart = replacingExistingAnimation;
mSchedulePipModeChangedState = schedulePipModeChangedState;
+ mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState;
mMoveFromFullscreen = moveFromFullscreen;
mMoveToFullscreen = moveToFullscreen;
addUpdateListener(this);
@@ -200,7 +195,7 @@
@Override
public void onAnimationStart(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
- + " mSkipAnimationStart=" + mSkipAnimationStart
+ + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
+ " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
mFinishAnimationAfterTransition = false;
mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
@@ -210,18 +205,26 @@
// running
updateBooster();
- // Ensure that we have prepared the target for animation before
- // we trigger any size changes, so it can swap surfaces
- // in to appropriate modes, or do as it wishes otherwise.
- if (!mSkipAnimationStart) {
+ // Ensure that we have prepared the target for animation before we trigger any size
+ // changes, so it can swap surfaces in to appropriate modes, or do as it wishes
+ // otherwise.
+ if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
mTarget.onAnimationStart(mSchedulePipModeChangedState ==
- SCHEDULE_PIP_MODE_CHANGED_ON_START);
+ SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */);
// When starting an animation from fullscreen, pause here and wait for the
// windows-drawn signal before we start the rest of the transition down into PiP.
if (mMoveFromFullscreen) {
pause();
}
+ } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
+ mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
+ // We are replacing a running animation into PiP, but since it hasn't completed, the
+ // client will not currently receive any picture-in-picture mode change callbacks.
+ // However, we still need to report to them that they are leaving PiP, so this will
+ // force an update via a mode changed callback.
+ mTarget.onAnimationStart(true /* schedulePipModeChangedCallback */,
+ true /* forceUpdate */);
}
// Immediately update the task bounds if they have to become larger, but preserve
@@ -388,6 +391,8 @@
boolean moveFromFullscreen, boolean moveToFullscreen) {
final BoundsAnimator existing = mRunningAnimations.get(target);
final boolean replacing = existing != null;
+ @SchedulePipModeChangedState int prevSchedulePipModeChangedState =
+ NO_PIP_MODE_CHANGED_CALLBACKS;
if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
+ " schedulePipModeChangedState=" + schedulePipModeChangedState
@@ -403,6 +408,9 @@
return existing;
}
+ // Save the previous state
+ prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState;
+
// Update the PiP callback states if we are replacing the animation
if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
@@ -428,7 +436,8 @@
existing.cancel();
}
final BoundsAnimator animator = new BoundsAnimator(target, from, to,
- schedulePipModeChangedState, moveFromFullscreen, moveToFullscreen, replacing);
+ schedulePipModeChangedState, prevSchedulePipModeChangedState,
+ moveFromFullscreen, moveToFullscreen);
mRunningAnimations.put(target, animator);
animator.setFloatValues(0f, 1f);
animator.setDuration((animationDuration != -1 ? animationDuration
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
index 8b1bf7b..647a2d6 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -31,7 +31,7 @@
* @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
* callbacks
*/
- void onAnimationStart(boolean schedulePipModeChangedCallback);
+ void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);
/**
* Sets the size of the target (without any intermediate steps, like scheduling animation)
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 266ebf3..5cd4fc4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2126,8 +2126,9 @@
pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
- pw.println(subPrefix + "deferred=" + mDeferredRemoval
+ pw.print(subPrefix + "deferred=" + mDeferredRemoval
+ " mLayoutNeeded=" + mLayoutNeeded);
+ pw.println(" mTouchExcludeRegion=" + mTouchExcludeRegion);
pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 989e8f2..fc0baad 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -202,10 +202,12 @@
*/
/** Calls directly into activity manager so window manager lock shouldn't held. */
- public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
+ public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+ boolean forceUpdate) {
if (mListener != null) {
PinnedStackWindowListener listener = (PinnedStackWindowListener) mListener;
- listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
+ listener.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds,
+ forceUpdate);
}
}
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
index 12b9c1f..33e8a60 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowListener.java
@@ -28,5 +28,6 @@
* Called when the stack container pinned stack animation will change the picture-in-picture
* mode. This is a direct call into ActivityManager.
*/
- default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {}
+ default void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
+ boolean forceUpdate) {}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 7bcad9f..a9e56f3 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -32,6 +32,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -663,19 +664,7 @@
defaultDisplay.pendingLayoutChanges);
}
- for (i = mService.mResizingWindows.size() - 1; i >= 0; i--) {
- WindowState win = mService.mResizingWindows.get(i);
- if (win.mAppFreezing) {
- // Don't remove this window until rotation has completed.
- continue;
- }
- // Discard the saved surface if window size is changed, it can't be reused.
- if (win.mAppToken != null) {
- win.mAppToken.destroySavedSurfaces();
- }
- win.reportResized();
- mService.mResizingWindows.remove(i);
- }
+ final ArraySet<DisplayContent> touchExcludeRegionUpdateDisplays = handleResizingWindows();
if (DEBUG_ORIENTATION && mService.mDisplayFrozen) Slog.v(TAG,
"With display frozen, orientationChangeComplete=" + mOrientationChangeComplete);
@@ -817,6 +806,16 @@
mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
}
mService.setFocusTaskRegionLocked(null);
+ if (touchExcludeRegionUpdateDisplays != null) {
+ final DisplayContent focusedDc = mService.mFocusedApp != null
+ ? mService.mFocusedApp.getDisplayContent() : null;
+ for (DisplayContent dc : touchExcludeRegionUpdateDisplays) {
+ // The focused DisplayContent was recalcuated in setFocusTaskRegionLocked
+ if (focusedDc != dc) {
+ dc.setTouchExcludeRegion(null /* focusedTask */);
+ }
+ }
+ }
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
@@ -868,6 +867,37 @@
}
/**
+ * Handles resizing windows during surface placement.
+ *
+ * @return A set of any DisplayContent whose touch exclude region needs to be recalculated due
+ * to a tap-exclude window resizing, or null if no such DisplayContents were found.
+ */
+ private ArraySet<DisplayContent> handleResizingWindows() {
+ ArraySet<DisplayContent> touchExcludeRegionUpdateSet = null;
+ for (int i = mService.mResizingWindows.size() - 1; i >= 0; i--) {
+ WindowState win = mService.mResizingWindows.get(i);
+ if (win.mAppFreezing) {
+ // Don't remove this window until rotation has completed.
+ continue;
+ }
+ // Discard the saved surface if window size is changed, it can't be reused.
+ if (win.mAppToken != null) {
+ win.mAppToken.destroySavedSurfaces();
+ }
+ win.reportResized();
+ mService.mResizingWindows.remove(i);
+ if (WindowManagerService.excludeWindowTypeFromTapOutTask(win.mAttrs.type)) {
+ final DisplayContent dc = win.getDisplayContent();
+ if (touchExcludeRegionUpdateSet == null) {
+ touchExcludeRegionUpdateSet = new ArraySet<>();
+ }
+ touchExcludeRegionUpdateSet.add(dc);
+ }
+ }
+ return touchExcludeRegionUpdateSet;
+ }
+
+ /**
* @param w WindowState this method is applied to.
* @param obscured True if there is a window on top of this obscuring the display.
* @param syswin System window?
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 39878cc..55151a1 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1498,7 +1498,7 @@
}
@Override // AnimatesBounds
- public void onAnimationStart(boolean schedulePipModeChangedCallback) {
+ public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
synchronized (mService.mWindowMap) {
mBoundsAnimatingRequested = false;
@@ -1523,9 +1523,11 @@
final PinnedStackWindowController controller =
(PinnedStackWindowController) getController();
if (schedulePipModeChangedCallback && controller != null) {
- // We need to schedule the PiP mode change after the animation down, so use the
- // final bounds
- controller.updatePictureInPictureModeForPinnedStackAnimation(null);
+ // We need to schedule the PiP mode change before the animation up. It is possible
+ // in this case for the animation down to not have been completed, so always
+ // force-schedule and update to the client to ensure that it is notified that it
+ // is no longer in picture-in-picture mode
+ controller.updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
}
}
}
@@ -1553,7 +1555,7 @@
// We need to schedule the PiP mode change after the animation down, so use the
// final bounds
controller.updatePictureInPictureModeForPinnedStackAnimation(
- mBoundsAnimationTarget);
+ mBoundsAnimationTarget, false /* forceUpdate */);
}
if (finalStackSize != null) {
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 0370490..d2f374d 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -153,9 +153,9 @@
if (status == Status::OK) {
return lengthMs;
} else if (status != Status::UNSUPPORTED_OPERATION) {
- // Don't warn on UNSUPPORTED_OPERATION, that's a normal even and just means the motor
- // doesn't have a pre-defined waveform to perform for it, so we should just fall back
- // to the framework waveforms.
+ // Don't warn on UNSUPPORTED_OPERATION, that's a normal event and just means the motor
+ // doesn't have a pre-defined waveform to perform for it, so we should just give the
+ // opportunity to fall back to the framework waveforms.
ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32
", error=%" PRIu32 ").", static_cast<int64_t>(effect),
static_cast<int32_t>(strength), static_cast<uint32_t>(status));
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 221b9b4..c828acc 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1331,11 +1331,13 @@
traceEnd();
}
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
- traceBeginAndSlog("StartVoiceRecognitionManager");
- mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
- traceEnd();
- }
+ // We need to always start this service, regardless of whether the
+ // FEATURE_VOICE_RECOGNIZERS feature is set, because it needs to take care
+ // of initializing various settings. It will internally modify its behavior
+ // based on that feature.
+ traceBeginAndSlog("StartVoiceRecognitionManager");
+ mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
+ traceEnd();
if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
traceBeginAndSlog("StartGestureLauncher");
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 9d32496..0081214 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -126,6 +126,7 @@
boolean mMovedToFullscreen;
boolean mAnimationStarted;
boolean mSchedulePipModeChangedOnStart;
+ boolean mForcePipModeChangedCallback;
boolean mAnimationEnded;
Rect mAnimationEndFinalStackBounds;
boolean mSchedulePipModeChangedOnEnd;
@@ -140,6 +141,7 @@
mAnimationStarted = false;
mAnimationEnded = false;
mAnimationEndFinalStackBounds = null;
+ mForcePipModeChangedCallback = false;
mSchedulePipModeChangedOnStart = false;
mSchedulePipModeChangedOnEnd = false;
mStackBounds = from;
@@ -148,10 +150,11 @@
}
@Override
- public void onAnimationStart(boolean schedulePipModeChangedCallback) {
+ public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
mAwaitingAnimationStart = false;
mAnimationStarted = true;
mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
+ mForcePipModeChangedCallback = forceUpdate;
}
@Override
@@ -232,7 +235,7 @@
return this;
}
- BoundsAnimationDriver restart(Rect to) {
+ BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) {
if (mAnimator == null) {
throw new IllegalArgumentException("Call start() to start a new animation");
}
@@ -251,8 +254,15 @@
assertSame(oldAnimator, mAnimator);
}
- // No animation start for replacing animation
- assertTrue(!mTarget.mAnimationStarted);
+ if (expectStartedAndPipModeChangedCallback) {
+ // Replacing animation with pending pip mode changed callback, ensure we update
+ assertTrue(mTarget.mAnimationStarted);
+ assertTrue(mTarget.mSchedulePipModeChangedOnStart);
+ assertTrue(mTarget.mForcePipModeChangedCallback);
+ } else {
+ // No animation start for replacing animation
+ assertTrue(!mTarget.mAnimationStarted);
+ }
mTarget.mAnimationStarted = true;
return this;
}
@@ -467,7 +477,7 @@
mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
- .restart(BOUNDS_FLOATING)
+ .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
@@ -478,7 +488,8 @@
mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
- .restart(BOUNDS_SMALLER_FLOATING)
+ .restart(BOUNDS_SMALLER_FLOATING,
+ false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
@@ -486,10 +497,12 @@
@UiThreadTest
@Test
public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() throws Exception {
+ // When animating from fullscreen and the animation is interruped, we expect the animation
+ // start callback to be made, with a forced pip mode change callback
mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
- .restart(BOUNDS_FULL)
+ .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
}
@@ -512,7 +525,7 @@
mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
.expectStarted(SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
- .restart(BOUNDS_FULL)
+ .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
}
@@ -523,7 +536,8 @@
mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
.expectStarted(SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
- .restart(BOUNDS_SMALLER_FLOATING)
+ .restart(BOUNDS_SMALLER_FLOATING,
+ false /* expectStartedAndPipModeChangedCallback */)
.end()
.expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 4ffacfd..1569ac3 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -183,7 +183,7 @@
private final boolean mEnableService;
VoiceInteractionManagerServiceStub() {
- mEnableService = shouldEnableService(mContext.getResources());
+ mEnableService = shouldEnableService(mContext);
}
// TODO: VI Make sure the caller is the current user or profile
@@ -348,10 +348,15 @@
}
}
- private boolean shouldEnableService(Resources res) {
- // VoiceInteractionService should not be enabled on low ram devices unless it has the config flag.
- return !ActivityManager.isLowRamDeviceStatic() ||
- getForceVoiceInteractionServicePackage(res) != null;
+ private boolean shouldEnableService(Context context) {
+ // VoiceInteractionService should not be enabled on any low RAM devices
+ // or devices that have not declared the recognition feature, unless the
+ // device's configuration has explicitly set the config flag for a fixed
+ // voice interaction service.
+ return (!ActivityManager.isLowRamDeviceStatic()
+ && context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_VOICE_RECOGNIZERS)) ||
+ getForceVoiceInteractionServicePackage(context.getResources()) != null;
}
private String getForceVoiceInteractionServicePackage(Resources res) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 735f09b..0f6a968 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -346,14 +346,29 @@
public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
/**
- * Flag indicating whether we should downgrade/terminate VT calls and disable VT when
- * data enabled changed (e.g. reach data limit or turn off data).
+ * When {@code true}, changes to the mobile data enabled switch will not cause the VT
+ * registration state to change. That is, turning on or off mobile data will not cause VT to be
+ * enabled or disabled.
+ * When {@code false}, disabling mobile data will cause VT to be de-registered.
+ * <p>
+ * See also {@link #KEY_VILTE_DATA_IS_METERED_BOOL}.
* @hide
*/
public static final String KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS =
"ignore_data_enabled_changed_for_video_calls";
/**
+ * Flag indicating whether data used for a video call over LTE is metered or not.
+ * <p>
+ * When {@code true}, if the device hits the data limit or data is disabled during a ViLTE call,
+ * the call will be downgraded to audio-only (or paused if
+ * {@link #KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} is {@code true}).
+ *
+ * @hide
+ */
+ public static final String KEY_VILTE_DATA_IS_METERED_BOOL = "vilte_data_is_metered_bool";
+
+ /**
* Flag specifying whether WFC over IMS should be available for carrier: independent of
* carrier provisioning. If false: hard disabled. If true: then depends on carrier
* provisioning, availability etc.
@@ -1569,7 +1584,8 @@
sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
- sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, false);
+ sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
+ sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);