Merge "Listen TRANSPORT_BLUETOOTH network change in GnssLocationProvider"
diff --git a/api/current.txt b/api/current.txt
index 49d6b47..0dc0c31 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -49217,7 +49217,6 @@
method public void goBack();
method public void goBackOrForward(int);
method public void goForward();
- method public static void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
method public void invokeZoomPicker();
method public boolean isPrivateBrowsingEnabled();
method public void loadData(java.lang.String, java.lang.String, java.lang.String);
@@ -49265,7 +49264,7 @@
method public void setWebViewClient(android.webkit.WebViewClient);
method public deprecated boolean shouldDelayChildPressedState();
method public deprecated boolean showFindDialog(java.lang.String, boolean);
- method public static void shutdownSafeBrowsing();
+ method public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
method public void stopLoading();
method public void zoomBy(float);
method public boolean zoomIn();
diff --git a/api/system-current.txt b/api/system-current.txt
index e56be3c..bde877e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -52921,7 +52921,6 @@
method public void goBack();
method public void goBackOrForward(int);
method public void goForward();
- method public static void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
method public void invokeZoomPicker();
method public boolean isPrivateBrowsingEnabled();
method public void loadData(java.lang.String, java.lang.String, java.lang.String);
@@ -52969,7 +52968,7 @@
method public void setWebViewClient(android.webkit.WebViewClient);
method public deprecated boolean shouldDelayChildPressedState();
method public deprecated boolean showFindDialog(java.lang.String, boolean);
- method public static void shutdownSafeBrowsing();
+ method public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
method public void stopLoading();
method public void zoomBy(float);
method public boolean zoomIn();
@@ -53166,7 +53165,6 @@
method public abstract android.net.Uri[] parseFileChooserResult(int, android.content.Intent);
method public abstract void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
method public abstract void setWebContentsDebuggingEnabled(boolean);
- method public abstract void shutdownSafeBrowsing();
}
public class WebViewFragment extends android.app.Fragment {
diff --git a/api/test-current.txt b/api/test-current.txt
index abd678d..2f7dc5cc 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -49682,7 +49682,6 @@
method public void goBack();
method public void goBackOrForward(int);
method public void goForward();
- method public static void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
method public void invokeZoomPicker();
method public boolean isPrivateBrowsingEnabled();
method public void loadData(java.lang.String, java.lang.String, java.lang.String);
@@ -49730,7 +49729,7 @@
method public void setWebViewClient(android.webkit.WebViewClient);
method public deprecated boolean shouldDelayChildPressedState();
method public deprecated boolean showFindDialog(java.lang.String, boolean);
- method public static void shutdownSafeBrowsing();
+ method public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
method public void stopLoading();
method public void zoomBy(float);
method public boolean zoomIn();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 586f13f..35b2012 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6951,6 +6951,7 @@
customContent = customContent.clone();
remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
+ remoteViews.setReapplyDisallowed();
}
// also update the end margin if there is an image
int endMargin = R.dimen.notification_content_margin_end;
@@ -7057,6 +7058,7 @@
customContent = customContent.clone();
remoteViews.removeAllViews(id);
remoteViews.addView(id, customContent);
+ remoteViews.setReapplyDisallowed();
}
return remoteViews;
}
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index 666da0a..e38d227 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -16,18 +16,14 @@
package android.net;
-import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import java.lang.Math;
-import java.lang.UnsupportedOperationException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
+import java.util.Set;
/**
* A network identifier along with a score for the quality of that network.
@@ -195,7 +191,28 @@
return Objects.equals(networkKey, that.networkKey)
&& Objects.equals(rssiCurve, that.rssiCurve)
&& Objects.equals(meteredHint, that.meteredHint)
- && Objects.equals(attributes, that.attributes);
+ && bundleEquals(attributes, that.attributes);
+ }
+
+ private boolean bundleEquals(Bundle bundle1, Bundle bundle2) {
+ if (bundle1 == bundle2) {
+ return true;
+ }
+ if (bundle1 == null || bundle2 == null) {
+ return false;
+ }
+ if (bundle1.size() != bundle2.size()) {
+ return false;
+ }
+ Set<String> keys = bundle1.keySet();
+ for (String key : keys) {
+ Object value1 = bundle1.get(key);
+ Object value2 = bundle2.get(key);
+ if (!Objects.equals(value1, value2)) {
+ return false;
+ }
+ }
+ return true;
}
@Override
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 049f1ef..4b98e35 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1628,42 +1628,28 @@
}
/**
- * Starts Safe Browsing initialization. This should only be called once.
- *
+ * Starts Safe Browsing initialization.
* <p>
- * Because the Safe Browsing feature takes time to initialize, WebView may temporarily whitelist
- * URLs until the feature is ready. The callback will be invoked with true once initialization
- * is complete.
- * </p>
- *
+ * URL loads are not guaranteed to be protected by Safe Browsing until after {@code callback} is
+ * invoked with {@code true}. Safe Browsing is not fully supported on all devices. For those
+ * devices {@code callback} will receive {@code false}.
* <p>
- * This does not enable the Safe Browsing feature itself, and should only be used if the feature
- * is otherwise enabled.
- * </p>
- *
+ * This does not enable the Safe Browsing feature itself, and should only be called if Safe
+ * Browsing is enabled by the manifest tag or {@link WebSettings#setSafeBrowsingEnabled}. This
+ * prepares resources used for Safe Browsing.
* <p>
- * This does not require an Activity Context, and will always use the application Context to do
- * its work.
- * </p>
+ * This should be called with the Application Context (and will always use the Application
+ * context to do its work regardless).
*
* @param context Application Context.
- * @param callback will be called with the value true if initialization is
- * successful. The callback will be run on the UI thread.
+ * @param callback will be called on the UI thread with {@code true} if initialization is
+ * successful, {@code false} otherwise.
*/
- public static void initSafeBrowsing(Context context, ValueCallback<Boolean> callback) {
+ public static void startSafeBrowsing(Context context, ValueCallback<Boolean> callback) {
getFactory().getStatics().initSafeBrowsing(context, callback);
}
/**
- * Shuts down Safe Browsing. This should only be called once. This does not disable the feature,
- * it only frees resources used by Safe Browsing code. To disable Safe Browsing on an individual
- * WebView, see {@link WebSettings#setSafeBrowsingEnabled}
- */
- public static void shutdownSafeBrowsing() {
- getFactory().getStatics().shutdownSafeBrowsing();
- }
-
- /**
* Sets the list of domains that are exempt from SafeBrowsing checks. The list is
* global for all the WebViews.
* <p>
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 4f6513f..4c47abc 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -80,17 +80,11 @@
/**
* Implement the API method
- * {@link android.webkit.WebView#initSafeBrowsing(Context , ValueCallback<Boolean>)}
+ * {@link android.webkit.WebView#startSafeBrowsing(Context , ValueCallback<Boolean>)}
*/
void initSafeBrowsing(Context context, ValueCallback<Boolean> callback);
/**
- * Implement the API method
- * {@link android.webkit.WebView#shutdownSafeBrowsing()}
- */
- void shutdownSafeBrowsing();
-
- /**
* Implement the API method
* {@link android.webkit.WebView#setSafeBrowsingWhitelist(List<String>,
* ValueCallback<Boolean>)}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index b77aa1c..5c63c75 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -154,6 +154,12 @@
private boolean mIsRoot = true;
/**
+ * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
+ * the layout in a way that isn't recoverable, since views are being removed.
+ */
+ private boolean mReapplyDisallowed;
+
+ /**
* Constants to whether or not this RemoteViews is composed of a landscape and portrait
* RemoteViews.
*/
@@ -215,6 +221,26 @@
}
/**
+ * Set that it is disallowed to reapply another remoteview with the same layout as this view.
+ * This should be done if an action is destroying the view tree of the base layout.
+ *
+ * @hide
+ */
+ public void setReapplyDisallowed() {
+ mReapplyDisallowed = true;
+ }
+
+ /**
+ * @return Whether it is disallowed to reapply another remoteview with the same layout as this
+ * view. True if this remoteview has actions that destroyed view tree of the base layout.
+ *
+ * @hide
+ */
+ public boolean isReapplyDisallowed() {
+ return mReapplyDisallowed;
+ }
+
+ /**
* Handle with care!
*/
static class MutablePair<F, S> {
@@ -2429,6 +2455,7 @@
mApplication = mPortrait.mApplication;
mLayoutId = mPortrait.getLayoutId();
}
+ mReapplyDisallowed = parcel.readInt() == 0;
// setup the memory usage statistics
mMemoryUsageCounter = new MemoryUsageCounter();
@@ -3738,6 +3765,7 @@
// Both RemoteViews already share the same package and user
mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
}
+ dest.writeInt(mReapplyDisallowed ? 1 : 0);
}
private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index f99637d..42e9273 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -76,6 +76,9 @@
case SkEncodedImageFormat::kWEBP:
mimeType = "image/webp";
break;
+ case SkEncodedImageFormat::kHEIF:
+ mimeType = "image/heif";
+ break;
case SkEncodedImageFormat::kWBMP:
mimeType = "image/vnd.wap.wbmp";
break;
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
index dc17da2..109f32e 100644
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -49,6 +49,55 @@
= new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00"));
@Test
+ public void scoredNetworksWithBothNullAttributeBundle_equal() {
+ ScoredNetwork scoredNetwork1 =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, null /* attributes */);
+ ScoredNetwork scoredNetwork2 =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, null /* attributes */);
+ assertTrue(scoredNetwork1.equals(scoredNetwork2));
+ }
+
+ @Test
+ public void scoredNetworksWithOneNullAttributeBundle_notEqual() {
+ ScoredNetwork scoredNetwork1 =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, ATTRIBUTES);
+ ScoredNetwork scoredNetwork2 =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, null /* attributes */);
+ assertFalse(scoredNetwork1.equals(scoredNetwork2));
+ }
+
+ @Test
+ public void scoredNetworksWithDifferentSizedAttributeBundle_notEqual() {
+ ScoredNetwork scoredNetwork1 =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, ATTRIBUTES);
+ Bundle attr = new Bundle(ATTRIBUTES);
+ attr.putBoolean(ScoredNetwork.ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL, true);
+ ScoredNetwork scoredNetwork2 =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, attr);
+ assertFalse(scoredNetwork1.equals(scoredNetwork2));
+ }
+
+ @Test
+ public void scoredNetworksWithDifferentAttributeValues_notEqual() {
+ ScoredNetwork scoredNetwork1 =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, ATTRIBUTES);
+ Bundle attr = new Bundle();
+ attr.putInt(ScoredNetwork.ATTRIBUTES_KEY_RANKING_SCORE_OFFSET, Integer.MIN_VALUE);
+ ScoredNetwork scoredNetwork2 =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, attr);
+ assertFalse(scoredNetwork1.equals(scoredNetwork2));
+ }
+
+ @Test
+ public void scoredNetworksWithSameAttributeValuesAndSize_equal() {
+ ScoredNetwork scoredNetwork1 =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, ATTRIBUTES);
+ ScoredNetwork scoredNetwork2 =
+ new ScoredNetwork(KEY, CURVE, false /* meteredHint */, ATTRIBUTES);
+ assertTrue(scoredNetwork1.equals(scoredNetwork2));
+ }
+
+ @Test
public void calculateRankingOffsetShouldThrowUnsupportedOperationException() {
// No curve or ranking score offset set in curve
ScoredNetwork scoredNetwork = new ScoredNetwork(KEY, null);
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 2f48ffb..fc4d15f 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -80,8 +80,9 @@
public static final int FILE_TYPE_BMP = 34;
public static final int FILE_TYPE_WBMP = 35;
public static final int FILE_TYPE_WEBP = 36;
+ public static final int FILE_TYPE_HEIF = 37;
private static final int FIRST_IMAGE_FILE_TYPE = FILE_TYPE_JPEG;
- private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_WEBP;
+ private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_HEIF;
// Raw image file types
public static final int FILE_TYPE_DNG = 300;
@@ -239,6 +240,8 @@
addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp", MtpConstants.FORMAT_BMP, true);
addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp", MtpConstants.FORMAT_DEFINED, false);
addFileType("WEBP", FILE_TYPE_WEBP, "image/webp", MtpConstants.FORMAT_DEFINED, false);
+ addFileType("HEIC", FILE_TYPE_HEIF, "image/heif", MtpConstants.FORMAT_HEIF, true);
+ addFileType("HEIF", FILE_TYPE_HEIF, "image/heif", MtpConstants.FORMAT_HEIF, false);
addFileType("DNG", FILE_TYPE_DNG, "image/x-adobe-dng", MtpConstants.FORMAT_DNG, true);
addFileType("CR2", FILE_TYPE_CR2, "image/x-canon-cr2", MtpConstants.FORMAT_TIFF, false);
diff --git a/media/java/android/mtp/MtpConstants.java b/media/java/android/mtp/MtpConstants.java
index 7d078d7..88e287e 100644
--- a/media/java/android/mtp/MtpConstants.java
+++ b/media/java/android/mtp/MtpConstants.java
@@ -211,6 +211,8 @@
public static final int FORMAT_JPX = 0x3810;
/** Format code for DNG files */
public static final int FORMAT_DNG = 0x3811;
+ /** Format code for HEIF files {@hide} */
+ public static final int FORMAT_HEIF = 0x3812;
/** Format code for firmware files */
public static final int FORMAT_UNDEFINED_FIRMWARE = 0xB802;
/** Format code for Windows image files */
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 698c9c9..9847d705 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -595,6 +595,7 @@
MtpConstants.FORMAT_XML_DOCUMENT,
MtpConstants.FORMAT_FLAC,
MtpConstants.FORMAT_DNG,
+ MtpConstants.FORMAT_HEIF,
};
}
@@ -705,6 +706,7 @@
case MtpConstants.FORMAT_PNG:
case MtpConstants.FORMAT_BMP:
case MtpConstants.FORMAT_DNG:
+ case MtpConstants.FORMAT_HEIF:
return IMAGE_PROPERTIES;
default:
return FILE_PROPERTIES;
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index cf4458a..5b874cd 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -849,6 +849,7 @@
// read EXIF data for thumbnail information
switch (info.mFormat) {
case MTP_FORMAT_EXIF_JPEG:
+ case MTP_FORMAT_HEIF:
case MTP_FORMAT_JFIF: {
ExifData *exifdata = exif_data_new_from_file(path);
if (exifdata) {
@@ -906,6 +907,7 @@
if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) {
switch (format) {
case MTP_FORMAT_EXIF_JPEG:
+ case MTP_FORMAT_HEIF:
case MTP_FORMAT_JFIF: {
ExifData *exifdata = exif_data_new_from_file(path);
if (exifdata) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index f3be8d0..0ecb8be 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -15,6 +15,7 @@
*/
package com.android.settingslib.wifi;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -60,9 +61,10 @@
R.string.accessibility_wifi_signal_full
};
- private final StateListDrawable mFrictionSld;
+ @Nullable private final StateListDrawable mFrictionSld;
private final int mBadgePadding;
private final UserBadgeCache mBadgeCache;
+ private final IconInjector mIconInjector;
private TextView mTitleView;
private boolean mForSavedNetworks = false;
@@ -86,60 +88,53 @@
return builder.toString();
}
+ @Nullable
+ private static StateListDrawable getFrictionStateListDrawable(Context context) {
+ TypedArray frictionSld;
+ try {
+ frictionSld = context.getTheme().obtainStyledAttributes(FRICTION_ATTRS);
+ } catch (Resources.NotFoundException e) {
+ // Fallback for platforms that do not need friction icon resources.
+ frictionSld = null;
+ }
+ return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
+ }
+
// Used for dummy pref.
public AccessPointPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mFrictionSld = null;
mBadgePadding = 0;
mBadgeCache = null;
+ mIconInjector = new IconInjector(context);
}
public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
boolean forSavedNetworks) {
- super(context);
- setWidgetLayoutResource(R.layout.access_point_friction_widget);
- mBadgeCache = cache;
- mAccessPoint = accessPoint;
- mForSavedNetworks = forSavedNetworks;
- mAccessPoint.setTag(this);
- mLevel = -1;
-
- TypedArray frictionSld;
- try {
- frictionSld = context.getTheme().obtainStyledAttributes(FRICTION_ATTRS);
- } catch (Resources.NotFoundException e) {
- // Fallback for platforms that do not need friction icon resources.
- frictionSld = null;
- }
- mFrictionSld = frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
-
- // Distance from the end of the title at which this AP's user badge should sit.
- mBadgePadding = context.getResources()
- .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
+ this(accessPoint, context, cache, 0 /* iconResId */, forSavedNetworks);
refresh();
}
public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
int iconResId, boolean forSavedNetworks) {
+ this(accessPoint, context, cache, iconResId, forSavedNetworks,
+ getFrictionStateListDrawable(context), -1 /* level */, new IconInjector(context));
+ }
+
+ @VisibleForTesting
+ AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
+ int iconResId, boolean forSavedNetworks, StateListDrawable frictionSld,
+ int level, IconInjector iconInjector) {
super(context);
setWidgetLayoutResource(R.layout.access_point_friction_widget);
mBadgeCache = cache;
mAccessPoint = accessPoint;
mForSavedNetworks = forSavedNetworks;
mAccessPoint.setTag(this);
- mLevel = -1;
+ mLevel = level;
mDefaultIconResId = iconResId;
-
- TypedArray frictionSld;
- try {
- frictionSld = context.getTheme().obtainStyledAttributes(FRICTION_ATTRS);
- } catch (Resources.NotFoundException e) {
- // Fallback for platforms that do not need friction icon resources.
- frictionSld = null;
- }
- mFrictionSld = frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
-
- // Distance from the end of the title at which this AP's user badge should sit.
+ mFrictionSld = frictionSld;
+ mIconInjector = iconInjector;
mBadgePadding = context.getResources()
.getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
}
@@ -179,7 +174,7 @@
}
TronUtils.logWifiSettingsSpeed(context, mWifiSpeed);
- Drawable drawable = context.getDrawable(Utils.getWifiIconResource(level));
+ Drawable drawable = mIconInjector.getIcon(level);
if (!mForSavedNetworks && drawable != null) {
drawable.setTint(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
setIcon(drawable);
@@ -319,4 +314,16 @@
return mBadges.valueAt(index);
}
}
+
+ static class IconInjector {
+ private final Context mContext;
+
+ public IconInjector(Context context) {
+ mContext = context;
+ }
+
+ public Drawable getIcon(int level) {
+ return mContext.getDrawable(Utils.getWifiIconResource(level));
+ }
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index 08757b2..7f89fd5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -16,16 +16,23 @@
package com.android.settingslib.wifi;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.TestConfig;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@@ -35,6 +42,22 @@
private Context mContext = RuntimeEnvironment.application;
+ @Mock private AccessPoint mockAccessPoint;
+ @Mock private AccessPointPreference.UserBadgeCache mockUserBadgeCache;
+ @Mock private AccessPointPreference.IconInjector mockIconInjector;
+
+ private AccessPointPreference createWithAccessPoint(AccessPoint accessPoint) {
+ return new AccessPointPreference(accessPoint, mContext, mockUserBadgeCache,
+ 0, true, null, -1, mockIconInjector);
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mockIconInjector.getIcon(anyInt())).thenReturn(new ColorDrawable());
+ }
+
@Test
public void generatePreferenceKey_shouldReturnSsidPlusSecurity() {
String ssid = "ssid";
@@ -78,4 +101,16 @@
RuntimeEnvironment.application, pref, ap))
.isEqualTo("ssid,connected,Wifi signal full.,Secure network");
}
+
+ @Test
+ public void refresh_shouldUpdateIcon() {
+ int level = 1;
+ when(mockAccessPoint.getSpeed()).thenReturn(0);
+ when(mockAccessPoint.getLevel()).thenReturn(level);
+
+ AccessPointPreference pref = createWithAccessPoint(mockAccessPoint);
+ pref.refresh();
+
+ verify(mockIconInjector).getIcon(level);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
index bf926c6..f967118 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -193,7 +193,7 @@
int flag = FLAG_REINFLATE_CONTENT_VIEW;
if ((reInflateFlags & flag) != 0) {
- boolean isNewView = !compareRemoteViews(result.newContentView, entry.cachedContentView);
+ boolean isNewView = !canReapplyRemoteView(result.newContentView, entry.cachedContentView);
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -215,7 +215,7 @@
flag = FLAG_REINFLATE_EXPANDED_VIEW;
if ((reInflateFlags & flag) != 0) {
if (result.newExpandedView != null) {
- boolean isNewView = !compareRemoteViews(result.newExpandedView,
+ boolean isNewView = !canReapplyRemoteView(result.newExpandedView,
entry.cachedBigContentView);
ApplyCallback applyCallback = new ApplyCallback() {
@Override
@@ -240,7 +240,7 @@
flag = FLAG_REINFLATE_HEADS_UP_VIEW;
if ((reInflateFlags & flag) != 0) {
if (result.newHeadsUpView != null) {
- boolean isNewView = !compareRemoteViews(result.newHeadsUpView,
+ boolean isNewView = !canReapplyRemoteView(result.newHeadsUpView,
entry.cachedHeadsUpContentView);
ApplyCallback applyCallback = new ApplyCallback() {
@Override
@@ -264,7 +264,7 @@
flag = FLAG_REINFLATE_PUBLIC_VIEW;
if ((reInflateFlags & flag) != 0) {
- boolean isNewView = !compareRemoteViews(result.newPublicView,
+ boolean isNewView = !canReapplyRemoteView(result.newPublicView,
entry.cachedPublicContentView);
ApplyCallback applyCallback = new ApplyCallback() {
@Override
@@ -288,7 +288,7 @@
if ((reInflateFlags & flag) != 0) {
NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout;
boolean isNewView = !canReapplyAmbient(row, redactAmbient) ||
- !compareRemoteViews(result.newAmbientView, entry.cachedAmbientContentView);
+ !canReapplyRemoteView(result.newAmbientView, entry.cachedAmbientContentView);
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -486,14 +486,21 @@
return builder.createContentView(useLarge);
}
- // Returns true if the RemoteViews are the same.
- private static boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) {
- return (a == null && b == null) ||
- (a != null && b != null
- && b.getPackage() != null
- && a.getPackage() != null
- && a.getPackage().equals(b.getPackage())
- && a.getLayoutId() == b.getLayoutId());
+ /**
+ * @param newView The new view that will be applied
+ * @param oldView The old view that was applied to the existing view before
+ * @return {@code true} if the RemoteViews are the same and the view can be reused to reapply.
+ */
+ @VisibleForTesting
+ static boolean canReapplyRemoteView(final RemoteViews newView,
+ final RemoteViews oldView) {
+ return (newView == null && oldView == null) ||
+ (newView != null && oldView != null
+ && oldView.getPackage() != null
+ && newView.getPackage() != null
+ && newView.getPackage().equals(oldView.getPackage())
+ && newView.getLayoutId() == oldView.getLayoutId()
+ && !oldView.isReapplyDisallowed());
}
public void setInflationCallback(InflationCallback callback) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index e65bab6..f7aa818 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -151,7 +151,7 @@
progress = Math.max(0.0f, Math.min(progress, 1.0f));
progress = mAccelerateInterpolator.getInterpolation(progress);
progress *= Math.pow(1 + mEmptyDragAmount / mDensity / 300, 0.3f);
- return progress;
+ return interpolate(progress, 1, mDarkAmount);
}
private int getClockNotificationsPadding() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
index fb64a7f..54a96cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
@@ -198,6 +198,18 @@
runningTask.abort();
}
+ @Test
+ public void doesntReapplyDisallowedRemoteView() throws Exception {
+ mBuilder.setStyle(new Notification.MediaStyle());
+ RemoteViews mediaView = mBuilder.createContentView();
+ mBuilder.setStyle(new Notification.DecoratedCustomViewStyle());
+ mBuilder.setCustomContentView(new RemoteViews(getContext().getPackageName(),
+ R.layout.custom_view_dark));
+ RemoteViews decoratedMediaView = mBuilder.createContentView();
+ Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
+ NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
+ }
+
public static void runThenWaitForInflation(Runnable block,
NotificationInflater inflater) throws Exception {
runThenWaitForInflation(block, false /* expectingException */, inflater);
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 4393e35..bcc74c0 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -32,11 +32,13 @@
import android.net.RouteInfo;
import android.net.util.SharedLog;
import android.os.Handler;
+import android.os.Looper;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.TextUtils;
+import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
import com.android.internal.util.IndentingPrintWriter;
@@ -46,8 +48,10 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
@@ -79,8 +83,8 @@
// Maps upstream interface names to offloaded traffic statistics.
// Always contains the latest value received from the hardware for each interface, regardless of
// whether offload is currently running on that interface.
- private HashMap<String, OffloadHardwareInterface.ForwardedStats>
- mForwardedStats = new HashMap<>();
+ private ConcurrentHashMap<String, ForwardedStats> mForwardedStats =
+ new ConcurrentHashMap<>(16, 0.75F, 1);
// Maps upstream interface names to interface quotas.
// Always contains the latest value received from the framework for each interface, regardless
@@ -205,27 +209,29 @@
private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
@Override
public NetworkStats getTetherStats(int how) {
+ // getTetherStats() is the only function in OffloadController that can be called from
+ // a different thread. Do not attempt to update stats by querying the offload HAL
+ // synchronously from a different thread than our Handler thread. http://b/64771555.
+ Runnable updateStats = () -> { updateStatsForCurrentUpstream(); };
+ if (Looper.myLooper() == mHandler.getLooper()) {
+ updateStats.run();
+ } else {
+ mHandler.post(updateStats);
+ }
+
NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
+ NetworkStats.Entry entry = new NetworkStats.Entry();
+ entry.set = SET_DEFAULT;
+ entry.tag = TAG_NONE;
+ entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL;
- // We can't just post to mHandler because we are mostly (but not always) called by
- // NetworkStatsService#performPollLocked, which is (currently) on the same thread as us.
- mHandler.runWithScissors(() -> {
- // We have to report both per-interface and per-UID stats, because offloaded traffic
- // is not seen by kernel interface counters.
- NetworkStats.Entry entry = new NetworkStats.Entry();
- entry.set = SET_DEFAULT;
- entry.tag = TAG_NONE;
- entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL;
-
- updateStatsForCurrentUpstream();
-
- for (String iface : mForwardedStats.keySet()) {
- entry.iface = iface;
- entry.rxBytes = mForwardedStats.get(iface).rxBytes;
- entry.txBytes = mForwardedStats.get(iface).txBytes;
- stats.addValues(entry);
- }
- }, 0);
+ for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
+ ForwardedStats value = kv.getValue();
+ entry.iface = kv.getKey();
+ entry.rxBytes = value.rxBytes;
+ entry.txBytes = value.txBytes;
+ stats.addValues(entry);
+ }
return stats;
}
@@ -247,10 +253,21 @@
return;
}
- if (!mForwardedStats.containsKey(iface)) {
- mForwardedStats.put(iface, new OffloadHardwareInterface.ForwardedStats());
+ // Always called on the handler thread.
+ //
+ // Use get()/put() instead of updating ForwardedStats in place because we can be called
+ // concurrently with getTetherStats. In combination with the guarantees provided by
+ // ConcurrentHashMap, this ensures that getTetherStats always gets the most recent copy of
+ // the stats for each interface, and does not observe partial writes where rxBytes is
+ // updated and txBytes is not.
+ ForwardedStats diff = mHwInterface.getForwardedStats(iface);
+ ForwardedStats base = mForwardedStats.get(iface);
+ if (base != null) {
+ diff.add(base);
}
- mForwardedStats.get(iface).add(mHwInterface.getForwardedStats(iface));
+ mForwardedStats.put(iface, diff);
+ // diff is a new object, just created by getForwardedStats(). Therefore, anyone reading from
+ // mForwardedStats (i.e., any caller of getTetherStats) will see the new stats immediately.
}
private boolean maybeUpdateDataLimit(String iface) {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 53a9b5a..c7290e7 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -400,23 +400,39 @@
when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(mobileStats);
+ InOrder inOrder = inOrder(mHardware);
+
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
+ // Previous upstream was null, so no stats are fetched.
+ inOrder.verify(mHardware, never()).getForwardedStats(any());
lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp);
+ // Expect that we fetch stats from the previous upstream.
+ inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
+ // Expect that we fetch stats from the previous upstream.
+ inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));
+ ethernetStats = new ForwardedStats();
ethernetStats.rxBytes = 100000;
ethernetStats.txBytes = 100000;
+ when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
offload.setUpstreamLinkProperties(null);
+ // Expect that we fetch stats from the previous upstream.
+ inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE);
NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID);
+ waitForIdle();
+ // There is no current upstream, so no stats are fetched.
+ inOrder.verify(mHardware, never()).getForwardedStats(eq(ethernetIface));
+ inOrder.verifyNoMoreInteractions();
assertEquals(2, stats.size());
assertEquals(2, perUidStats.size());