Merge changes I5cad0b5d,I6863eca2
* changes:
Render VectorDrawables in software and then upload to the VDAtlas.
Adding traces and some generic cleanup.
diff --git a/api/current.txt b/api/current.txt
index 0ba052b..23a0394 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30679,6 +30679,7 @@
field public static final int N_MR1 = 25; // 0x19
field public static final int O = 26; // 0x1a
field public static final int O_MR1 = 27; // 0x1b
+ field public static final int P = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -37081,13 +37082,14 @@
public final class FillEventHistory implements android.os.Parcelable {
method public int describeContents();
- method public android.os.Bundle getClientState();
+ method public deprecated android.os.Bundle getClientState();
method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
}
public static final class FillEventHistory.Event {
+ method public android.os.Bundle getClientState();
method public java.lang.String getDatasetId();
method public int getType();
field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
@@ -39659,6 +39661,7 @@
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
+ field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
diff --git a/api/system-current.txt b/api/system-current.txt
index 3e2eddb..a843e6d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -33396,6 +33396,7 @@
field public static final int N_MR1 = 25; // 0x19
field public static final int O = 26; // 0x1a
field public static final int O_MR1 = 27; // 0x1b
+ field public static final int P = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -40172,13 +40173,14 @@
public final class FillEventHistory implements android.os.Parcelable {
method public int describeContents();
- method public android.os.Bundle getClientState();
+ method public deprecated android.os.Bundle getClientState();
method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
}
public static final class FillEventHistory.Event {
+ method public android.os.Bundle getClientState();
method public java.lang.String getDatasetId();
method public int getType();
field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
@@ -43102,6 +43104,7 @@
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
+ field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
diff --git a/api/test-current.txt b/api/test-current.txt
index 545d11c..f5b31d1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -30823,6 +30823,7 @@
field public static final int N_MR1 = 25; // 0x19
field public static final int O = 26; // 0x1a
field public static final int O_MR1 = 27; // 0x1b
+ field public static final int P = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -37279,13 +37280,14 @@
public final class FillEventHistory implements android.os.Parcelable {
method public int describeContents();
- method public android.os.Bundle getClientState();
+ method public deprecated android.os.Bundle getClientState();
method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
}
public static final class FillEventHistory.Event {
+ method public android.os.Bundle getClientState();
method public java.lang.String getDatasetId();
method public int getType();
field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
@@ -39918,6 +39920,7 @@
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
+ field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 595ecd2..fb11272 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -964,7 +964,7 @@
// TODO(adamlesinski): Make this accept more than just overlay directories.
final void applyNewResourceDirsLocked(@NonNull final String baseCodePath,
- @NonNull final String[] newResourceDirs) {
+ @Nullable final String[] newResourceDirs) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#applyNewResourceDirsLocked");
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 1242cb0..8a1eae2 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -40,7 +40,6 @@
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Adapter;
import android.widget.AdapterView;
@@ -469,7 +468,9 @@
// We've already done this -- nothing to do.
return ;
}
- Log.w(TAG, "updateAppWidget couldn't find any view, using error view", exception);
+ if (exception != null) {
+ Log.w(TAG, "Error inflating RemoteViews : " + exception.toString());
+ }
content = getErrorView();
mViewMode = VIEW_MODE_ERROR;
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 92f4849..48587b3 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1103,12 +1103,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());
@@ -1127,7 +1127,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));
}
@@ -1143,7 +1143,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/os/Build.java b/core/java/android/os/Build.java
index 0627998..935f5f3 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -774,6 +774,11 @@
* O MR1.
*/
public static final int O_MR1 = 27;
+
+ /**
+ * P.
+ */
+ public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
}
/** The type of build, like "user" or "eng". */
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/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index f7dc1c5..60c1c9a 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -81,10 +81,13 @@
/**
* Returns the client state set in the previous {@link FillResponse}.
*
- * <p><b>NOTE: </b>the state is associated with the app that was autofilled in the previous
+ * <p><b>Note: </b>the state is associated with the app that was autofilled in the previous
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}
* , which is not necessary the same app being autofilled now.
+ *
+ * @deprecated use {@link #getEvents()} then {@link Event#getClientState()} instead.
*/
+ @Deprecated
@Nullable public Bundle getClientState() {
return mClientState;
}
@@ -126,7 +129,6 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeBundle(mClientState);
-
if (mEvents == null) {
dest.writeInt(0);
} else {
@@ -137,6 +139,7 @@
Event event = mEvents.get(i);
dest.writeInt(event.getType());
dest.writeString(event.getDatasetId());
+ dest.writeBundle(event.getClientState());
}
}
}
@@ -177,6 +180,7 @@
@EventIds private final int mEventType;
@Nullable private final String mDatasetId;
+ @Nullable private final Bundle mClientState;
/**
* Returns the type of the event.
@@ -197,18 +201,32 @@
}
/**
+ * Returns the client state from the {@link FillResponse} used to generate this event.
+ *
+ * <p><b>Note: </b>the state is associated with the app that was autofilled in the previous
+ * {@link
+ * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)},
+ * which is not necessary the same app being autofilled now.
+ */
+ @Nullable public Bundle getClientState() {
+ return mClientState;
+ }
+
+ /**
* Creates a new event.
*
* @param eventType The type of the event
* @param datasetId The dataset the event was on, or {@code null} if the event was on the
* whole response.
+ * @param clientState The client state associated with the event.
*
* @hide
*/
- public Event(int eventType, String datasetId) {
+ public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState) {
mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_SAVE_SHOWN,
"eventType");
mDatasetId = datasetId;
+ mClientState = clientState;
}
}
@@ -220,7 +238,8 @@
int numEvents = parcel.readInt();
for (int i = 0; i < numEvents; i++) {
- selection.addEvent(new Event(parcel.readInt(), parcel.readString()));
+ selection.addEvent(new Event(parcel.readInt(), parcel.readString(),
+ parcel.readBundle()));
}
return selection;
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/util/StatsLog.java b/core/java/android/util/StatsLog.java
new file mode 100644
index 0000000..0be1a8c
--- /dev/null
+++ b/core/java/android/util/StatsLog.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Logging access for platform metrics.
+ *
+ * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
+ * These diagnostic stats are for system integrators, not application authors.
+ *
+ * <p>Stats use integer tag codes.
+ * They carry a payload of one or more int, long, or String values.
+ * @hide
+ */
+public class StatsLog {
+ /** @hide */ public StatsLog() {}
+
+ private static final String TAG = "StatsLog";
+
+ // We assume that the native methods deal with any concurrency issues.
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param value A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeInt(int tag, int value);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param value A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeLong(int tag, long value);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param value A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeFloat(int tag, float value);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param str A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeString(int tag, String str);
+
+ /**
+ * Records an stats log message.
+ * @param tag The stats type tag code
+ * @param list A list of values to log. All values should
+ * be of type int, long, float or String.
+ * @return The number of bytes written
+ */
+ public static native int writeArray(int tag, Object... list);
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ffb3203..e5bd5ac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7621,6 +7621,9 @@
* <li>Call
* {@link android.view.autofill.AutofillManager#notifyValueChanged(View, int, AutofillValue)}
* when the value of a virtual child changed.
+ * <li>Call {@link
+ * android.view.autofill.AutofillManager#notifyViewVisibilityChanged(View, int, boolean)}
+ * when the visibility of a virtual child changed.
* <li>Call {@link AutofillManager#commit()} when the autofill context of the view structure
* changed and the current context should be committed (for example, when the user tapped
* a {@code SUBMIT} button in an HTML page).
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5adbdbe..46742b8 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -73,6 +73,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
@@ -188,17 +191,12 @@
private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
- private static final Object[] sMethodsLock = new Object[0];
- private static final ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>> sMethods =
- new ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>>();
- private static final ArrayMap<Method, Method> sAsyncMethods = new ArrayMap<>();
+ private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
- private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
- @Override
- protected Object[] initialValue() {
- return new Object[1];
- }
- };
+ /**
+ * This key is used to perform lookups in sMethods without causing allocations.
+ */
+ private static final MethodKey sLookupKey = new MethodKey();
/**
* @hide
@@ -255,37 +253,47 @@
}
/**
- * Handle with care!
+ * Stores information related to reflection method lookup.
*/
- static class MutablePair<F, S> {
- F first;
- S second;
-
- MutablePair(F first, S second) {
- this.first = first;
- this.second = second;
- }
+ static class MethodKey {
+ public Class targetClass;
+ public Class paramClass;
+ public String methodName;
@Override
public boolean equals(Object o) {
- if (!(o instanceof MutablePair)) {
+ if (!(o instanceof MethodKey)) {
return false;
}
- MutablePair<?, ?> p = (MutablePair<?, ?>) o;
- return Objects.equal(p.first, first) && Objects.equal(p.second, second);
+ MethodKey p = (MethodKey) o;
+ return Objects.equal(p.targetClass, targetClass)
+ && Objects.equal(p.paramClass, paramClass)
+ && Objects.equal(p.methodName, methodName);
}
@Override
public int hashCode() {
- return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
+ return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass)
+ ^ Objects.hashCode(methodName);
+ }
+
+ public void set(Class targetClass, Class paramClass, String methodName) {
+ this.targetClass = targetClass;
+ this.paramClass = paramClass;
+ this.methodName = methodName;
}
}
+
/**
- * This pair is used to perform lookups in sMethods without causing allocations.
+ * Stores information related to reflection method lookup result.
*/
- private final MutablePair<String, Class<?>> mPair =
- new MutablePair<String, Class<?>>(null, null);
+ static class MethodArgs {
+ public MethodHandle syncMethod;
+ public MethodHandle asyncMethod;
+ public String asyncMethodName;
+ }
+
/**
* This annotation indicates that a subclass of View is allowed to be used
@@ -307,6 +315,12 @@
public ActionException(String message) {
super(message);
}
+ /**
+ * @hide
+ */
+ public ActionException(Throwable t) {
+ super(t);
+ }
}
/** @hide */
@@ -943,73 +957,66 @@
return rect;
}
- private Method getMethod(View view, String methodName, Class<?> paramType) {
- Method method;
+ private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
+ boolean async) {
+ MethodArgs result;
Class<? extends View> klass = view.getClass();
- synchronized (sMethodsLock) {
- ArrayMap<MutablePair<String, Class<?>>, Method> methods = sMethods.get(klass);
- if (methods == null) {
- methods = new ArrayMap<MutablePair<String, Class<?>>, Method>();
- sMethods.put(klass, methods);
- }
+ synchronized (sMethods) {
+ // The key is defined by the view class, param class and method name.
+ sLookupKey.set(klass, paramType, methodName);
+ result = sMethods.get(sLookupKey);
- mPair.first = methodName;
- mPair.second = paramType;
-
- method = methods.get(mPair);
- if (method == null) {
+ if (result == null) {
+ Method method;
try {
if (paramType == null) {
method = klass.getMethod(methodName);
} else {
method = klass.getMethod(methodName, paramType);
}
- } catch (NoSuchMethodException ex) {
+ if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
+ throw new ActionException("view: " + klass.getName()
+ + " can't use method with RemoteViews: "
+ + methodName + getParameters(paramType));
+ }
+
+ result = new MethodArgs();
+ result.syncMethod = MethodHandles.publicLookup().unreflect(method);
+ result.asyncMethodName =
+ method.getAnnotation(RemotableViewMethod.class).asyncImpl();
+ } catch (NoSuchMethodException | IllegalAccessException ex) {
throw new ActionException("view: " + klass.getName() + " doesn't have method: "
+ methodName + getParameters(paramType));
}
- if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
- throw new ActionException("view: " + klass.getName()
- + " can't use method with RemoteViews: "
- + methodName + getParameters(paramType));
- }
-
- methods.put(new MutablePair<String, Class<?>>(methodName, paramType), method);
- }
- }
-
- return method;
- }
-
- /**
- * @return the async implementation of the provided method.
- */
- private Method getAsyncMethod(Method method) {
- synchronized (sAsyncMethods) {
- int valueIndex = sAsyncMethods.indexOfKey(method);
- if (valueIndex >= 0) {
- return sAsyncMethods.valueAt(valueIndex);
+ MethodKey key = new MethodKey();
+ key.set(klass, paramType, methodName);
+ sMethods.put(key, result);
}
- RemotableViewMethod annotation = method.getAnnotation(RemotableViewMethod.class);
- Method asyncMethod = null;
- if (!annotation.asyncImpl().isEmpty()) {
+ if (!async) {
+ return result.syncMethod;
+ }
+ // Check this so see if async method is implemented or not.
+ if (result.asyncMethodName.isEmpty()) {
+ return null;
+ }
+ // Async method is lazily loaded. If it is not yet loaded, load now.
+ if (result.asyncMethod == null) {
+ MethodType asyncType = result.syncMethod.type()
+ .dropParameterTypes(0, 1).changeReturnType(Runnable.class);
try {
- asyncMethod = method.getDeclaringClass()
- .getMethod(annotation.asyncImpl(), method.getParameterTypes());
- if (!asyncMethod.getReturnType().equals(Runnable.class)) {
- throw new ActionException("Async implementation for " + method.getName() +
- " does not return a Runnable");
- }
- } catch (NoSuchMethodException ex) {
- throw new ActionException("Async implementation declared but not defined for " +
- method.getName());
+ result.asyncMethod = MethodHandles.publicLookup().findVirtual(
+ klass, result.asyncMethodName, asyncType);
+ } catch (NoSuchMethodException | IllegalAccessException ex) {
+ throw new ActionException("Async implementation declared as "
+ + result.asyncMethodName + " but not defined for " + methodName
+ + ": public Runnable " + result.asyncMethodName + " ("
+ + TextUtils.join(",", asyncType.parameterArray()) + ")");
}
}
- sAsyncMethods.put(method, asyncMethod);
- return asyncMethod;
+ return result.asyncMethod;
}
}
@@ -1018,12 +1025,6 @@
return "(" + paramType + ")";
}
- private static Object[] wrapArg(Object value) {
- Object[] args = sInvokeArgsTls.get();
- args[0] = value;
- return args;
- }
-
/**
* Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
* {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
@@ -1140,10 +1141,8 @@
if (view == null) return;
try {
- getMethod(view, this.methodName, null).invoke(view);
- } catch (ActionException e) {
- throw e;
- } catch (Exception ex) {
+ getMethod(view, this.methodName, null, false /* async */).invoke(view);
+ } catch (Throwable ex) {
throw new ActionException(ex);
}
}
@@ -1516,12 +1515,9 @@
if (param == null) {
throw new ActionException("bad type: " + this.type);
}
-
try {
- getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
- } catch (ActionException e) {
- throw e;
- } catch (Exception ex) {
+ getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
+ } catch (Throwable ex) {
throw new ActionException(ex);
}
}
@@ -1537,11 +1533,10 @@
}
try {
- Method method = getMethod(view, this.methodName, param);
- Method asyncMethod = getAsyncMethod(method);
+ MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
- if (asyncMethod != null) {
- Runnable endAction = (Runnable) asyncMethod.invoke(view, wrapArg(this.value));
+ if (method != null) {
+ Runnable endAction = (Runnable) method.invoke(view, this.value);
if (endAction == null) {
return ACTION_NOOP;
} else {
@@ -1555,9 +1550,7 @@
return new RunnableAction(endAction);
}
}
- } catch (ActionException e) {
- throw e;
- } catch (Exception ex) {
+ } catch (Throwable ex) {
throw new ActionException(ex);
}
@@ -2672,7 +2665,7 @@
* given {@link RemoteViews}.
*
* @param viewId The id of the parent {@link ViewGroup} to add the child into.
- * @param nestedView {@link RemoveViews} of the child to add.
+ * @param nestedView {@link RemoteViews} of the child to add.
* @param index The position at which to add the child.
*
* @hide
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 2561ffe..cbb8d88 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -248,6 +248,9 @@
// with the Smart Select animation
layout.getSelection(start, end, (left, top, right, bottom) ->
result.add(new RectF(left, top, right, bottom)));
+
+ result.sort(SmartSelectSprite.RECTANGLE_COMPARATOR);
+
return result;
}
diff --git a/core/java/android/widget/SmartSelectSprite.java b/core/java/android/widget/SmartSelectSprite.java
index 844c714..45466c2 100644
--- a/core/java/android/widget/SmartSelectSprite.java
+++ b/core/java/android/widget/SmartSelectSprite.java
@@ -75,6 +75,10 @@
private final int mStrokeColor;
private Set<Drawable> mExistingAnimationDrawables = new HashSet<>();
+ static final Comparator<RectF> RECTANGLE_COMPARATOR = Comparator
+ .<RectF>comparingDouble(e -> e.bottom)
+ .thenComparingDouble(e -> e.left);
+
/**
* Represents a set of points connected by lines.
*/
@@ -110,7 +114,7 @@
*/
private static final class RoundedRectangleShape extends Shape {
- private static final String PROPERTY_ROUND_PERCENTAGE = "roundPercentage";
+ private static final String PROPERTY_ROUND_RATIO = "roundRatio";
@Retention(SOURCE)
@IntDef({ExpansionDirection.LEFT, ExpansionDirection.CENTER, ExpansionDirection.RIGHT})
@@ -134,7 +138,7 @@
private final float mStrokeWidth;
private final RectF mBoundingRectangle;
- private float mRoundPercentage = 1.0f;
+ private float mRoundRatio = 1.0f;
private final @ExpansionDirection int mExpansionDirection;
private final @RectangleBorderType int mRectangleBorderType;
@@ -158,9 +162,9 @@
mStrokeWidth = strokeWidth;
if (boundingRectangle.height() > boundingRectangle.width()) {
- setRoundPercentage(0.0f);
+ setRoundRatio(0.0f);
} else {
- setRoundPercentage(1.0f);
+ setRoundRatio(1.0f);
}
}
@@ -218,13 +222,12 @@
canvas.restore();
}
- public void setRoundPercentage(
- @FloatRange(from = 0.0, to = 1.0) final float newPercentage) {
- mRoundPercentage = newPercentage;
+ public void setRoundRatio(@FloatRange(from = 0.0, to = 1.0) final float roundRatio) {
+ mRoundRatio = roundRatio;
}
- public float getRoundPercentage() {
- return mRoundPercentage;
+ public float getRoundRatio() {
+ return mRoundRatio;
}
private void setLeftBoundary(final float leftBoundary) {
@@ -240,7 +243,7 @@
}
private float getAdjustedCornerRadius() {
- return (getCornerRadius() * mRoundPercentage);
+ return (getCornerRadius() * mRoundRatio);
}
private float getBoundingWidth() {
@@ -267,13 +270,6 @@
private RectangleList(List<RoundedRectangleShape> rectangles) {
mRectangles = new LinkedList<>(rectangles);
- mRectangles.sort((o1, o2) -> {
- if (o1.mBoundingRectangle.top == o2.mBoundingRectangle.top) {
- return Float.compare(o1.mBoundingRectangle.left, o2.mBoundingRectangle.left);
- } else {
- return Float.compare(o1.mBoundingRectangle.top, o2.mBoundingRectangle.top);
- }
- });
mReversedRectangles = new LinkedList<>(rectangles);
Collections.reverse(mReversedRectangles);
}
@@ -425,7 +421,9 @@
* @param start The point from which the animation will start. Must be inside
* destinationRectangles.
* @param destinationRectangles The rectangles which the animation will fill out by its
- * "selection" and finally join them into a single polygon.
+ * "selection" and finally join them into a single polygon. In
+ * order to get the correct visual behavior, these rectangles
+ * should be sorted according to {@link #RECTANGLE_COMPARATOR}.
* @param onAnimationEnd The callback which will be invoked once the whole animation
* completes.
* @throws IllegalArgumentException if the given start point is not in any of the
@@ -583,8 +581,8 @@
final ValueAnimator.AnimatorUpdateListener listener) {
final ObjectAnimator animator = ObjectAnimator.ofFloat(
shape,
- RoundedRectangleShape.PROPERTY_ROUND_PERCENTAGE,
- shape.getRoundPercentage(), 0.0F);
+ RoundedRectangleShape.PROPERTY_ROUND_RATIO,
+ shape.getRoundRatio(), 0.0F);
animator.setDuration(CORNER_DURATION);
animator.addUpdateListener(listener);
animator.setInterpolator(mCornerInterpolator);
@@ -592,8 +590,7 @@
}
private static @RoundedRectangleShape.ExpansionDirection int[] generateDirections(
- final RectF centerRectangle,
- final List<RectF> rectangles) throws IllegalArgumentException {
+ final RectF centerRectangle, final List<RectF> rectangles) {
final @RoundedRectangleShape.ExpansionDirection int[] result = new int[rectangles.size()];
final int centerRectangleIndex = rectangles.indexOf(centerRectangle);
@@ -601,7 +598,17 @@
for (int i = 0; i < centerRectangleIndex - 1; ++i) {
result[i] = RoundedRectangleShape.ExpansionDirection.LEFT;
}
- result[centerRectangleIndex] = RoundedRectangleShape.ExpansionDirection.CENTER;
+
+ if (rectangles.size() == 1) {
+ result[centerRectangleIndex] = RoundedRectangleShape.ExpansionDirection.CENTER;
+ } else if (centerRectangleIndex == 0) {
+ result[centerRectangleIndex] = RoundedRectangleShape.ExpansionDirection.LEFT;
+ } else if (centerRectangleIndex == rectangles.size() - 1) {
+ result[centerRectangleIndex] = RoundedRectangleShape.ExpansionDirection.RIGHT;
+ } else {
+ result[centerRectangleIndex] = RoundedRectangleShape.ExpansionDirection.CENTER;
+ }
+
for (int i = centerRectangleIndex + 1; i < result.length; ++i) {
result[i] = RoundedRectangleShape.ExpansionDirection.RIGHT;
}
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/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index bd94fc7..8ea0242 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1807,7 +1807,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/jni/Android.bp b/core/jni/Android.bp
index c629341..d63e22c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -103,6 +103,7 @@
"android_nio_utils.cpp",
"android_util_AssetManager.cpp",
"android_util_Binder.cpp",
+ "android_util_StatsLog.cpp",
"android_util_EventLog.cpp",
"android_util_MemoryIntArray.cpp",
"android_util_Log.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5afd067..02c9848 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -111,6 +111,7 @@
extern int register_android_app_admin_SecurityLog(JNIEnv* env);
extern int register_android_content_AssetManager(JNIEnv* env);
extern int register_android_util_EventLog(JNIEnv* env);
+extern int register_android_util_StatsLog(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
extern int register_android_util_MemoryIntArray(JNIEnv* env);
extern int register_android_util_PathParser(JNIEnv* env);
@@ -1311,6 +1312,7 @@
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
+ REG_JNI(register_android_util_StatsLog),
REG_JNI(register_android_util_Log),
REG_JNI(register_android_util_MemoryIntArray),
REG_JNI(register_android_util_PathParser),
diff --git a/core/jni/android_util_StatsLog.cpp b/core/jni/android_util_StatsLog.cpp
new file mode 100644
index 0000000..c992365
--- /dev/null
+++ b/core/jni/android_util_StatsLog.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2007-2014 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.
+ */
+
+#include <fcntl.h>
+#include <log/log_event_list.h>
+
+#include <log/log.h>
+
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+#define UNUSED __attribute__((__unused__))
+
+namespace android {
+
+static jclass gCollectionClass;
+static jmethodID gCollectionAddID;
+
+static jclass gIntegerClass;
+static jfieldID gIntegerValueID;
+
+static jclass gLongClass;
+static jfieldID gLongValueID;
+
+static jclass gFloatClass;
+static jfieldID gFloatValueID;
+
+static jclass gStringClass;
+
+/*
+ * In class android.util.StatsLog:
+ * static native int writeInt(int tag, int value)
+ */
+static jint android_util_StatsLog_write_Integer(JNIEnv* env UNUSED,
+ jobject clazz UNUSED,
+ jint tag, jint value)
+{
+ android_log_event_list ctx(tag);
+ ctx << (int32_t)value;
+ return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ * static native int writeLong(long tag, long value)
+ */
+static jint android_util_StatsLog_write_Long(JNIEnv* env UNUSED,
+ jobject clazz UNUSED,
+ jint tag, jlong value)
+{
+ android_log_event_list ctx(tag);
+ ctx << (int64_t)value;
+ return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ * static native int writeFloat(long tag, float value)
+ */
+static jint android_util_StatsLog_write_Float(JNIEnv* env UNUSED,
+ jobject clazz UNUSED,
+ jint tag, jfloat value)
+{
+ android_log_event_list ctx(tag);
+ ctx << (float)value;
+ return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ * static native int writeString(int tag, String value)
+ */
+static jint android_util_StatsLog_write_String(JNIEnv* env,
+ jobject clazz UNUSED,
+ jint tag, jstring value) {
+ android_log_event_list ctx(tag);
+ // Don't throw NPE -- I feel like it's sort of mean for a logging function
+ // to be all crashy if you pass in NULL -- but make the NULL value explicit.
+ if (value != NULL) {
+ const char *str = env->GetStringUTFChars(value, NULL);
+ ctx << str;
+ env->ReleaseStringUTFChars(value, str);
+ } else {
+ ctx << "NULL";
+ }
+ return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * In class android.util.StatsLog:
+ * static native int writeArray(long tag, Object... value)
+ */
+static jint android_util_StatsLog_write_Array(JNIEnv* env, jobject clazz,
+ jint tag, jobjectArray value) {
+ android_log_event_list ctx(tag);
+
+ if (value == NULL) {
+ ctx << "[NULL]";
+ return ctx.write(LOG_ID_STATS);
+ }
+
+ jsize copied = 0, num = env->GetArrayLength(value);
+ for (; copied < num && copied < 255; ++copied) {
+ if (ctx.status()) break;
+ jobject item = env->GetObjectArrayElement(value, copied);
+ if (item == NULL) {
+ ctx << "NULL";
+ } else if (env->IsInstanceOf(item, gStringClass)) {
+ const char *str = env->GetStringUTFChars((jstring) item, NULL);
+ ctx << str;
+ env->ReleaseStringUTFChars((jstring) item, str);
+ } else if (env->IsInstanceOf(item, gIntegerClass)) {
+ ctx << (int32_t)env->GetIntField(item, gIntegerValueID);
+ } else if (env->IsInstanceOf(item, gLongClass)) {
+ ctx << (int64_t)env->GetLongField(item, gLongValueID);
+ } else if (env->IsInstanceOf(item, gFloatClass)) {
+ ctx << (float)env->GetFloatField(item, gFloatValueID);
+ } else {
+ jniThrowException(env,
+ "java/lang/IllegalArgumentException",
+ "Invalid payload item type");
+ return -1;
+ }
+ env->DeleteLocalRef(item);
+ }
+ return ctx.write(LOG_ID_STATS);
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gRegisterMethods[] = {
+ /* name, signature, funcPtr */
+ { "writeInt", "(II)I", (void*) android_util_StatsLog_write_Integer },
+ { "writeLong", "(IJ)I", (void*) android_util_StatsLog_write_Long },
+ { "writeFloat", "(IF)I", (void*) android_util_StatsLog_write_Float },
+ { "writeString",
+ "(ILjava/lang/String;)I",
+ (void*) android_util_StatsLog_write_String
+ },
+ { "writeArray",
+ "(I[Ljava/lang/Object;)I",
+ (void*) android_util_StatsLog_write_Array
+ },
+};
+
+static struct { const char *name; jclass *clazz; } gClasses[] = {
+ { "java/lang/Integer", &gIntegerClass },
+ { "java/lang/Long", &gLongClass },
+ { "java/lang/Float", &gFloatClass },
+ { "java/lang/String", &gStringClass },
+ { "java/util/Collection", &gCollectionClass },
+};
+
+static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
+ { &gIntegerClass, "value", "I", &gIntegerValueID },
+ { &gLongClass, "value", "J", &gLongValueID },
+ { &gFloatClass, "value", "F", &gFloatValueID },
+};
+
+static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
+ { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
+};
+
+int register_android_util_StatsLog(JNIEnv* env) {
+ for (int i = 0; i < NELEM(gClasses); ++i) {
+ jclass clazz = FindClassOrDie(env, gClasses[i].name);
+ *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz);
+ }
+
+ for (int i = 0; i < NELEM(gFields); ++i) {
+ *gFields[i].id = GetFieldIDOrDie(env,
+ *gFields[i].c, gFields[i].name, gFields[i].ft);
+ }
+
+ for (int i = 0; i < NELEM(gMethods); ++i) {
+ *gMethods[i].id = GetMethodIDOrDie(env,
+ *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
+ }
+
+ return RegisterMethodsOrDie(
+ env,
+ "android/util/StatsLog",
+ gRegisterMethods, NELEM(gRegisterMethods));
+}
+
+}; // namespace android
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/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 7e9f561..f5b350b 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -181,7 +181,7 @@
<shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
<!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963" />
+ <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963|76551" />
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
<shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288" />
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 2627380..8def036 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -96,7 +96,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/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/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c8e6021..54421f74 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -846,7 +846,8 @@
<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>
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/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/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 0a0d2ce..bdc5e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -556,7 +556,7 @@
@Override
public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
- if (viewHolder.getItemViewType() == TYPE_EDIT) {
+ if (viewHolder.getItemViewType() == TYPE_EDIT || viewHolder.getItemViewType() == TYPE_DIVIDER) {
return makeMovementFlags(0, 0);
}
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT
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/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/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/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/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/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 20ccee2..2b8b25e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -522,10 +522,11 @@
/**
* Updates the last fill selection when an authentication was selected.
*/
- void setAuthenticationSelected(int sessionId) {
+ void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState) {
synchronized (mLock) {
if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
- mEventHistory.addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null));
+ mEventHistory
+ .addEvent(new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState));
}
}
}
@@ -533,11 +534,13 @@
/**
* Updates the last fill selection when an dataset authentication was selected.
*/
- void setDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId) {
+ void setDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
+ @Nullable Bundle clientState) {
synchronized (mLock) {
if (isValidEventLocked("setDatasetAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(
- new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset));
+ new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
+ clientState));
}
}
}
@@ -545,10 +548,10 @@
/**
* Updates the last fill selection when an save Ui is shown.
*/
- void setSaveShown(int sessionId) {
+ void setSaveShown(int sessionId, @Nullable Bundle clientState) {
synchronized (mLock) {
if (isValidEventLocked("setSaveShown()", sessionId)) {
- mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null));
+ mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState));
}
}
}
@@ -556,10 +559,12 @@
/**
* Updates the last fill response when a dataset was selected.
*/
- void setDatasetSelected(@Nullable String selectedDataset, int sessionId) {
+ void setDatasetSelected(@Nullable String selectedDataset, int sessionId,
+ @Nullable Bundle clientState) {
synchronized (mLock) {
if (isValidEventLocked("setDatasetSelected()", sessionId)) {
- mEventHistory.addEvent(new Event(Event.TYPE_DATASET_SELECTED, selectedDataset));
+ mEventHistory.addEvent(
+ new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState));
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 95db603..8d9f0aa2 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -597,7 +597,7 @@
getFillContextByRequestIdLocked(requestId).getStructure(), extras);
}
- mService.setAuthenticationSelected(id);
+ mService.setAuthenticationSelected(id, mClientState);
final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
mHandlerCaller.getHandler().post(() -> startAuthentication(authenticationId,
@@ -970,7 +970,7 @@
}
if (sDebug) Slog.d(TAG, "Good news, everyone! All checks passed, show save UI!");
- mService.setSaveShown(id);
+ mService.setSaveShown(id, mClientState);
final IAutoFillManagerClient client = getClient();
mPendingSaveUi = new PendingUi(mActivityToken);
getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo,
@@ -1532,14 +1532,14 @@
}
// Autofill it directly...
if (dataset.getAuthentication() == null) {
- mService.setDatasetSelected(dataset.getId(), id);
+ mService.setDatasetSelected(dataset.getId(), id, mClientState);
autoFillApp(dataset);
return;
}
// ...or handle authentication.
- mService.setDatasetAuthenticationSelected(dataset.getId(), id);
+ mService.setDatasetAuthenticationSelected(dataset.getId(), id, mClientState);
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
final Intent fillInIntent = createAuthFillInIntent(
getFillContextByRequestIdLocked(requestId).getStructure(), mClientState);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 29d562b..c1a7f68 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -10475,7 +10475,7 @@
public ComponentName[] listAllTransportComponents() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"listAllTransportComponents");
- return mTransportManager.getAllTransportCompenents();
+ return mTransportManager.getAllTransportComponents();
}
@Override
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index b01cfc5..83b6693 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -2762,7 +2762,7 @@
public ComponentName[] listAllTransportComponents() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"listAllTransportComponents");
- return mTransportManager.getAllTransportCompenents();
+ return mTransportManager.getAllTransportComponents();
}
@Override
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index fb2982e..321ef26 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -41,10 +41,12 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -57,7 +59,9 @@
private static final String TAG = "BackupTransportManager";
- private static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
+ @VisibleForTesting
+ /* package */ static final String SERVICE_ACTION_TRANSPORT_HOST =
+ "android.backup.TRANSPORT_HOST";
private static final long REBINDING_TIMEOUT_UNPROVISIONED_MS = 30 * 1000; // 30 sec
private static final long REBINDING_TIMEOUT_PROVISIONED_MS = 5 * 60 * 1000; // 5 mins
@@ -95,7 +99,11 @@
TransportBoundListener listener, Looper looper) {
mContext = context;
mPackageManager = context.getPackageManager();
- mTransportWhitelist = (whitelist != null) ? whitelist : new ArraySet<>();
+ if (whitelist != null) {
+ mTransportWhitelist = whitelist;
+ } else {
+ mTransportWhitelist = new ArraySet<>();
+ }
mCurrentTransportName = defaultTransport;
mTransportBoundListener = listener;
mHandler = new RebindOnTimeoutHandler(looper);
@@ -186,7 +194,7 @@
}
}
- ComponentName[] getAllTransportCompenents() {
+ ComponentName[] getAllTransportComponents() {
synchronized (mTransportLock) {
return mValidTransports.keySet().toArray(new ComponentName[mValidTransports.size()]);
}
@@ -208,7 +216,8 @@
}
}
- void ensureTransportReady(ComponentName transportComponent, SelectBackupTransportCallback listener) {
+ void ensureTransportReady(ComponentName transportComponent,
+ SelectBackupTransportCallback listener) {
synchronized (mTransportLock) {
TransportConnection conn = mValidTransports.get(transportComponent);
if (conn == null) {
@@ -252,7 +261,7 @@
intent, 0, UserHandle.USER_SYSTEM);
if (hosts != null) {
for (ResolveInfo host : hosts) {
- final ComponentName infoComponentName = host.serviceInfo.getComponentName();
+ final ComponentName infoComponentName = getComponentName(host.serviceInfo);
boolean shouldBind = false;
if (components != null && packageName != null) {
for (String component : components) {
@@ -310,7 +319,7 @@
Intent intent = new Intent(mTransportServiceIntent)
.setComponent(componentName);
return mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
- UserHandle.SYSTEM);
+ createSystemUserHandle());
}
private class TransportConnection implements ServiceConnection {
@@ -333,7 +342,7 @@
boolean success = false;
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE,
- component.flattenToShortString(), 1);
+ component.flattenToShortString(), 1);
try {
mTransportName = mBinder.name();
@@ -437,7 +446,8 @@
}
private long getRebindTimeout() {
- final boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
+ final boolean isDeviceProvisioned = Settings.Global.getInt(
+ mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0) != 0;
return isDeviceProvisioned
? REBINDING_TIMEOUT_PROVISIONED_MS
@@ -465,7 +475,7 @@
synchronized (mTransportLock) {
if (mBoundTransports.containsValue(transportComponent)) {
Slog.d(TAG, "Explicit rebinding timeout passed, but already bound to "
- + componentShortString + " so not attempting to rebind");
+ + componentShortString + " so not attempting to rebind");
return;
}
Slog.d(TAG, "Explicit rebinding timeout passed, attempting rebinding to: "
@@ -492,4 +502,18 @@
Slog.v(TAG, message);
}
}
+
+ // These only exists to make it testable with Robolectric, which is not updated to API level 24
+ // yet.
+ // TODO: Get rid of this once Robolectric is updated.
+ private static ComponentName getComponentName(ServiceInfo serviceInfo) {
+ return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+ }
+
+ // These only exists to make it testable with Robolectric, which is not updated to API level 24
+ // yet.
+ // TODO: Get rid of this once Robolectric is updated.
+ private static UserHandle createSystemUserHandle() {
+ return new UserHandle(UserHandle.USER_SYSTEM);
+ }
}
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 bbd3369..f3bc62f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13277,7 +13277,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;
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 4433b84..2c79d2e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -652,14 +652,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;
@@ -674,8 +674,7 @@
private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
try {
app.thread.schedulePictureInPictureModeChanged(appToken,
- mLastReportedPictureInPictureMode,
- overrideConfig);
+ mLastReportedPictureInPictureMode, overrideConfig);
} catch (Exception e) {
// If process died, no one cares.
}
@@ -1521,7 +1520,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/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 6a9a4fe..2da4377 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -5062,24 +5062,29 @@
}
boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
- boolean dumpClient, String dumpPackage, boolean needSep, String header) {
- boolean printed = false;
+ boolean dumpClient, String dumpPackage, boolean needSep) {
+
+ if (mTaskHistory.isEmpty()) {
+ return false;
+ }
+ final String prefix = " ";
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- printed |= ActivityStackSupervisor.dumpHistoryList(fd, pw,
- mTaskHistory.get(taskNdx).mActivities, " ", "Hist", true, !dumpAll,
- dumpClient, dumpPackage, needSep, header,
- " Task id #" + task.taskId + "\n" +
- " mFullscreen=" + task.mFullscreen + "\n" +
- " mBounds=" + task.mBounds + "\n" +
- " mMinWidth=" + task.mMinWidth + "\n" +
- " mMinHeight=" + task.mMinHeight + "\n" +
- " mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
- if (printed) {
- header = null;
+ if (needSep) {
+ pw.println("");
}
+ pw.println(prefix + "Task id #" + task.taskId);
+ pw.println(prefix + "mFullscreen=" + task.mFullscreen);
+ pw.println(prefix + "mBounds=" + task.mBounds);
+ pw.println(prefix + "mMinWidth=" + task.mMinWidth);
+ pw.println(prefix + "mMinHeight=" + task.mMinHeight);
+ pw.println(prefix + "mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
+ pw.println(prefix + "* " + task);
+ task.dump(pw, prefix + " ");
+ ActivityStackSupervisor.dumpHistoryList(fd, pw, mTaskHistory.get(taskNdx).mActivities,
+ prefix, "Hist", true, !dumpAll, dumpClient, dumpPackage, false, null, task);
}
- return printed;
+ return true;
}
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1107390..17b7f98 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1268,6 +1268,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
@@ -1294,9 +1298,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)) {
@@ -3644,25 +3645,14 @@
ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- StringBuilder stackHeader = new StringBuilder(128);
- stackHeader.append(" Stack #");
- stackHeader.append(stack.mStackId);
- stackHeader.append(":");
- stackHeader.append("\n");
- stackHeader.append(" mFullscreen=" + stack.mFullscreen);
- stackHeader.append("\n");
- stackHeader.append(" isSleeping=" + stack.shouldSleepActivities());
- stackHeader.append("\n");
- stackHeader.append(" mBounds=" + stack.mBounds);
+ pw.println();
+ pw.println(" Stack #" + stack.mStackId + ":");
+ pw.println(" mFullscreen=" + stack.mFullscreen);
+ pw.println(" isSleeping=" + stack.shouldSleepActivities());
+ pw.println(" mBounds=" + stack.mBounds);
- final boolean printedStackHeader = stack.dumpActivitiesLocked(fd, pw, dumpAll,
- dumpClient, dumpPackage, needSep, stackHeader.toString());
- printed |= printedStackHeader;
- if (!printedStackHeader) {
- // Ensure we always dump the stack header even if there are no activities
- pw.println();
- pw.println(stackHeader);
- }
+ printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
+ needSep);
printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false,
!dumpAll, false, dumpPackage, true,
@@ -3710,8 +3700,7 @@
static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
String prefix, String label, boolean complete, boolean brief, boolean client,
- String dumpPackage, boolean needNL, String header1, String header2) {
- TaskRecord lastTask = null;
+ String dumpPackage, boolean needNL, String header, TaskRecord lastTask) {
String innerPrefix = null;
String[] args = null;
boolean printed = false;
@@ -3730,13 +3719,9 @@
pw.println("");
needNL = false;
}
- if (header1 != null) {
- pw.println(header1);
- header1 = null;
- }
- if (header2 != null) {
- pw.println(header2);
- header2 = null;
+ if (header != null) {
+ pw.println(header);
+ header = null;
}
if (lastTask != r.getTask()) {
lastTask = r.getTask();
@@ -4151,31 +4136,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);
}
}
}
@@ -4237,7 +4220,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 a1b95f9..86ee3f4 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -92,15 +92,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/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/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 79bed73..c6998d6 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -520,11 +520,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);
@@ -544,7 +545,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) {
/*
@@ -573,7 +575,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);
@@ -584,8 +586,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) {
@@ -604,13 +604,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");
}
}
}
@@ -790,13 +788,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);
}
}
}
@@ -811,8 +813,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/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/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 93458c8..b224069 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;
@@ -191,8 +192,6 @@
import libcore.io.IoUtils;
-import com.google.android.collect.Lists;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -373,8 +372,6 @@
/** Defined network policies. */
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. */
final SparseArray<SubscriptionPlan[]> mSubscriptionPlans = new SparseArray<>();
@@ -927,7 +924,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
@@ -935,10 +933,9 @@
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
upgradeWifiMeteredOverrideAL();
+ updateNetworkRulesNL();
}
}
- // Only need to perform upgrade logic once
- mContext.unregisterReceiver(this);
}
};
@@ -1445,6 +1442,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.
@@ -1462,60 +1475,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;
@@ -1545,16 +1531,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);
}
}
@@ -1568,29 +1552,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();
}
/**
@@ -3973,6 +3964,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);
@@ -3983,6 +3980,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/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 04d91f8..807c343 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -92,26 +92,10 @@
return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())).isFile();
}
- boolean isDangerous(@NonNull final PackageInfo overlayPackage, final int userId) {
- // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
- return isDangerous(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath()));
- }
-
private String getIdmapPath(@NonNull final String baseCodePath) {
final StringBuilder sb = new StringBuilder("/data/resource-cache/");
sb.append(baseCodePath.substring(1).replace('/', '@'));
sb.append("@idmap");
return sb.toString();
}
-
- private boolean isDangerous(@NonNull final String idmapPath) {
- try (DataInputStream dis = new DataInputStream(new FileInputStream(idmapPath))) {
- final int magic = dis.readInt();
- final int version = dis.readInt();
- final int dangerous = dis.readInt();
- return dangerous != 0;
- } catch (IOException e) {
- return true;
- }
- }
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index db6e974..c3957f43 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -169,8 +169,9 @@
}
final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
- updateAllOverlaysForTarget(packageName, userId, targetPackage);
- mListener.onOverlaysChanged(packageName, userId);
+ if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
@@ -210,7 +211,9 @@
Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
}
- updateAllOverlaysForTarget(packageName, userId, null);
+ if (updateAllOverlaysForTarget(packageName, userId, null)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d21100c..68913c3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -7820,13 +7820,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/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index f628d5e..66e0a15 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -365,7 +365,6 @@
// Now that the app is going invisible, we can remove it. It will be restarted
// if made visible again.
wtoken.removeDeadWindows();
- mService.mUnknownAppVisibilityController.appRemovedOrHidden(wtoken);
} else {
if (!mService.mAppTransition.isTransitionSet()
&& mService.mAppTransition.isReady()) {
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/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 135f400..b5c9b99 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -204,10 +204,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/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 1a6e2c8..c876887 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1525,7 +1525,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;
@@ -1550,9 +1550,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);
}
}
}
@@ -1580,7 +1582,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 1620df1..57271fa 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/robotests/Android.mk b/services/robotests/Android.mk
new file mode 100644
index 0000000..3e82d3e
--- /dev/null
+++ b/services/robotests/Android.mk
@@ -0,0 +1,76 @@
+# Copyright (C) 2016 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.
+
+
+############################################################
+# FrameworksServicesLib app just for Robolectric test target. #
+############################################################
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := FrameworksServicesLib
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ frameworks-base-testutils \
+ services.backup \
+ services.core \
+ android-support-test \
+ mockito-target-minus-junit4 \
+ platform-test-annotations \
+ truth-prebuilt
+
+include $(BUILD_PACKAGE)
+
+#############################################
+# FrameworksServices Robolectric test target. #
+#############################################
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Include the testing libraries (JUnit4 + Robolectric libs).
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ platform-system-robolectric \
+ truth-prebuilt
+
+LOCAL_JAVA_LIBRARIES := \
+ junit \
+ platform-robolectric-prebuilt
+
+LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib
+LOCAL_MODULE := FrameworksServicesRoboTests
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+#############################################################
+# FrameworksServices runner target to run the previous target. #
+#############################################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunFrameworksServicesRoboTests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ FrameworksServicesRoboTests
+
+LOCAL_TEST_PACKAGE := FrameworksServicesLib
+
+include prebuilts/misc/common/robolectric/run_robotests.mk
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
new file mode 100644
index 0000000..0f7a091
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.RequiresPermission;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.UserHandle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.res.ResourceLoader;
+import org.robolectric.res.builder.DefaultPackageManager;
+import org.robolectric.res.builder.RobolectricPackageManager;
+import org.robolectric.shadows.ShadowContextImpl;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(
+ manifest = Config.NONE,
+ sdk = 23,
+ shadows = {TransportManagerTest.ShadowContextImplWithBindServiceAsUser.class}
+)
+public class TransportManagerTest {
+ private static final String PACKAGE_NAME = "some.package.name";
+ private static final String TRANSPORT1_NAME = "transport1.name";
+ private static final String TRANSPORT2_NAME = "transport2.name";
+ private static final ComponentName TRANSPORT1_COMPONENT_NAME = new ComponentName(PACKAGE_NAME,
+ TRANSPORT1_NAME);
+ private static final ComponentName TRANSPORT2_COMPONENT_NAME = new ComponentName(PACKAGE_NAME,
+ TRANSPORT2_NAME);
+ private static final List<ComponentName> TRANSPORTS_COMPONENT_NAMES = Arrays.asList(
+ TRANSPORT1_COMPONENT_NAME, TRANSPORT2_COMPONENT_NAME);
+
+ private RobolectricPackageManager mPackageManager;
+
+ @Mock private TransportManager.TransportBoundListener mTransportBoundListener;
+
+ @Before
+ public void setUp() {
+ mPackageManager = new DefaultPackageManagerWithQueryIntentServicesAsUser(
+ RuntimeEnvironment.getAppResourceLoader());
+ RuntimeEnvironment.setRobolectricPackageManager(mPackageManager);
+ }
+
+ @Test
+ public void onPackageAdded_bindsToAllTransports() {
+ Intent intent = new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST);
+ intent.setPackage(PACKAGE_NAME);
+
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = PACKAGE_NAME;
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+
+ mPackageManager.addPackage(packageInfo);
+
+ ResolveInfo transport1 = new ResolveInfo();
+ transport1.serviceInfo = new ServiceInfo();
+ transport1.serviceInfo.packageName = PACKAGE_NAME;
+ transport1.serviceInfo.name = TRANSPORT1_NAME;
+
+ ResolveInfo transport2 = new ResolveInfo();
+ transport2.serviceInfo = new ServiceInfo();
+ transport2.serviceInfo.packageName = PACKAGE_NAME;
+ transport2.serviceInfo.name = TRANSPORT2_NAME;
+
+ mPackageManager.addResolveInfoForIntent(intent, Arrays.asList(transport1, transport2));
+
+ TransportManager transportManager = new TransportManager(
+ RuntimeEnvironment.application.getApplicationContext(),
+ new HashSet<>(TRANSPORTS_COMPONENT_NAMES),
+ null,
+ mTransportBoundListener,
+ ShadowLooper.getMainLooper());
+ transportManager.onPackageAdded(PACKAGE_NAME);
+
+ assertThat(transportManager.getAllTransportComponents()).asList().containsExactlyElementsIn(
+ TRANSPORTS_COMPONENT_NAMES);
+ }
+
+ private static class DefaultPackageManagerWithQueryIntentServicesAsUser extends
+ DefaultPackageManager {
+
+ /* package */ DefaultPackageManagerWithQueryIntentServicesAsUser(
+ ResourceLoader appResourceLoader) {
+ super(appResourceLoader);
+ }
+
+ @Override
+ public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
+ return super.queryIntentServices(intent, flags);
+ }
+ }
+
+ @Implements(className = ShadowContextImpl.CLASS_NAME)
+ public static class ShadowContextImplWithBindServiceAsUser extends ShadowContextImpl {
+
+ @Implementation
+ public boolean bindServiceAsUser(@RequiresPermission Intent service, ServiceConnection conn,
+ int flags, UserHandle user) {
+ return true;
+ }
+ }
+}
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..8e4f487 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.
@@ -825,8 +840,6 @@
* If user has explicitly disabled some packages in the list, won't re-enable.
* Other carrier specific apps which are not in this list may be disabled for current carrier,
* and only be re-enabled when this config for another carrier includes it.
- *
- * @hide
*/
public static final String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
@@ -1569,7 +1582,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);
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
index bf56ec0..b982d0d 100644
--- a/tools/aapt/SdkConstants.h
+++ b/tools/aapt/SdkConstants.h
@@ -43,6 +43,7 @@
SDK_NOUGAT_MR1 = 25,
SDK_O = 26,
SDK_O_MR1 = 27,
+ SDK_P = 10000, // STOPSHIP Replace with the real version.
};
#endif // H_AAPT_SDK_CONSTANTS
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 5c32ed4..13584c0 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -53,6 +53,7 @@
SDK_NOUGAT_MR1 = 25,
SDK_O = 26,
SDK_O_MR1 = 27,
+ SDK_P = 10000, // STOPSHIP Replace with the real version.
};
ApiVersion FindAttributeSdkLevel(const ResourceId& id);
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 84b7927..887803e 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -323,11 +323,13 @@
std::vector<std::string> configs;
std::vector<std::string> split_args;
bool verbose = false;
+ bool print_only = false;
Flags flags =
Flags()
.OptionalFlag("-o", "Path to the output APK.", &options.output_path)
.OptionalFlag("-d", "Path to the output directory (for splits).", &options.output_dir)
.OptionalFlag("-x", "Path to XML configuration file.", &config_path)
+ .OptionalSwitch("-p", "Print the multi APK artifacts and exit.", &print_only)
.OptionalFlag(
"--target-densities",
"Comma separated list of the screen densities that the APK will be optimized for.\n"
@@ -372,12 +374,12 @@
}
context.SetVerbose(verbose);
+ IDiagnostics* diag = context.GetDiagnostics();
if (target_densities) {
// Parse the target screen densities.
for (const StringPiece& config_str : util::Tokenize(target_densities.value(), ',')) {
- Maybe<uint16_t> target_density =
- ParseTargetDensityParameter(config_str, context.GetDiagnostics());
+ Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
if (!target_density) {
return 1;
}
@@ -387,7 +389,7 @@
std::unique_ptr<IConfigFilter> filter;
if (!configs.empty()) {
- filter = ParseConfigFilterParameters(configs, context.GetDiagnostics());
+ filter = ParseConfigFilterParameters(configs, diag);
if (filter == nullptr) {
return 1;
}
@@ -398,26 +400,45 @@
for (const std::string& split_arg : split_args) {
options.split_paths.emplace_back();
options.split_constraints.emplace_back();
- if (!ParseSplitParameter(split_arg, context.GetDiagnostics(), &options.split_paths.back(),
+ if (!ParseSplitParameter(split_arg, diag, &options.split_paths.back(),
&options.split_constraints.back())) {
return 1;
}
}
if (config_path) {
- if (!options.output_dir) {
- context.GetDiagnostics()->Error(
- DiagMessage() << "Output directory is required when using a configuration file");
- return 1;
- }
std::string& path = config_path.value();
Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
if (for_path) {
- options.configuration = for_path.value().WithDiagnostics(context.GetDiagnostics()).Parse();
+ options.configuration = for_path.value().WithDiagnostics(diag).Parse();
} else {
- context.GetDiagnostics()->Error(DiagMessage() << "Could not parse config file " << path);
+ diag->Error(DiagMessage() << "Could not parse config file " << path);
return 1;
}
+
+ if (print_only) {
+ std::vector<std::string> names;
+ const PostProcessingConfiguration& config = options.configuration.value();
+ if (!config.AllArtifactNames(file::GetFilename(apk_path), &names, diag)) {
+ diag->Error(DiagMessage() << "Failed to generate output artifact list");
+ return 1;
+ }
+
+ for (const auto& name : names) {
+ std::cout << name << std::endl;
+ }
+ return 0;
+ }
+
+ // Since we know that we are going to process the APK (not just print targets), make sure we
+ // have somewhere to write them to.
+ if (!options.output_dir) {
+ diag->Error(DiagMessage() << "Output directory is required when using a configuration file");
+ return 1;
+ }
+ } else if (print_only) {
+ diag->Error(DiagMessage() << "Asked to print artifacts without providing a configurations");
+ return 1;
}
if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index 424e9be..1735a50 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -30,6 +30,7 @@
#include "io/File.h"
#include "io/FileSystem.h"
#include "io/StringInputStream.h"
+#include "util/Files.h"
#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlActionExecutor.h"
@@ -149,24 +150,49 @@
return true;
}
-Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, IDiagnostics* diag,
- const StringPiece& base_name,
- const StringPiece& ext) const {
- std::string result = format.to_string();
+/**
+ * Returns the common artifact base name from a template string.
+ */
+Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
+ const StringPiece ext = file::GetExtension(apk_name);
+ size_t end_index = apk_name.to_string().rfind(ext.to_string());
+ const std::string base_name =
+ (end_index != std::string::npos) ? std::string{apk_name.begin(), end_index} : "";
- Maybe<StringPiece> maybe_base_name =
- base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
- if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
- return {};
+ // Base name is optional.
+ if (result.find("${basename}") != std::string::npos) {
+ Maybe<StringPiece> maybe_base_name =
+ base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
+ if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
+ return {};
+ }
}
// Extension is optional.
if (result.find("${ext}") != std::string::npos) {
- if (!ReplacePlaceholder("${ext}", {ext}, &result, diag)) {
+ // Make sure we disregard the '.' in the extension when replacing the placeholder.
+ if (!ReplacePlaceholder("${ext}", {ext.substr(1)}, &result, diag)) {
return {};
}
+ } else {
+ // If no extension is specified, and the name template does not end in the current extension,
+ // add the existing extension.
+ if (!util::EndsWith(result, ext)) {
+ result.append(ext.to_string());
+ }
}
+ return result;
+}
+
+Maybe<std::string> Artifact::ToArtifactName(const StringPiece& format, const StringPiece& apk_name,
+ IDiagnostics* diag) const {
+ Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
+ if (!base) {
+ return {};
+ }
+ std::string result = std::move(base.value());
+
if (!ReplacePlaceholder("${abi}", abi_group, &result, diag)) {
return {};
}
@@ -194,29 +220,37 @@
return result;
}
-Maybe<std::string> Artifact::Name(const StringPiece& base_name, const StringPiece& ext,
- IDiagnostics* diag) const {
+Maybe<std::string> Artifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
if (!name) {
return {};
}
- std::string result = name.value();
+ return ToBaseName(name.value(), apk_name, diag);
+}
- // Base name is optional.
- if (result.find("${basename}") != std::string::npos) {
- if (!ReplacePlaceholder("${basename}", {base_name}, &result, diag)) {
- return {};
+bool PostProcessingConfiguration::AllArtifactNames(const StringPiece& apk_name,
+ std::vector<std::string>* artifact_names,
+ IDiagnostics* diag) const {
+ for (const auto& artifact : artifacts) {
+ Maybe<std::string> name;
+ if (artifact.name) {
+ name = artifact.Name(apk_name, diag);
+ } else {
+ if (!artifact_format) {
+ diag->Error(DiagMessage() << "No global artifact template and an artifact name is missing");
+ return false;
+ }
+ name = artifact.ToArtifactName(artifact_format.value(), apk_name, diag);
}
+
+ if (!name) {
+ return false;
+ }
+
+ artifact_names->push_back(std::move(name.value()));
}
- // Extension is optional.
- if (result.find("${ext}") != std::string::npos) {
- if (!ReplacePlaceholder("${ext}", {ext}, &result, diag)) {
- return {};
- }
- }
-
- return result;
+ return true;
}
} // namespace configuration
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index 6259ce8..a58685e 100644
--- a/tools/aapt2/configuration/ConfigurationParser.h
+++ b/tools/aapt2/configuration/ConfigurationParser.h
@@ -51,13 +51,11 @@
Maybe<std::string> gl_texture_group;
/** Convert an artifact name template into a name string based on configuration contents. */
- Maybe<std::string> ToArtifactName(const android::StringPiece& format, IDiagnostics* diag,
- const android::StringPiece& base_name = "",
- const android::StringPiece& ext = "apk") const;
+ Maybe<std::string> ToArtifactName(const android::StringPiece& format,
+ const android::StringPiece& apk_name, IDiagnostics* diag) const;
/** Convert an artifact name template into a name string based on configuration contents. */
- Maybe<std::string> Name(const android::StringPiece& base_name, const android::StringPiece& ext,
- IDiagnostics* diag) const;
+ Maybe<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
};
/** Enumeration of currently supported ABIs. */
@@ -139,6 +137,10 @@
Group<AndroidSdk> android_sdk_groups;
Group<DeviceFeature> device_feature_groups;
Group<GlTexture> gl_texture_groups;
+
+ /** Helper method that generates a list of artifact names and returns true on success. */
+ bool AllArtifactNames(const android::StringPiece& apk_name,
+ std::vector<std::string>* artifact_names, IDiagnostics* diag) const;
};
} // namespace configuration
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index ece70a9..d3bfd33 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -414,16 +414,36 @@
Artifact x86;
x86.abi_group = {"x86"};
- auto x86_result = x86.ToArtifactName("something.${abi}.apk", &diag);
+ auto x86_result = x86.ToArtifactName("something.${abi}.apk", "", &diag);
ASSERT_TRUE(x86_result);
EXPECT_EQ(x86_result.value(), "something.x86.apk");
Artifact arm;
arm.abi_group = {"armeabi-v7a"};
- auto arm_result = arm.ToArtifactName("app.${abi}.apk", &diag);
- ASSERT_TRUE(arm_result);
- EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+ {
+ auto arm_result = arm.ToArtifactName("app.${abi}.apk", "", &diag);
+ ASSERT_TRUE(arm_result);
+ EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+ }
+
+ {
+ auto arm_result = arm.ToArtifactName("app.${abi}.apk", "different_name.apk", &diag);
+ ASSERT_TRUE(arm_result);
+ EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+ }
+
+ {
+ auto arm_result = arm.ToArtifactName("${basename}.${abi}.apk", "app.apk", &diag);
+ ASSERT_TRUE(arm_result);
+ EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+ }
+
+ {
+ auto arm_result = arm.ToArtifactName("app.${abi}.${ext}", "app.apk", &diag);
+ ASSERT_TRUE(arm_result);
+ EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+ }
}
TEST(ArtifactTest, Complex) {
@@ -436,10 +456,40 @@
artifact.locale_group = {"en-AU"};
artifact.android_sdk_group = {"26"};
- auto result = artifact.ToArtifactName(
- "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", &diag);
- ASSERT_TRUE(result);
- EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+ {
+ auto result = artifact.ToArtifactName(
+ "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", "", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+ }
+
+ {
+ auto result = artifact.ToArtifactName(
+ "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", "app.apk", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+ }
+
+ {
+ auto result = artifact.ToArtifactName(
+ "${basename}.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.apk", "app.apk", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+ }
+
+ {
+ auto result = artifact.ToArtifactName(
+ "app.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}.${ext}", "app.apk", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+ }
+
+ {
+ auto result = artifact.ToArtifactName(
+ "${basename}.${density}_${locale}_${feature}_${gl}.sdk${sdk}.${abi}", "app.apk", &diag);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+ }
}
TEST(ArtifactTest, Missing) {
@@ -447,16 +497,20 @@
Artifact x86;
x86.abi_group = {"x86"};
- EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", &diag));
- EXPECT_FALSE(x86.ToArtifactName("something.apk", &diag));
+ EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "", &diag));
+ EXPECT_FALSE(x86.ToArtifactName("something.apk", "", &diag));
+ EXPECT_FALSE(x86.ToArtifactName("something.${density}.apk", "something.apk", &diag));
+ EXPECT_FALSE(x86.ToArtifactName("something.apk", "something.apk", &diag));
}
TEST(ArtifactTest, Empty) {
StdErrDiagnostics diag;
Artifact artifact;
- EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", &diag));
- EXPECT_TRUE(artifact.ToArtifactName("something.apk", &diag));
+ EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "", &diag));
+ EXPECT_TRUE(artifact.ToArtifactName("something.apk", "", &diag));
+ EXPECT_FALSE(artifact.ToArtifactName("something.${density}.apk", "something.apk", &diag));
+ EXPECT_TRUE(artifact.ToArtifactName("something.apk", "something.apk", &diag));
}
TEST(ArtifactTest, Repeated) {
@@ -464,8 +518,9 @@
Artifact artifact;
artifact.screen_density_group = {"mdpi"};
- ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", &diag));
- EXPECT_FALSE(artifact.ToArtifactName("something.${density}.${density}.apk", &diag));
+ ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "", &diag));
+ EXPECT_FALSE(artifact.ToArtifactName("something.${density}.${density}.apk", "", &diag));
+ ASSERT_TRUE(artifact.ToArtifactName("something.${density}.apk", "something.apk", &diag));
}
TEST(ArtifactTest, Nesting) {
@@ -473,9 +528,9 @@
Artifact x86;
x86.abi_group = {"x86"};
- EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", &diag));
+ EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", "", &diag));
- const Maybe<std::string>& name = x86.ToArtifactName("something.${abi${abi}}.apk", &diag);
+ const Maybe<std::string>& name = x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag);
ASSERT_TRUE(name);
EXPECT_EQ(name.value(), "something.${abix86}.apk");
}
@@ -486,12 +541,12 @@
artifact.device_feature_group = {"${gl}"};
artifact.gl_texture_group = {"glx1"};
- EXPECT_FALSE(artifact.ToArtifactName("app.${feature}.${gl}.apk", &diag));
+ EXPECT_FALSE(artifact.ToArtifactName("app.${feature}.${gl}.apk", "", &diag));
artifact.device_feature_group = {"df1"};
artifact.gl_texture_group = {"${feature}"};
{
- const auto& result = artifact.ToArtifactName("app.${feature}.${gl}.apk", &diag);
+ const auto& result = artifact.ToArtifactName("app.${feature}.${gl}.apk", "", &diag);
ASSERT_TRUE(result);
EXPECT_EQ(result.value(), "app.df1.${feature}.apk");
}
@@ -501,7 +556,7 @@
artifact.device_feature_group = {"${gl}"};
artifact.gl_texture_group = {"glx1"};
{
- const auto& result = artifact.ToArtifactName("app.${feature}.apk", &diag);
+ const auto& result = artifact.ToArtifactName("app.${feature}.apk", "", &diag);
ASSERT_TRUE(result);
EXPECT_EQ(result.value(), "app.glx1.apk");
}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index a0ffefa..6fb1793 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -293,6 +293,7 @@
manifest_action["instrumentation"]["meta-data"] = meta_data_action;
manifest_action["original-package"];
+ manifest_action["overlay"];
manifest_action["protected-broadcast"];
manifest_action["uses-permission"];
manifest_action["uses-permission-sdk-23"];
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 8316264..e7a4f85 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -44,11 +44,11 @@
const PostProcessingConfiguration& config,
const TableFlattenerOptions& table_flattener_options) {
// TODO(safarmer): Handle APK version codes for the generated APKs.
- // TODO(safarmer): Handle explicit outputs/generating an output file list for other tools.
+ IDiagnostics* diag = context_->GetDiagnostics();
- const std::string& apk_path = file::GetFilename(apk_->GetSource().path).to_string();
- const StringPiece ext = file::GetExtension(apk_path);
- const std::string base_name = apk_path.substr(0, apk_path.rfind(ext.to_string()));
+ const std::string& apk_name = file::GetFilename(apk_->GetSource().path).to_string();
+ const StringPiece ext = file::GetExtension(apk_name);
+ const std::string base_name = apk_name.substr(0, apk_name.rfind(ext.to_string()));
// For now, just write out the stripped APK since ABI splitting doesn't modify anything else.
for (const Artifact& artifact : config.artifacts) {
@@ -57,20 +57,17 @@
AxisConfigFilter axis_filter;
if (!artifact.name && !config.artifact_format) {
- context_->GetDiagnostics()->Error(
+ diag->Error(
DiagMessage() << "Artifact does not have a name and no global name template defined");
return false;
}
Maybe<std::string> artifact_name =
- (artifact.name)
- ? artifact.Name(base_name, ext.substr(1), context_->GetDiagnostics())
- : artifact.ToArtifactName(config.artifact_format.value(), context_->GetDiagnostics(),
- base_name, ext.substr(1));
+ (artifact.name) ? artifact.Name(apk_name, diag)
+ : artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
if (!artifact_name) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "Could not determine split APK artifact name");
+ diag->Error(DiagMessage() << "Could not determine split APK artifact name");
return false;
}
@@ -80,8 +77,7 @@
auto group = config.abi_groups.find(group_name);
// TODO: Remove validation when configuration parser ensures referential integrity.
if (group == config.abi_groups.end()) {
- context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced ABI group '"
- << group_name << "'");
+ diag->Error(DiagMessage() << "could not find referenced ABI group '" << group_name << "'");
return false;
}
filters.AddFilter(AbiFilter::FromAbiList(group->second));
@@ -93,8 +89,7 @@
auto group = config.screen_density_groups.find(group_name);
// TODO: Remove validation when configuration parser ensures referential integrity.
if (group == config.screen_density_groups.end()) {
- context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
- << group_name << "'");
+ diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'");
return false;
}
@@ -109,8 +104,7 @@
auto group = config.locale_groups.find(group_name);
// TODO: Remove validation when configuration parser ensures referential integrity.
if (group == config.locale_groups.end()) {
- context_->GetDiagnostics()->Error(DiagMessage() << "could not find referenced group '"
- << group_name << "'");
+ diag->Error(DiagMessage() << "could not find referenced group '" << group_name << "'");
return false;
}
@@ -132,11 +126,10 @@
}
file::AppendPath(&out, artifact_name.value());
- std::unique_ptr<IArchiveWriter> writer =
- CreateZipFileArchiveWriter(context_->GetDiagnostics(), out);
+ std::unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(diag, out);
if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage() << "Writing output: " << out);
+ diag->Note(DiagMessage() << "Writing output: " << out);
}
if (!apk_->WriteToArchive(context_, table.get(), table_flattener_options, &filters,