Merge "Only enable wide color gamut support on capable devices" into oc-dr1-dev
diff --git a/api/current.txt b/api/current.txt
index 5dd3839..d7f8ecd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6089,13 +6089,17 @@
public final class WallpaperColors implements android.os.Parcelable {
ctor public WallpaperColors(android.os.Parcel);
- ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>);
- ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>, boolean);
+ ctor public WallpaperColors(android.graphics.Color, android.graphics.Color, android.graphics.Color, int);
method public int describeContents();
- method public java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>> getColors();
- method public boolean supportsDarkText();
+ method public static android.app.WallpaperColors fromBitmap(android.graphics.Bitmap);
+ method public static android.app.WallpaperColors fromDrawable(android.graphics.drawable.Drawable);
+ method public int getColorHints();
+ method public android.graphics.Color getPrimaryColor();
+ method public android.graphics.Color getSecondaryColor();
+ method public android.graphics.Color getTertiaryColor();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
}
public final class WallpaperInfo implements android.os.Parcelable {
@@ -37581,12 +37585,10 @@
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
- method public void invalidateColors();
method public boolean isPreview();
method public boolean isVisible();
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
- method public android.app.WallpaperColors onComputeWallpaperColors();
method public void onCreate(android.view.SurfaceHolder);
method public void onDesiredSizeChanged(int, int);
method public void onDestroy();
diff --git a/api/system-current.txt b/api/system-current.txt
index dfad7a2..85e9175 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6299,13 +6299,17 @@
public final class WallpaperColors implements android.os.Parcelable {
ctor public WallpaperColors(android.os.Parcel);
- ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>);
- ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>, boolean);
+ ctor public WallpaperColors(android.graphics.Color, android.graphics.Color, android.graphics.Color, int);
method public int describeContents();
- method public java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>> getColors();
- method public boolean supportsDarkText();
+ method public static android.app.WallpaperColors fromBitmap(android.graphics.Bitmap);
+ method public static android.app.WallpaperColors fromDrawable(android.graphics.drawable.Drawable);
+ method public int getColorHints();
+ method public android.graphics.Color getPrimaryColor();
+ method public android.graphics.Color getSecondaryColor();
+ method public android.graphics.Color getTertiaryColor();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
}
public final class WallpaperInfo implements android.os.Parcelable {
@@ -40793,12 +40797,10 @@
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
- method public void invalidateColors();
method public boolean isPreview();
method public boolean isVisible();
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
- method public android.app.WallpaperColors onComputeWallpaperColors();
method public void onCreate(android.view.SurfaceHolder);
method public void onDesiredSizeChanged(int, int);
method public void onDestroy();
diff --git a/api/test-current.txt b/api/test-current.txt
index 9bafd5f..ed9071a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6110,13 +6110,17 @@
public final class WallpaperColors implements android.os.Parcelable {
ctor public WallpaperColors(android.os.Parcel);
- ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>);
- ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>, boolean);
+ ctor public WallpaperColors(android.graphics.Color, android.graphics.Color, android.graphics.Color, int);
method public int describeContents();
- method public java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>> getColors();
- method public boolean supportsDarkText();
+ method public static android.app.WallpaperColors fromBitmap(android.graphics.Bitmap);
+ method public static android.app.WallpaperColors fromDrawable(android.graphics.drawable.Drawable);
+ method public int getColorHints();
+ method public android.graphics.Color getPrimaryColor();
+ method public android.graphics.Color getSecondaryColor();
+ method public android.graphics.Color getTertiaryColor();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
}
public final class WallpaperInfo implements android.os.Parcelable {
@@ -37788,12 +37792,10 @@
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
- method public void invalidateColors();
method public boolean isPreview();
method public boolean isVisible();
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
- method public android.app.WallpaperColors onComputeWallpaperColors();
method public void onCreate(android.view.SurfaceHolder);
method public void onDesiredSizeChanged(int, int);
method public void onDestroy();
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 8eefd25..4966b43 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -382,6 +382,7 @@
oldAnims = mWm.getAnimationScales();
mWm.setAnimationScale(0, 0.0f);
mWm.setAnimationScale(1, 0.0f);
+ mWm.setAnimationScale(2, 0.0f);
}
// Figure out which component we are tring to do.
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 607e6e0..71be632 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -251,5 +251,7 @@
if (mapbase != MAP_FAILED) {
munmap((void *)mapbase, mapsize);
}
- return 0;
+
+ // b/36066697: Avoid running static destructors.
+ _exit(1);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3574f8d..bc6e9cd 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,17 +16,9 @@
package android.app;
-import android.graphics.Rect;
-import android.view.ViewRootImpl.ActivityConfigCallback;
-import android.view.autofill.AutofillManager;
-import android.view.autofill.AutofillPopupWindow;
-import android.view.autofill.IAutofillWindowPresenter;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.app.ToolbarActionBar;
-import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.policy.DecorView;
-import com.android.internal.policy.PhoneWindow;
+import static android.os.Build.VERSION_CODES.O;
+
+import static java.lang.Character.MIN_VALUE;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
@@ -62,6 +54,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.session.MediaController;
@@ -114,15 +107,26 @@
import android.view.ViewGroup.LayoutParams;
import android.view.ViewManager;
import android.view.ViewRootImpl;
+import android.view.ViewRootImpl.ActivityConfigCallback;
import android.view.Window;
import android.view.Window.WindowControllerCallback;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillPopupWindow;
+import android.view.autofill.IAutofillWindowPresenter;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ToolbarActionBar;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.DecorView;
+import com.android.internal.policy.PhoneWindow;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -131,9 +135,6 @@
import java.util.HashMap;
import java.util.List;
-import static android.os.Build.VERSION_CODES.O;
-import static java.lang.Character.MIN_VALUE;
-
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -719,7 +720,7 @@
public static final int FINISH_TASK_WITH_ACTIVITY = 2;
static final String FRAGMENTS_TAG = "android:fragments";
- private static final String LAST_ACCESSIBILITY_ID = "android:lastAccessibilityId";
+ private static final String LAST_AUTOFILL_ID = "android:lastAutofillId";
private static final String AUTOFILL_RESET_NEEDED = "@android:autofillResetNeeded";
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
@@ -853,8 +854,8 @@
private boolean mAutoFillResetNeeded;
- /** The last accessibility id that was returned from {@link #getNextAccessibilityId()} */
- private int mLastAccessibilityId = View.LAST_APP_ACCESSIBILITY_ID;
+ /** The last autofill id that was returned from {@link #getNextAutofillId()} */
+ private int mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
private AutofillPopupWindow mAutofillPopupWindow;
@@ -999,7 +1000,8 @@
}
if (savedInstanceState != null) {
mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED, false);
- mLastAccessibilityId = savedInstanceState.getInt(LAST_ACCESSIBILITY_ID, View.NO_ID);
+ mLastAutofillId = savedInstanceState.getInt(LAST_AUTOFILL_ID,
+ View.LAST_APP_AUTOFILL_ID);
if (mAutoFillResetNeeded) {
getAutofillManager().onCreate(savedInstanceState);
@@ -1348,24 +1350,23 @@
}
/**
- * Gets the next accessibility ID.
+ * Gets the next autofill ID.
*
- * <p>All IDs will be bigger than {@link View#LAST_APP_ACCESSIBILITY_ID}. All IDs returned
+ * <p>All IDs will be bigger than {@link View#LAST_APP_AUTOFILL_ID}. All IDs returned
* will be unique.
*
* @return A ID that is unique in the activity
*
* {@hide}
*/
- @Override
- public int getNextAccessibilityId() {
- if (mLastAccessibilityId == Integer.MAX_VALUE - 1) {
- mLastAccessibilityId = View.LAST_APP_ACCESSIBILITY_ID;
+ public int getNextAutofillId() {
+ if (mLastAutofillId == Integer.MAX_VALUE - 1) {
+ mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
}
- mLastAccessibilityId++;
+ mLastAutofillId++;
- return mLastAccessibilityId;
+ return mLastAutofillId;
}
/**
@@ -1563,7 +1564,7 @@
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
- outState.putInt(LAST_ACCESSIBILITY_ID, mLastAccessibilityId);
+ outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
@@ -7455,7 +7456,7 @@
/** @hide */
@Override
- @NonNull public View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds) {
+ @NonNull public View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds) {
final View[] views = new View[viewIds.length];
final ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
@@ -7466,7 +7467,7 @@
if (rootView != null) {
for (int viewNum = 0; viewNum < viewIds.length; viewNum++) {
if (views[viewNum] == null) {
- views[viewNum] = rootView.findViewByAccessibilityIdTraversal(
+ views[viewNum] = rootView.findViewByAutofillIdTraversal(
viewIds[viewNum]);
}
}
@@ -7478,14 +7479,14 @@
/** @hide */
@Override
- @Nullable public View findViewByAccessibilityIdTraversal(int viewId) {
+ @Nullable public View findViewByAutofillIdTraversal(int viewId) {
final ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
final View rootView = roots.get(rootNum).getView();
if (rootView != null) {
- final View view = rootView.findViewByAccessibilityIdTraversal(viewId);
+ final View view = rootView.findViewByAutofillIdTraversal(viewId);
if (view != null) {
return view;
}
@@ -7499,7 +7500,7 @@
@Override
@NonNull public boolean[] getViewVisibility(@NonNull int[] viewIds) {
final boolean[] isVisible = new boolean[viewIds.length];
- final View views[] = findViewsByAccessibilityIdTraversal(viewIds);
+ final View views[] = findViewsByAutofillIdTraversal(viewIds);
for (int i = 0; i < viewIds.length; i++) {
View view = views[i];
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index cbb93a0..6dead3e 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1244,7 +1244,7 @@
// Once we parcel the thumbnail for transfering over to the system, create a copy of
// the bitmap to a hardware bitmap and pass through the GraphicBuffer
if (mThumbnail != null) {
- final Bitmap hwBitmap = mThumbnail.copy(Config.HARDWARE, true /* immutable */);
+ final Bitmap hwBitmap = mThumbnail.copy(Config.HARDWARE, false /* isMutable */);
if (hwBitmap != null) {
b.putParcelable(KEY_ANIM_THUMBNAIL, hwBitmap.createGraphicBufferHandle());
} else {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 8c64129..a040520f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -256,28 +256,34 @@
@Override
public void setTheme(int resId) {
- if (mThemeResource != resId) {
- mThemeResource = resId;
- initializeTheme();
+ synchronized (mSync) {
+ if (mThemeResource != resId) {
+ mThemeResource = resId;
+ initializeTheme();
+ }
}
}
@Override
public int getThemeResId() {
- return mThemeResource;
+ synchronized (mSync) {
+ return mThemeResource;
+ }
}
@Override
public Resources.Theme getTheme() {
- if (mTheme != null) {
+ synchronized (mSync) {
+ if (mTheme != null) {
+ return mTheme;
+ }
+
+ mThemeResource = Resources.selectDefaultTheme(mThemeResource,
+ getOuterContext().getApplicationInfo().targetSdkVersion);
+ initializeTheme();
+
return mTheme;
}
-
- mThemeResource = Resources.selectDefaultTheme(mThemeResource,
- getOuterContext().getApplicationInfo().targetSdkVersion);
- initializeTheme();
-
- return mTheme;
}
private void initializeTheme() {
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index b3c70a4..8f172ba 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -16,63 +16,206 @@
package android.app;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Size;
-import android.util.Pair;
+import com.android.internal.graphics.palette.Palette;
+import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
- * A class containing information about the colors of a wallpaper.
+ * Provides information about the colors of a wallpaper.
+ * <p>
+ * This class contains two main components:
+ * <ul>
+ * <li>Named colors: Most visually representative colors of a wallpaper. Can be either
+ * {@link WallpaperColors#getPrimaryColor()}, {@link WallpaperColors#getSecondaryColor()}
+ * or {@link WallpaperColors#getTertiaryColor()}.
+ * </li>
+ * <li>Hints: How colors may affect other system components. Currently the only supported hint is
+ * {@link WallpaperColors#HINT_SUPPORTS_DARK_TEXT}, which specifies if dark text is preferred
+ * over the wallpaper.</li>
+ * </ul>
*/
public final class WallpaperColors implements Parcelable {
- private static final float BRIGHT_LUMINANCE = 0.9f;
- private final List<Pair<Color, Integer>> mColors;
- private final boolean mSupportsDarkText;
+ /**
+ * Specifies that dark text is preferred over the current wallpaper for best presentation.
+ * <p>
+ * eg. A launcher may set its text color to black if this flag is specified.
+ */
+ public static final int HINT_SUPPORTS_DARK_TEXT = 0x1;
+
+ // Maximum size that a bitmap can have to keep our calculations sane
+ private static final int MAX_BITMAP_SIZE = 112;
+
+ // Even though we have a maximum size, we'll mainly match bitmap sizes
+ // using the area instead. This way our comparisons are aspect ratio independent.
+ private static final int MAX_WALLPAPER_EXTRACTION_AREA = MAX_BITMAP_SIZE * MAX_BITMAP_SIZE;
+
+ // When extracting the main colors, only consider colors
+ // present in at least MIN_COLOR_OCCURRENCE of the image
+ private static final float MIN_COLOR_OCCURRENCE = 0.05f;
+
+ // Minimum mean luminosity that an image needs to have to support dark text
+ private static final float BRIGHT_IMAGE_MEAN_LUMINANCE = 0.9f;
+ // We also check if the image has dark pixels in it,
+ // to avoid bright images with some dark spots.
+ private static final float DARK_PIXEL_LUMINANCE = 0.45f;
+ private static final float MAX_DARK_AREA = 0.05f;
+
+ private final ArrayList<Color> mMainColors;
+ private int mColorHints;
public WallpaperColors(Parcel parcel) {
- mColors = new ArrayList<>();
- int count = parcel.readInt();
- for (int i=0; i < count; i++) {
- Color color = Color.valueOf(parcel.readInt());
- int weight = parcel.readInt();
- mColors.add(new Pair<>(color, weight));
+ mMainColors = new ArrayList<>();
+ final int count = parcel.readInt();
+ for (int i = 0; i < count; i++) {
+ final int colorInt = parcel.readInt();
+ Color color = Color.valueOf(colorInt);
+ mMainColors.add(color);
}
- mSupportsDarkText = parcel.readBoolean();
+ mColorHints = parcel.readInt();
}
/**
- * Wallpaper color details containing a list of colors and their weights,
- * as if it were an histogram.
- * This list can be extracted from a bitmap by the Palette API.
+ * Constructs {@link WallpaperColors} from a drawable.
+ * <p>
+ * Main colors will be extracted from the drawable and hints will be calculated.
*
- * Dark text support will be calculated internally based on the histogram.
- *
- * @param colors list of pairs where each pair contains a color
- * and number of occurrences/influence.
+ * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
+ * @param drawable Source where to extract from.
*/
- public WallpaperColors(List<Pair<Color, Integer>> colors) {
- this(colors, calculateDarkTextSupport(colors));
+ public static WallpaperColors fromDrawable(Drawable drawable) {
+ int width = drawable.getIntrinsicWidth();
+ int height = drawable.getIntrinsicHeight();
+
+ // Some drawables do not have intrinsic dimensions
+ if (width <= 0 || height <= 0) {
+ width = MAX_BITMAP_SIZE;
+ height = MAX_BITMAP_SIZE;
+ }
+
+ Size optimalSize = calculateOptimalSize(width, height);
+ Bitmap bitmap = Bitmap.createBitmap(optimalSize.getWidth(), optimalSize.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ final Canvas bmpCanvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ drawable.draw(bmpCanvas);
+
+ final WallpaperColors colors = WallpaperColors.fromBitmap(bitmap);
+ bitmap.recycle();
+
+ return colors;
}
/**
- * Wallpaper color details containing a list of colors and their weights,
- * as if it were an histogram.
- * Explicit dark text support.
+ * Constructs {@link WallpaperColors} from a bitmap.
+ * <p>
+ * Main colors will be extracted from the bitmap and hints will be calculated.
*
- * @param colors list of pairs where each pair contains a color
- * and number of occurrences/influence.
- * @param supportsDarkText can have dark text on top or not
+ * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
+ * @param bitmap Source where to extract from.
*/
- public WallpaperColors(List<Pair<Color, Integer>> colors, boolean supportsDarkText) {
- if (colors == null)
- colors = new ArrayList<>();
- mColors = colors;
- mSupportsDarkText = supportsDarkText;
+ public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap) {
+ if (bitmap == null) {
+ throw new IllegalArgumentException("Bitmap can't be null");
+ }
+
+ final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
+ if (bitmapArea > MAX_WALLPAPER_EXTRACTION_AREA) {
+ Size optimalSize = calculateOptimalSize(bitmap.getWidth(), bitmap.getHeight());
+ Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, optimalSize.getWidth(),
+ optimalSize.getHeight(), true /* filter */);
+ bitmap.recycle();
+ bitmap = scaledBitmap;
+ }
+
+ final Palette palette = Palette
+ .from(bitmap)
+ .setQuantizer(new VariationalKMeansQuantizer())
+ .maximumColorCount(5)
+ .clearFilters()
+ .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA)
+ .generate();
+
+ // Remove insignificant colors and sort swatches by population
+ final ArrayList<Palette.Swatch> swatches = new ArrayList<>(palette.getSwatches());
+ final float minColorArea = bitmap.getWidth() * bitmap.getHeight() * MIN_COLOR_OCCURRENCE;
+ swatches.removeIf(s -> s.getPopulation() < minColorArea);
+ swatches.sort((a, b) -> b.getPopulation() - a.getPopulation());
+
+ final int swatchesSize = swatches.size();
+ Color primary = null, secondary = null, tertiary = null;
+
+ swatchLoop:
+ for (int i = 0; i < swatchesSize; i++) {
+ Color color = Color.valueOf(swatches.get(i).getRgb());
+ switch (i) {
+ case 0:
+ primary = color;
+ break;
+ case 1:
+ secondary = color;
+ break;
+ case 2:
+ tertiary = color;
+ break;
+ default:
+ // out of bounds
+ break swatchLoop;
+ }
+ }
+
+ int hints = 0;
+ if (calculateDarkTextSupport(bitmap)) {
+ hints |= HINT_SUPPORTS_DARK_TEXT;
+ }
+ return new WallpaperColors(primary, secondary, tertiary, hints);
+ }
+
+ /**
+ * Constructs a new object from three colors, where hints can be specified.
+ *
+ * @param primaryColor Primary color.
+ * @param secondaryColor Secondary color.
+ * @param tertiaryColor Tertiary color.
+ * @param colorHints A combination of WallpaperColor hints.
+ * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
+ * @see WallpaperColors#fromBitmap(Bitmap)
+ * @see WallpaperColors#fromDrawable(Drawable)
+ */
+ public WallpaperColors(@NonNull Color primaryColor, @Nullable Color secondaryColor,
+ @Nullable Color tertiaryColor, int colorHints) {
+
+ if (primaryColor == null) {
+ throw new IllegalArgumentException("Primary color should never be null.");
+ }
+
+ mMainColors = new ArrayList<>(3);
+ mMainColors.add(primaryColor);
+ if (secondaryColor != null) {
+ mMainColors.add(secondaryColor);
+ }
+ if (tertiaryColor != null) {
+ if (secondaryColor == null) {
+ throw new IllegalArgumentException("tertiaryColor can't be specified when "
+ + "secondaryColor is null");
+ }
+ mMainColors.add(tertiaryColor);
+ }
+
+ mColorHints = colorHints;
}
public static final Creator<WallpaperColors> CREATOR = new Creator<WallpaperColors>() {
@@ -94,21 +237,53 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- int count = mColors.size();
+ List<Color> mainColors = getMainColors();
+ int count = mainColors.size();
dest.writeInt(count);
- for (Pair<Color, Integer> color : mColors) {
- dest.writeInt(color.first.toArgb());
- dest.writeInt(color.second);
+ for (int i = 0; i < count; i++) {
+ Color color = mainColors.get(i);
+ dest.writeInt(color.toArgb());
}
- dest.writeBoolean(mSupportsDarkText);
+ dest.writeInt(mColorHints);
}
/**
- * List of colors with their occurrences. The bigger the int, the more relevant the color.
- * @return list of colors paired with their weights.
+ * Gets the most visually representative color of the wallpaper.
+ * "Visually representative" means easily noticeable in the image,
+ * probably happening at high frequency.
+ *
+ * @return A color.
*/
- public List<Pair<Color, Integer>> getColors() {
- return mColors;
+ public @NonNull Color getPrimaryColor() {
+ return mMainColors.get(0);
+ }
+
+ /**
+ * Gets the second most preeminent color of the wallpaper. Can be null.
+ *
+ * @return A color, may be null.
+ */
+ public @Nullable Color getSecondaryColor() {
+ return mMainColors.size() < 2 ? null : mMainColors.get(1);
+ }
+
+ /**
+ * Gets the third most preeminent color of the wallpaper. Can be null.
+ *
+ * @return A color, may be null.
+ */
+ public @Nullable Color getTertiaryColor() {
+ return mMainColors.size() < 3 ? null : mMainColors.get(2);
+ }
+
+ /**
+ * List of most preeminent colors, sorted by importance.
+ *
+ * @return List of colors.
+ * @hide
+ */
+ public @NonNull List<Color> getMainColors() {
+ return Collections.unmodifiableList(mMainColors);
}
@Override
@@ -118,38 +293,91 @@
}
WallpaperColors other = (WallpaperColors) o;
- return mColors.equals(other.mColors) && mSupportsDarkText == other.mSupportsDarkText;
+ return mMainColors.equals(other.mMainColors)
+ && mColorHints == other.mColorHints;
}
@Override
public int hashCode() {
- return 31 * mColors.hashCode() + (mSupportsDarkText ? 1 : 0);
+ return 31 * mMainColors.hashCode() + mColorHints;
}
/**
- * Whether or not dark text is legible on top of this wallpaper.
+ * Combination of WallpaperColor hints.
*
- * @return true if dark text is supported
+ * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
+ * @return True if dark text is supported.
*/
- public boolean supportsDarkText() {
- return mSupportsDarkText;
+ public int getColorHints() {
+ return mColorHints;
}
- private static boolean calculateDarkTextSupport(List<Pair<Color, Integer>> colors) {
- if (colors == null) {
+ /**
+ * @param colorHints Combination of WallpaperColors hints.
+ * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT
+ * @hide
+ */
+ public void setColorHints(int colorHints) {
+ mColorHints = colorHints;
+ }
+
+ /**
+ * Checks if image is bright and clean enough to support light text.
+ *
+ * @param source What to read.
+ * @return Whether image supports dark text or not.
+ */
+ private static boolean calculateDarkTextSupport(Bitmap source) {
+ if (source == null) {
return false;
}
- Pair<Color, Integer> mainColor = null;
+ int[] pixels = new int[source.getWidth() * source.getHeight()];
+ double totalLuminance = 0;
+ final int maxDarkPixels = (int) (pixels.length * MAX_DARK_AREA);
+ int darkPixels = 0;
+ source.getPixels(pixels, 0 /* offset */, source.getWidth(), 0 /* x */, 0 /* y */,
+ source.getWidth(), source.getHeight());
- for (Pair<Color, Integer> color : colors) {
- if (mainColor == null) {
- mainColor = color;
- } else if (color.second > mainColor.second) {
- mainColor = color;
+ // This bitmap was already resized to fit the maximum allowed area.
+ // Let's just loop through the pixels, no sweat!
+ for (int i = 0; i < pixels.length; i++) {
+ final float luminance = Color.luminance(pixels[i]);
+ final int alpha = Color.alpha(pixels[i]);
+
+ // Make sure we don't have a dark pixel mass that will
+ // make text illegible.
+ if (luminance < DARK_PIXEL_LUMINANCE && alpha != 0) {
+ darkPixels++;
+ if (darkPixels > maxDarkPixels) {
+ return false;
+ }
}
+
+ totalLuminance += luminance;
}
- return mainColor != null &&
- mainColor.first.luminance() > BRIGHT_LUMINANCE;
+ return totalLuminance / pixels.length > BRIGHT_IMAGE_MEAN_LUMINANCE;
+ }
+
+ private static Size calculateOptimalSize(int width, int height) {
+ // Calculate how big the bitmap needs to be.
+ // This avoids unnecessary processing and allocation inside Palette.
+ final int requestedArea = width * height;
+ double scale = 1;
+ if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
+ scale = Math.sqrt(MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea);
+ }
+ int newWidth = (int) (width * scale);
+ int newHeight = (int) (height * scale);
+ // Dealing with edge cases of the drawable being too wide or too tall.
+ // Width or height would end up being 0, in this case we'll set it to 1.
+ if (newWidth == 0) {
+ newWidth = 1;
+ }
+ if (newHeight == 0) {
+ newHeight = 1;
+ }
+
+ return new Size(newWidth, newHeight);
}
}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 266fa7e..4e8277c 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1051,6 +1051,9 @@
public void updateAutofillValue(AutofillValue value) {
mAutofillValue = value;
if (value.isText()) {
+ if (mText == null) {
+ mText = new ViewNodeText();
+ }
mText.mText = value.getTextValue();
}
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 67d56d5..dfd5996 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -208,6 +208,8 @@
if (wrapper == null) return;
stopAdvertisingSet(wrapper);
+
+ mLegacyAdvertisers.remove(callback);
}
}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index dabe608..86a30cf 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -214,10 +214,12 @@
return;
}
try {
- mService.requestNotificationAccess(component).send();
+ IntentSender intentSender = mService.requestNotificationAccess(component)
+ .getIntentSender();
+ mContext.startIntentSender(intentSender, null, 0, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } catch (PendingIntent.CanceledException e) {
+ } catch (IntentSender.SendIntentException e) {
throw new RuntimeException(e);
}
}
@@ -288,6 +290,7 @@
@Override
public void onActivityDestroyed(Activity activity) {
+ if (activity != getActivity()) return;
try {
mService.stopScan(mRequest, this, getCallingPackage());
} catch (RemoteException e) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2f79538..5929aca 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -487,27 +487,27 @@
*/
public abstract Context getApplicationContext();
- /** Non-activity related accessibility ids are unique in the app */
- private static int sLastAccessibilityId = View.NO_ID;
+ /** Non-activity related autofill ids are unique in the app */
+ private static int sLastAutofillId = View.NO_ID;
/**
- * Gets the next accessibility ID.
+ * Gets the next autofill ID.
*
- * <p>All IDs will be smaller or the same as {@link View#LAST_APP_ACCESSIBILITY_ID}. All IDs
+ * <p>All IDs will be smaller or the same as {@link View#LAST_APP_AUTOFILL_ID}. All IDs
* returned will be unique.
*
* @return A ID that is unique in the process
*
* {@hide}
*/
- public int getNextAccessibilityId() {
- if (sLastAccessibilityId == View.LAST_APP_ACCESSIBILITY_ID - 1) {
- sLastAccessibilityId = View.NO_ID;
+ public int getNextAutofillId() {
+ if (sLastAutofillId == View.LAST_APP_AUTOFILL_ID - 1) {
+ sLastAutofillId = View.NO_ID;
}
- sLastAccessibilityId++;
+ sLastAutofillId++;
- return sLastAccessibilityId;
+ return sLastAutofillId;
}
/**
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 20fafb2..c719c64 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -961,8 +961,7 @@
/**
* @hide
*/
- @Override
- public int getNextAccessibilityId() {
- return mBase.getNextAccessibilityId();
+ public int getNextAutofillId() {
+ return mBase.getNextAutofillId();
}
}
diff --git a/core/java/android/content/SyncStatusInfo.java b/core/java/android/content/SyncStatusInfo.java
index bb24ccd..663e6e4 100644
--- a/core/java/android/content/SyncStatusInfo.java
+++ b/core/java/android/content/SyncStatusInfo.java
@@ -24,7 +24,11 @@
/** @hide */
public class SyncStatusInfo implements Parcelable {
- static final int VERSION = 2;
+ private static final String TAG = "Sync";
+
+ static final int VERSION = 3;
+
+ private static final int MAX_EVENT_COUNT = 10;
public final int authorityId;
public long totalElapsedTime;
@@ -47,7 +51,8 @@
// no race conditions when accessing this list
private ArrayList<Long> periodicSyncTimes;
- private static final String TAG = "Sync";
+ private final ArrayList<Long> mLastEventTimes = new ArrayList<>();
+ private final ArrayList<String> mLastEvents = new ArrayList<>();
public SyncStatusInfo(int authorityId) {
this.authorityId = authorityId;
@@ -92,6 +97,11 @@
} else {
parcel.writeInt(-1);
}
+ parcel.writeInt(mLastEventTimes.size());
+ for (int i = 0; i < mLastEventTimes.size(); i++) {
+ parcel.writeLong(mLastEventTimes.get(i));
+ parcel.writeString(mLastEvents.get(i));
+ }
}
public SyncStatusInfo(Parcel parcel) {
@@ -117,15 +127,24 @@
if (version == 1) {
periodicSyncTimes = null;
} else {
- int N = parcel.readInt();
- if (N < 0) {
+ final int count = parcel.readInt();
+ if (count < 0) {
periodicSyncTimes = null;
} else {
periodicSyncTimes = new ArrayList<Long>();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < count; i++) {
periodicSyncTimes.add(parcel.readLong());
}
}
+ if (version >= 3) {
+ mLastEventTimes.clear();
+ mLastEvents.clear();
+ final int nEvents = parcel.readInt();
+ for (int i = 0; i < nEvents; i++) {
+ mLastEventTimes.add(parcel.readLong());
+ mLastEvents.add(parcel.readString());
+ }
+ }
}
}
@@ -149,6 +168,8 @@
if (other.periodicSyncTimes != null) {
periodicSyncTimes = new ArrayList<Long>(other.periodicSyncTimes);
}
+ mLastEventTimes.addAll(other.mLastEventTimes);
+ mLastEvents.addAll(other.mLastEvents);
}
public void setPeriodicSyncTime(int index, long when) {
@@ -172,6 +193,31 @@
}
}
+ /** */
+ public void addEvent(String message) {
+ if (mLastEventTimes.size() >= MAX_EVENT_COUNT) {
+ mLastEventTimes.remove(MAX_EVENT_COUNT - 1);
+ mLastEvents.remove(MAX_EVENT_COUNT - 1);
+ }
+ mLastEventTimes.add(0, System.currentTimeMillis());
+ mLastEvents.add(0, message);
+ }
+
+ /** */
+ public int getEventCount() {
+ return mLastEventTimes.size();
+ }
+
+ /** */
+ public long getEventTime(int i) {
+ return mLastEventTimes.get(i);
+ }
+
+ /** */
+ public String getEvent(int i) {
+ return mLastEvents.get(i);
+ }
+
public static final Creator<SyncStatusInfo> CREATOR = new Creator<SyncStatusInfo>() {
public SyncStatusInfo createFromParcel(Parcel in) {
return new SyncStatusInfo(in);
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 15dbf26..fec7fd9 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -96,6 +96,9 @@
CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder(
requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+ // Carry over userTag, as native metadata doesn't have this field.
+ singleTargetRequestBuilder.setTag(request.getTag());
+
// Overwrite the capture intent to make sure a good value is set.
Iterator<Surface> iterator = outputSurfaces.iterator();
Surface firstSurface = iterator.next();
@@ -118,6 +121,7 @@
requestMetadata = new CameraMetadataNative(request.getNativeCopy());
doubleTargetRequestBuilder = new CaptureRequest.Builder(
requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+ doubleTargetRequestBuilder.setTag(request.getTag());
doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
doubleTargetRequestBuilder.addTarget(firstSurface);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index fcb99b1..f478071 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -602,6 +602,15 @@
public final static int REQUEST_ID_UNSET = 0;
/**
+ * Static unique request used as a tombstone for NetworkCallbacks that have been unregistered.
+ * This allows to distinguish when unregistering NetworkCallbacks those that were never
+ * registered and those that were already unregistered.
+ * @hide
+ */
+ private final static NetworkRequest ALREADY_UNREGISTERED =
+ new NetworkRequest.Builder().clearCapabilities().build();
+
+ /**
* A NetID indicating no Network is selected.
* Keep in sync with bionic/libc/dns/include/resolv_netid.h
* @hide
@@ -2686,10 +2695,6 @@
public void onNetworkResumed(Network network) {}
private NetworkRequest networkRequest;
-
- private boolean isRegistered() {
- return (networkRequest != null) && (networkRequest.requestId != REQUEST_ID_UNSET);
- }
}
/**
@@ -2856,7 +2861,8 @@
final NetworkRequest request;
try {
synchronized(sCallbacks) {
- if (callback.isRegistered()) {
+ if (callback.networkRequest != null
+ && callback.networkRequest != ALREADY_UNREGISTERED) {
// TODO: throw exception instead and enforce 1:1 mapping of callbacks
// and requests (http://b/20701525).
Log.e(TAG, "NetworkCallback was already registered");
@@ -3302,8 +3308,10 @@
// Find all requests associated to this callback and stop callback triggers immediately.
// Callback is reusable immediately. http://b/20701525, http://b/35921499.
synchronized (sCallbacks) {
- Preconditions.checkArgument(
- networkCallback.isRegistered(), "NetworkCallback was not registered");
+ Preconditions.checkArgument(networkCallback.networkRequest != null,
+ "NetworkCallback was not registered");
+ Preconditions.checkArgument(networkCallback.networkRequest != ALREADY_UNREGISTERED,
+ "NetworkCallback was already unregistered");
for (Map.Entry<NetworkRequest, NetworkCallback> e : sCallbacks.entrySet()) {
if (e.getValue() == networkCallback) {
reqs.add(e.getKey());
@@ -3319,7 +3327,7 @@
// Only remove mapping if rpc was successful.
sCallbacks.remove(r);
}
- networkCallback.networkRequest = null;
+ networkCallback.networkRequest = ALREADY_UNREGISTERED;
}
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 0611f17..2b82c77 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -936,6 +936,11 @@
return this;
}
+ Builder disable(int bit) {
+ mMask &= ~bit;
+ return this;
+ }
+
/**
* Construct the VmPolicy instance.
*
@@ -1214,7 +1219,13 @@
if (IS_USER_BUILD) {
setCloseGuardEnabled(false);
} else {
- VmPolicy.Builder policyBuilder = new VmPolicy.Builder().detectAll().penaltyDropBox();
+ VmPolicy.Builder policyBuilder = new VmPolicy.Builder().detectAll();
+ if (!IS_ENG_BUILD) {
+ // Activity leak detection causes too much slowdown for userdebug because of the
+ // GCs.
+ policyBuilder = policyBuilder.disable(DETECT_VM_ACTIVITY_LEAKS);
+ }
+ policyBuilder = policyBuilder.penaltyDropBox();
if (IS_ENG_BUILD) {
policyBuilder.penaltyLog();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1b410e8..88930ba 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5216,6 +5216,15 @@
public static final String USER_SETUP_COMPLETE = "user_setup_complete";
/**
+ * Whether the current user has been set up via setup wizard (0 = false, 1 = true)
+ * This value differs from USER_SETUP_COMPLETE in that it can be reset back to 0
+ * in case SetupWizard has been re-enabled on TV devices.
+ *
+ * @hide
+ */
+ public static final String TV_USER_SETUP_COMPLETE = "tv_user_setup_complete";
+
+ /**
* Prefix for category name that marks whether a suggested action from that category was
* completed.
* @hide
@@ -9939,6 +9948,16 @@
public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature";
/**
+ * Toggle to enable/disable dexopt for instant applications. The default is for dexopt
+ * to be disabled.
+ * <p>
+ * Type: int (0 to disable, 1 to enable)
+ *
+ * @hide
+ */
+ public static final String INSTANT_APP_DEXOPT_ENABLED = "instant_app_dexopt_enabled";
+
+ /**
* The min period for caching installed instant apps in milliseconds.
* <p>
* Type: long
@@ -10088,6 +10107,15 @@
"backup_refactored_service_disabled";
/**
+ * Flag to set the waiting time for euicc factory reset inside System > Settings
+ * Type: long
+ *
+ * @hide
+ */
+ public static final String EUICC_WIPING_TIMEOUT_MILLIS =
+ "euicc_wiping_timeout_millis";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
@@ -10673,6 +10701,13 @@
*/
public static final String ENABLE_CACHE_QUOTA_CALCULATION =
"enable_cache_quota_calculation";
+
+ /**
+ * Whether the Deletion Helper no threshold toggle is available.
+ * @hide
+ */
+ public static final String ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE =
+ "enable_deletion_helper_no_threshold_toggle";
}
/**
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 9df315b..07c3503 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -23,9 +23,7 @@
import android.annotation.SdkConstant;
import android.app.Activity;
import android.app.Service;
-import android.app.assist.AssistStructure;
import android.content.Intent;
-import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.ICancellationSignal;
@@ -35,9 +33,6 @@
import com.android.internal.os.SomeArgs;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Top-level service of the current autofill service for a given user.
*
@@ -192,6 +187,11 @@
* {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
* to notify the result of the request.
*
+ * <p><b>NOTE: </b>to retrieve the actual value of the field, the service should call
+ * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls
+ * {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no
+ * guarantee such method will return the most recent value of the field.
+ *
* @param request the {@link SaveRequest request} to handle.
* See {@link FillResponse} for examples of multiple-sections requests.
* @param callback object used to notify the result of the request.
@@ -207,12 +207,6 @@
public void onDisconnected() {
}
- /** @hide */
- @Deprecated
- public final void disableSelf() {
- getSystemService(AutofillManager.class).disableOwnedAutofillServices();
- }
-
/**
* Returns the {@link FillEventHistory.Event events} since the last {@link FillResponse} was
* returned.
diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java
index 6956c8a..f8a8751 100644
--- a/core/java/android/service/autofill/FillContext.java
+++ b/core/java/android/service/autofill/FillContext.java
@@ -106,15 +106,15 @@
}
/**
- * Finds {@link ViewNode}s that have the requested ids.
+ * Finds {@link ViewNode ViewNodes} that have the requested ids.
*
- * @param ids The ids of the node to find
+ * @param ids The ids of the node to find.
*
- * @return The nodes indexed in the same way as the ids
+ * @return The nodes indexed in the same way as the ids.
*
* @hide
*/
- @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId... ids) {
+ @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId[] ids) {
final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
final ViewNode[] foundNodes = new AssistStructure.ViewNode[ids.length];
@@ -178,6 +178,30 @@
return foundNodes;
}
+ /**
+ * Finds the {@link ViewNode} that has the requested {@code id}, if any.
+ *
+ * @hide
+ */
+ @Nullable public ViewNode findViewNodeByAutofillId(@NonNull AutofillId id) {
+ final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
+ final int numWindowNodes = mStructure.getWindowNodeCount();
+ for (int i = 0; i < numWindowNodes; i++) {
+ nodesToProcess.add(mStructure.getWindowNodeAt(i).getRootViewNode());
+ }
+ while (!nodesToProcess.isEmpty()) {
+ final ViewNode node = nodesToProcess.removeFirst();
+ if (id.equals(node.getAutofillId())) {
+ return node;
+ }
+ for (int i = 0; i < node.getChildCount(); i++) {
+ nodesToProcess.addLast(node.getChildAt(i));
+ }
+ }
+
+ return null;
+ }
+
public static final Parcelable.Creator<FillContext> CREATOR =
new Parcelable.Creator<FillContext>() {
@Override
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index fa3f55b..6ea7d5e 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -273,13 +273,24 @@
*
* <p>See {@link SaveInfo} for more info.
*
- * @throws IllegalArgumentException if {@code requiredIds} is {@code null} or empty.
+ * @throws IllegalArgumentException if {@code requiredIds} is {@code null} or empty, or if
+ * it contains any {@code null} entry.
*/
public Builder(@SaveDataType int type, @NonNull AutofillId[] requiredIds) {
- Preconditions.checkArgument(requiredIds != null && requiredIds.length > 0,
- "must have at least one required id: " + Arrays.toString(requiredIds));
+ // TODO: add CTS unit tests (not integration) to assert the null cases
mType = type;
- mRequiredIds = requiredIds;
+ mRequiredIds = assertValid(requiredIds);
+ }
+
+ private AutofillId[] assertValid(AutofillId[] ids) {
+ Preconditions.checkArgument(ids != null && ids.length > 0,
+ "must have at least one id: " + Arrays.toString(ids));
+ for (int i = 0; i < ids.length; i++) {
+ final AutofillId id = ids[i];
+ Preconditions.checkArgument(id != null,
+ "cannot have null id: " + Arrays.toString(ids));
+ }
+ return ids;
}
/**
@@ -302,12 +313,14 @@
*
* @param ids The ids of the optional views.
* @return This builder.
+ *
+ * @throws IllegalArgumentException if {@code ids} is {@code null} or empty, or if
+ * it contains any {@code null} entry.
*/
- public @NonNull Builder setOptionalIds(@Nullable AutofillId[] ids) {
+ public @NonNull Builder setOptionalIds(@NonNull AutofillId[] ids) {
+ // TODO: add CTS unit tests (not integration) to assert the null cases
throwIfDestroyed();
- if (ids != null && ids.length != 0) {
- mOptionalIds = ids;
- }
+ mOptionalIds = assertValid(ids);
return this;
}
@@ -421,7 +434,10 @@
final Builder builder = new Builder(parcel.readInt(),
parcel.readParcelableArray(null, AutofillId.class));
builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null));
- builder.setOptionalIds(parcel.readParcelableArray(null, AutofillId.class));
+ final AutofillId[] optionalIds = parcel.readParcelableArray(null, AutofillId.class);
+ if (optionalIds != null) {
+ builder.setOptionalIds(optionalIds);
+ }
builder.setDescription(parcel.readCharSequence());
builder.setFlags(parcel.readInt());
return builder.build();
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 8f61d96..875f286 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -25,6 +25,12 @@
import android.telephony.euicc.EuiccInfo;
import android.util.ArraySet;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* Service interface linking the system with an eUICC local profile assistant (LPA) application.
*
@@ -116,10 +122,45 @@
private final IEuiccService.Stub mStubWrapper;
+ private ThreadPoolExecutor mExecutor;
+
public EuiccService() {
mStubWrapper = new IEuiccServiceWrapper();
}
+ @Override
+ @CallSuper
+ public void onCreate() {
+ super.onCreate();
+ // We use a oneway AIDL interface to avoid blocking phone process binder threads on IPCs to
+ // an external process, but doing so means the requests are serialized by binder, which is
+ // not desired. Spin up a background thread pool to allow requests to be parallelized.
+ // TODO(b/38206971): Consider removing this if basic card-level functions like listing
+ // profiles are moved to the platform.
+ mExecutor = new ThreadPoolExecutor(
+ 4 /* corePoolSize */,
+ 4 /* maxPoolSize */,
+ 30, TimeUnit.SECONDS, /* keepAliveTime */
+ new LinkedBlockingQueue<>(), /* workQueue */
+ new ThreadFactory() {
+ private final AtomicInteger mCount = new AtomicInteger(1);
+
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "EuiccService #" + mCount.getAndIncrement());
+ }
+ }
+ );
+ mExecutor.allowCoreThreadTimeOut(true);
+ }
+
+ @Override
+ @CallSuper
+ public void onDestroy() {
+ mExecutor.shutdownNow();
+ super.onDestroy();
+ }
+
/**
* If overriding this method, call through to the super method for any unknown actions.
* {@inheritDoc}
@@ -279,23 +320,33 @@
public void downloadSubscription(int slotId, DownloadableSubscription subscription,
boolean switchAfterDownload, boolean forceDeactivateSim,
IDownloadSubscriptionCallback callback) {
- int result = EuiccService.this.onDownloadSubscription(
- slotId, subscription, switchAfterDownload, forceDeactivateSim);
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- // Can't communicate with the phone process; ignore.
- }
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ int result = EuiccService.this.onDownloadSubscription(
+ slotId, subscription, switchAfterDownload, forceDeactivateSim);
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
}
@Override
public void getEid(int slotId, IGetEidCallback callback) {
- String eid = EuiccService.this.onGetEid(slotId);
- try {
- callback.onSuccess(eid);
- } catch (RemoteException e) {
- // Can't communicate with the phone process; ignore.
- }
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ String eid = EuiccService.this.onGetEid(slotId);
+ try {
+ callback.onSuccess(eid);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
}
@Override
@@ -303,92 +354,135 @@
DownloadableSubscription subscription,
boolean forceDeactivateSim,
IGetDownloadableSubscriptionMetadataCallback callback) {
- GetDownloadableSubscriptionMetadataResult result =
- EuiccService.this.onGetDownloadableSubscriptionMetadata(
- slotId, subscription, forceDeactivateSim);
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- // Can't communicate with the phone process; ignore.
- }
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ GetDownloadableSubscriptionMetadataResult result =
+ EuiccService.this.onGetDownloadableSubscriptionMetadata(
+ slotId, subscription, forceDeactivateSim);
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
}
@Override
public void getDefaultDownloadableSubscriptionList(int slotId, boolean forceDeactivateSim,
IGetDefaultDownloadableSubscriptionListCallback callback) {
- GetDefaultDownloadableSubscriptionListResult result =
- EuiccService.this.onGetDefaultDownloadableSubscriptionList(
- slotId, forceDeactivateSim);
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- // Can't communicate with the phone process; ignore.
- }
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ GetDefaultDownloadableSubscriptionListResult result =
+ EuiccService.this.onGetDefaultDownloadableSubscriptionList(
+ slotId, forceDeactivateSim);
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
}
@Override
public void getEuiccProfileInfoList(int slotId, IGetEuiccProfileInfoListCallback callback) {
- GetEuiccProfileInfoListResult result =
- EuiccService.this.onGetEuiccProfileInfoList(slotId);
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- // Can't communicate with the phone process; ignore.
- }
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ GetEuiccProfileInfoListResult result =
+ EuiccService.this.onGetEuiccProfileInfoList(slotId);
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
}
@Override
public void getEuiccInfo(int slotId, IGetEuiccInfoCallback callback) {
- EuiccInfo euiccInfo = EuiccService.this.onGetEuiccInfo(slotId);
- try {
- callback.onSuccess(euiccInfo);
- } catch (RemoteException e) {
- // Can't communicate with the phone process; ignore.
- }
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ EuiccInfo euiccInfo = EuiccService.this.onGetEuiccInfo(slotId);
+ try {
+ callback.onSuccess(euiccInfo);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
+
}
@Override
public void deleteSubscription(int slotId, String iccid,
IDeleteSubscriptionCallback callback) {
- int result = EuiccService.this.onDeleteSubscription(slotId, iccid);
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- // Can't communicate with the phone process; ignore.
- }
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ int result = EuiccService.this.onDeleteSubscription(slotId, iccid);
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
}
@Override
public void switchToSubscription(int slotId, String iccid, boolean forceDeactivateSim,
ISwitchToSubscriptionCallback callback) {
- int result =
- EuiccService.this.onSwitchToSubscription(slotId, iccid, forceDeactivateSim);
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- // Can't communicate with the phone process; ignore.
- }
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ int result =
+ EuiccService.this.onSwitchToSubscription(
+ slotId, iccid, forceDeactivateSim);
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
}
@Override
public void updateSubscriptionNickname(int slotId, String iccid, String nickname,
IUpdateSubscriptionNicknameCallback callback) {
- int result = EuiccService.this.onUpdateSubscriptionNickname(slotId, iccid, nickname);
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- // Can't communicate with the phone process; ignore.
- }
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ int result =
+ EuiccService.this.onUpdateSubscriptionNickname(slotId, iccid, nickname);
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
}
@Override
public void eraseSubscriptions(int slotId, IEraseSubscriptionsCallback callback) {
- int result = EuiccService.this.onEraseSubscriptions(slotId);
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- // Can't communicate with the phone process; ignore.
- }
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ int result = EuiccService.this.onEraseSubscriptions(slotId);
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
}
}
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 539278f..e4d3142 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -17,25 +17,19 @@
package android.service.wallpaper;
import android.annotation.Nullable;
-import android.app.WallpaperColors;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.util.MergedConfiguration;
-import android.view.WindowInsets;
-
-import com.android.internal.R;
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.view.BaseIWindow;
-import com.android.internal.view.BaseSurfaceHolder;
-
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
+import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Bundle;
@@ -44,6 +38,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import android.util.MergedConfiguration;
import android.view.Display;
import android.view.Gravity;
import android.view.IWindowSession;
@@ -55,9 +50,14 @@
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.view.BaseIWindow;
+import com.android.internal.view.BaseSurfaceHolder;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -548,6 +548,7 @@
/**
* Notifies the engine that wallpaper colors changed significantly.
* This will trigger a {@link #onComputeWallpaperColors()} call.
+ * @hide
*/
public void invalidateColors() {
try {
@@ -562,8 +563,14 @@
* Notifies the system about what colors the wallpaper is using.
* You might return null if no color information is available at the moment. In that case
* you might want to call {@link #invalidateColors()} in a near future.
+ * <p>
+ * The simplest way of creating A {@link android.app.WallpaperColors} object is by using
+ * {@link android.app.WallpaperColors#fromBitmap(Bitmap)} or
+ * {@link android.app.WallpaperColors#fromDrawable(Drawable)}, but you can also specify
+ * your main colors and dark text support explicitly using one of the constructors.
*
- * @return List of wallpaper colors and their weights.
+ * @return Wallpaper colors.
+ * @hide
*/
public @Nullable WallpaperColors onComputeWallpaperColors() {
return null;
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index caadc36..cb98c88 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -72,6 +72,15 @@
* Creates a display event receiver.
*
* @param looper The looper to use when invoking callbacks.
+ */
+ public DisplayEventReceiver(Looper looper) {
+ this(looper, VSYNC_SOURCE_APP);
+ }
+
+ /**
+ * Creates a display event receiver.
+ *
+ * @param looper The looper to use when invoking callbacks.
* @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values.
*/
public DisplayEventReceiver(Looper looper, int vsyncSource) {
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 31cece4..0a73949 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -62,6 +62,12 @@
public static final int VIRTUAL_KEY_RELEASE = 7;
/**
+ * The user has performed a selection/insertion handle move on text field.
+ * @hide
+ */
+ public static final int TEXT_HANDLE_MOVE = 8;
+
+ /**
* This is a private constant. Feel free to renumber as desired.
* @hide
*/
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 5f55bdc..04fa637 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -224,6 +224,14 @@
* Constant for {@link #getActionMasked}: A movement has happened outside of the
* normal bounds of the UI element. This does not provide a full gesture,
* but only the initial location of the movement/touch.
+ * <p>
+ * Note: Because the location of any event will be outside the
+ * bounds of the view hierarchy, it will not get dispatched to
+ * any children of a ViewGroup by default. Therefore,
+ * movements with ACTION_OUTSIDE should be handled in either the
+ * root {@link View} or in the appropriate {@link Window.Callback}
+ * (e.g. {@link android.app.Activity} or {@link android.app.Dialog}).
+ * </p>
*/
public static final int ACTION_OUTSIDE = 4;
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 8bb3fa9..4f9dbd5 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -52,7 +52,9 @@
private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
throws OutOfResourcesException;
+
private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject);
+ private static native long nativeGetFromSurfaceControl(long surfaceControlNativeObject);
private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
throws OutOfResourcesException;
@@ -410,6 +412,9 @@
* back from a client, converting it from the representation being managed
* by the window manager to the representation the client uses to draw
* in to it.
+ *
+ * @param other {@link SurfaceControl} to copy from.
+ *
* @hide
*/
public void copyFrom(SurfaceControl other) {
@@ -420,7 +425,39 @@
long surfaceControlPtr = other.mNativeObject;
if (surfaceControlPtr == 0) {
throw new NullPointerException(
- "SurfaceControl native object is null. Are you using a released SurfaceControl?");
+ "null SurfaceControl native object. Are you using a released SurfaceControl?");
+ }
+ long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr);
+
+ synchronized (mLock) {
+ if (mNativeObject != 0) {
+ nativeRelease(mNativeObject);
+ }
+ setNativeObjectLocked(newNativeObject);
+ }
+ }
+
+ /**
+ * Gets a reference a surface created from this one. This surface now holds a reference
+ * to the same data as the original surface, and is -not- the owner.
+ * This is for use by the window manager when returning a window surface
+ * back from a client, converting it from the representation being managed
+ * by the window manager to the representation the client uses to draw
+ * in to it.
+ *
+ * @param other {@link SurfaceControl} to create surface from.
+ *
+ * @hide
+ */
+ public void createFrom(SurfaceControl other) {
+ if (other == null) {
+ throw new IllegalArgumentException("other must not be null");
+ }
+
+ long surfaceControlPtr = other.mNativeObject;
+ if (surfaceControlPtr == 0) {
+ throw new NullPointerException(
+ "null SurfaceControl native object. Are you using a released SurfaceControl?");
}
long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 679a9cd..7e0ade4 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -641,6 +641,16 @@
mSurface.copyFrom(mSurfaceControl);
}
+ if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.O) {
+ // Some legacy applications use the underlying native {@link Surface} object
+ // as a key to whether anything has changed. In these cases, updates to the
+ // existing {@link Surface} will be ignored when the size changes.
+ // Therefore, we must explicitly recreate the {@link Surface} in these
+ // cases.
+ mSurface.createFrom(mSurfaceControl);
+ }
+
if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 97f5331..c329db4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -802,7 +802,7 @@
*
* {@hide}
*/
- public static final int LAST_APP_ACCESSIBILITY_ID = Integer.MAX_VALUE / 2;
+ public static final int LAST_APP_AUTOFILL_ID = Integer.MAX_VALUE / 2;
/**
* Attribute to find the autofilled highlight
@@ -2045,6 +2045,11 @@
private SparseArray<Object> mKeyedTags;
/**
+ * The next available accessibility id.
+ */
+ private static int sNextAccessibilityViewId;
+
+ /**
* The animation currently associated with this view.
* @hide
*/
@@ -2086,16 +2091,19 @@
@ViewDebug.ExportedProperty(resolveId = true)
int mID = NO_ID;
- /** The ID of this view for accessibility and autofill purposes.
+ /** The ID of this view for autofill purposes.
* <ul>
* <li>== {@link #NO_ID}: ID has not been assigned yet
- * <li>≤ {@link #LAST_APP_ACCESSIBILITY_ID}: View is not part of a activity. The ID is
+ * <li>≤ {@link #LAST_APP_AUTOFILL_ID}: View is not part of a activity. The ID is
* unique in the process. This might change
* over activity lifecycle events.
- * <li>> {@link #LAST_APP_ACCESSIBILITY_ID}: View is part of a activity. The ID is
+ * <li>> {@link #LAST_APP_AUTOFILL_ID}: View is part of a activity. The ID is
* unique in the activity. This stays the same
* over activity lifecycle events.
*/
+ private int mAutofillViewId = NO_ID;
+
+ // ID for accessibility purposes. This ID must be unique for every window
private int mAccessibilityViewId = NO_ID;
private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
@@ -7721,7 +7729,7 @@
if (mAutofillId == null) {
// The autofill id needs to be unique, but its value doesn't matter,
// so it's better to reuse the accessibility id to save space.
- mAutofillId = new AutofillId(getAccessibilityViewId());
+ mAutofillId = new AutofillId(getAutofillViewId());
}
return mAutofillId;
}
@@ -7954,7 +7962,7 @@
private boolean isAutofillable() {
return getAutofillType() != AUTOFILL_TYPE_NONE && isImportantForAutofill()
- && getAccessibilityViewId() > LAST_APP_ACCESSIBILITY_ID;
+ && getAutofillViewId() > LAST_APP_AUTOFILL_ID;
}
private void populateVirtualStructure(ViewStructure structure,
@@ -8472,12 +8480,26 @@
*/
public int getAccessibilityViewId() {
if (mAccessibilityViewId == NO_ID) {
- mAccessibilityViewId = mContext.getNextAccessibilityId();
+ mAccessibilityViewId = sNextAccessibilityViewId++;
}
return mAccessibilityViewId;
}
/**
+ * Gets the unique identifier of this view on the screen for autofill purposes.
+ *
+ * @return The view autofill id.
+ *
+ * @hide
+ */
+ public int getAutofillViewId() {
+ if (mAutofillViewId == NO_ID) {
+ mAutofillViewId = mContext.getNextAutofillId();
+ }
+ return mAutofillViewId;
+ }
+
+ /**
* Gets the unique identifier of the window in which this View reseides.
*
* @return The window accessibility id.
@@ -12125,7 +12147,7 @@
if (isAutofillable()) {
AutofillManager afm = getAutofillManager();
- if (afm != null && getAccessibilityViewId() > LAST_APP_ACCESSIBILITY_ID) {
+ if (afm != null && getAutofillViewId() > LAST_APP_AUTOFILL_ID) {
if (mVisibilityChangeForAutofillHandler != null) {
mVisibilityChangeForAutofillHandler.removeMessages(0);
}
@@ -17555,16 +17577,16 @@
*
* @return Returns a Parcelable object containing the view's current dynamic
* state, or null if there is nothing interesting to save.
- * @see #onRestoreInstanceState(android.os.Parcelable)
- * @see #saveHierarchyState(android.util.SparseArray)
- * @see #dispatchSaveInstanceState(android.util.SparseArray)
+ * @see #onRestoreInstanceState(Parcelable)
+ * @see #saveHierarchyState(SparseArray)
+ * @see #dispatchSaveInstanceState(SparseArray)
* @see #setSaveEnabled(boolean)
*/
@CallSuper
@Nullable protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (mStartActivityRequestWho != null || isAutofilled()
- || mAccessibilityViewId > LAST_APP_ACCESSIBILITY_ID) {
+ || mAutofillViewId > LAST_APP_AUTOFILL_ID) {
BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
if (mStartActivityRequestWho != null) {
@@ -17575,13 +17597,13 @@
state.mSavedData |= BaseSavedState.IS_AUTOFILLED;
}
- if (mAccessibilityViewId > LAST_APP_ACCESSIBILITY_ID) {
- state.mSavedData |= BaseSavedState.ACCESSIBILITY_ID;
+ if (mAutofillViewId > LAST_APP_AUTOFILL_ID) {
+ state.mSavedData |= BaseSavedState.AUTOFILL_ID;
}
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
state.mIsAutofilled = isAutofilled();
- state.mAccessibilityViewId = mAccessibilityViewId;
+ state.mAutofillViewId = mAutofillViewId;
return state;
}
return BaseSavedState.EMPTY_STATE;
@@ -17659,8 +17681,8 @@
if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) {
setAutofilled(baseState.mIsAutofilled);
}
- if ((baseState.mSavedData & BaseSavedState.ACCESSIBILITY_ID) != 0) {
- mAccessibilityViewId = baseState.mAccessibilityViewId;
+ if ((baseState.mSavedData & BaseSavedState.AUTOFILL_ID) != 0) {
+ mAutofillViewId = baseState.mAutofillViewId;
}
}
}
@@ -21484,7 +21506,7 @@
* @param accessibilityId The searched accessibility id.
* @return The found view.
*/
- final <T extends View> T findViewByAccessibilityId(int accessibilityId) {
+ final <T extends View> T findViewByAccessibilityId(int accessibilityId) {
if (accessibilityId < 0) {
return null;
}
@@ -21496,11 +21518,11 @@
}
/**
- * Performs the traversal to find a view by its unuque and stable accessibility id.
+ * Performs the traversal to find a view by its unique and stable accessibility id.
*
* <strong>Note:</strong>This method does not stop at the root namespace
* boundary since the user can touch the screen at an arbitrary location
- * potentially crossing the root namespace bounday which will send an
+ * potentially crossing the root namespace boundary which will send an
* accessibility event to accessibility services and they should be able
* to obtain the event source. Also accessibility ids are guaranteed to be
* unique in the window.
@@ -21517,6 +21539,23 @@
}
/**
+ * Performs the traversal to find a view by its autofill id.
+ *
+ * <strong>Note:</strong>This method does not stop at the root namespace
+ * boundary.
+ *
+ * @param autofillId The autofill id.
+ * @return The found view.
+ * @hide
+ */
+ public <T extends View> T findViewByAutofillIdTraversal(int autofillId) {
+ if (getAutofillViewId() == autofillId) {
+ return (T) this;
+ }
+ return null;
+ }
+
+ /**
* Look for a child view with the given tag. If this view has the given
* tag, return this view.
*
@@ -24982,13 +25021,13 @@
public static class BaseSavedState extends AbsSavedState {
static final int START_ACTIVITY_REQUESTED_WHO_SAVED = 0b1;
static final int IS_AUTOFILLED = 0b10;
- static final int ACCESSIBILITY_ID = 0b100;
+ static final int AUTOFILL_ID = 0b100;
// Flags that describe what data in this state is valid
int mSavedData;
String mStartActivityRequestWhoSaved;
boolean mIsAutofilled;
- int mAccessibilityViewId;
+ int mAutofillViewId;
/**
* Constructor used when reading from a parcel. Reads the state of the superclass.
@@ -25011,7 +25050,7 @@
mSavedData = source.readInt();
mStartActivityRequestWhoSaved = source.readString();
mIsAutofilled = source.readBoolean();
- mAccessibilityViewId = source.readInt();
+ mAutofillViewId = source.readInt();
}
/**
@@ -25030,7 +25069,7 @@
out.writeInt(mSavedData);
out.writeString(mStartActivityRequestWhoSaved);
out.writeBoolean(mIsAutofilled);
- out.writeInt(mAccessibilityViewId);
+ out.writeInt(mAutofillViewId);
}
public static final Parcelable.Creator<BaseSavedState> CREATOR
@@ -26208,9 +26247,6 @@
mTooltipInfo.mHideTooltipRunnable = this::hideTooltip;
}
mTooltipInfo.mTooltipText = tooltipText;
- if (mTooltipInfo.mTooltipPopup != null && mTooltipInfo.mTooltipPopup.isShowing()) {
- mTooltipInfo.mTooltipPopup.updateContent(mTooltipInfo.mTooltipText);
- }
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 2e817eb..cb92a4c 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1361,6 +1361,27 @@
return null;
}
+ /** @hide */
+ @Override
+ public View findViewByAutofillIdTraversal(int autofillId) {
+ View foundView = super.findViewByAutofillIdTraversal(autofillId);
+ if (foundView != null) {
+ return foundView;
+ }
+
+ final int childrenCount = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i = 0; i < childrenCount; i++) {
+ View child = children[i];
+ foundView = child.findViewByAutofillIdTraversal(autofillId);
+ if (foundView != null) {
+ return foundView;
+ }
+ }
+
+ return null;
+ }
+
@Override
public void dispatchWindowFocusChanged(boolean hasFocus) {
super.dispatchWindowFocusChanged(hasFocus);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 32973a0..86b19f4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2477,6 +2477,9 @@
mInLayout = true;
final View host = mView;
+ if (host == null) {
+ return;
+ }
if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
Log.v(mTag, "Laying out " + host + " to (" +
host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
@@ -2784,6 +2787,8 @@
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
+ } else if (mView == null) {
+ return;
}
final boolean fullRedrawNeeded = mFullRedrawNeeded;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index dfbf962..4c0a190 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1391,6 +1391,13 @@
public static final int PRIVATE_FLAG_TASK_SNAPSHOT = 0x00080000;
/**
+ * Flag to indicate that this window should be ignored when determining what parts of the
+ * screen can be magnified.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_NO_MAGNIFICATION_REGION_EFFECT = 0x00100000;
+
+ /**
* Control flags that are private to the platform.
* @hide
*/
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 55aed52..4c9cf40 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -229,6 +229,11 @@
public abstract boolean isKeyguardGoingAway();
/**
+ * @return Whether the keyguard is showing and not occluded.
+ */
+ public abstract boolean isKeyguardShowingAndNotOccluded();
+
+ /**
* Gets the frame of a window given its token.
*
* @param token The token.
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 3a3e171..69892d9 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -971,14 +971,14 @@
}
/**
- * Notifies that the availability of the accessibility button in the system's navigation area
+ * Notifies that the visibility of the accessibility button in the system's navigation area
* has changed.
*
- * @param available {@code true} if the accessibility button is available within the system
+ * @param shown {@code true} if the accessibility button is visible within the system
* navigation area, {@code false} otherwise
* @hide
*/
- public void notifyAccessibilityButtonAvailabilityChanged(boolean available) {
+ public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
final IAccessibilityManager service;
synchronized (mLock) {
service = getServiceLocked();
@@ -987,9 +987,9 @@
}
}
try {
- service.notifyAccessibilityButtonAvailabilityChanged(available);
+ service.notifyAccessibilityButtonVisibilityChanged(shown);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while dispatching accessibility button availability change", re);
+ Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re);
}
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 06cb5dc..3f499ab 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -64,7 +64,7 @@
void notifyAccessibilityButtonClicked();
- void notifyAccessibilityButtonAvailabilityChanged(boolean available);
+ void notifyAccessibilityButtonVisibilityChanged(boolean available);
// Requires WRITE_SECURE_SETTINGS
void performAccessibilityShortcut();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 310ec1c..5b04f41 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -246,20 +246,20 @@
/**
* Finds views by traversing the hierarchies of the client.
*
- * @param viewIds The accessibility ids of the views to find
+ * @param viewIds The autofill ids of the views to find
*
* @return And array containing the views (empty if no views found).
*/
- @NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds);
+ @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds);
/**
* Finds a view by traversing the hierarchies of the client.
*
- * @param viewId The accessibility id of the views to find
+ * @param viewId The autofill id of the views to find
*
* @return The view, or {@code null} if not found
*/
- @Nullable View findViewByAccessibilityIdTraversal(int viewId);
+ @Nullable View findViewByAutofillIdTraversal(int viewId);
/**
* Runs the specified action on the UI thread.
@@ -795,11 +795,11 @@
}
private static AutofillId getAutofillId(View view) {
- return new AutofillId(view.getAccessibilityViewId());
+ return new AutofillId(view.getAutofillViewId());
}
private static AutofillId getAutofillId(View parent, int virtualId) {
- return new AutofillId(parent.getAccessibilityViewId(), virtualId);
+ return new AutofillId(parent.getAutofillViewId(), virtualId);
}
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@@ -1039,7 +1039,7 @@
final int itemCount = ids.size();
int numApplied = 0;
ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
- final View[] views = client.findViewsByAccessibilityIdTraversal(getViewIds(ids));
+ final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids));
for (int i = 0; i < itemCount; i++) {
final AutofillId id = ids.get(i);
@@ -1232,7 +1232,7 @@
return null;
}
- return client.findViewByAccessibilityIdTraversal(autofillId.getViewId());
+ return client.findViewByAutofillIdTraversal(autofillId.getViewId());
}
/** @hide */
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0e6e3ae..45e5f8a 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2584,14 +2584,18 @@
if (offset == -1) {
return;
}
+
stopTextActionModeWithPreservingSelection();
- final boolean isOnSelection = mTextView.hasSelection()
- && offset >= mTextView.getSelectionStart() && offset <= mTextView.getSelectionEnd();
- if (!isOnSelection) {
- // Right clicked position is not on the selection. Remove the selection and move the
- // cursor to the right clicked position.
- Selection.setSelection((Spannable) mTextView.getText(), offset);
- stopTextActionMode();
+ if (mTextView.canSelectText()) {
+ final boolean isOnSelection = mTextView.hasSelection()
+ && offset >= mTextView.getSelectionStart()
+ && offset <= mTextView.getSelectionEnd();
+ if (!isOnSelection) {
+ // Right clicked position is not on the selection. Remove the selection and move the
+ // cursor to the right clicked position.
+ Selection.setSelection((Spannable) mTextView.getText(), offset);
+ stopTextActionMode();
+ }
}
if (shouldOfferToShowSuggestions()) {
diff --git a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java
index 56d60a1..9ac753b 100644
--- a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java
@@ -61,7 +61,7 @@
* This means that the color space is divided into distinct colors, rather than representative
* colors.
*/
-final class ColorCutQuantizer {
+final class ColorCutQuantizer implements Quantizer {
private static final String LOG_TAG = "ColorCutQuantizer";
private static final boolean LOG_TIMINGS = false;
@@ -73,22 +73,22 @@
private static final int QUANTIZE_WORD_WIDTH = 5;
private static final int QUANTIZE_WORD_MASK = (1 << QUANTIZE_WORD_WIDTH) - 1;
- final int[] mColors;
- final int[] mHistogram;
- final List<Swatch> mQuantizedColors;
- final TimingLogger mTimingLogger;
- final Palette.Filter[] mFilters;
+ int[] mColors;
+ int[] mHistogram;
+ List<Swatch> mQuantizedColors;
+ TimingLogger mTimingLogger;
+ Palette.Filter[] mFilters;
private final float[] mTempHsl = new float[3];
/**
- * Constructor.
+ * Execute color quantization.
*
* @param pixels histogram representing an image's pixel data
* @param maxColors The maximum number of colors that should be in the result palette.
* @param filters Set of filters to use in the quantization stage
*/
- ColorCutQuantizer(final int[] pixels, final int maxColors, final Palette.Filter[] filters) {
+ public void quantize(final int[] pixels, final int maxColors, final Palette.Filter[] filters) {
mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null;
mFilters = filters;
@@ -160,7 +160,7 @@
/**
* @return the list of quantized colors
*/
- List<Swatch> getQuantizedColors() {
+ public List<Swatch> getQuantizedColors() {
return mQuantizedColors;
}
diff --git a/core/java/com/android/internal/graphics/palette/Palette.java b/core/java/com/android/internal/graphics/palette/Palette.java
index 9f1504a..a4f9a59 100644
--- a/core/java/com/android/internal/graphics/palette/Palette.java
+++ b/core/java/com/android/internal/graphics/palette/Palette.java
@@ -613,6 +613,8 @@
private final List<Palette.Filter> mFilters = new ArrayList<>();
private Rect mRegion;
+ private Quantizer mQuantizer;
+
/**
* Construct a new {@link Palette.Builder} using a source {@link Bitmap}
*/
@@ -726,6 +728,18 @@
}
/**
+ * Set a specific quantization algorithm. {@link ColorCutQuantizer} will
+ * be used if unspecified.
+ *
+ * @param quantizer Quantizer implementation.
+ */
+ @NonNull
+ public Palette.Builder setQuantizer(Quantizer quantizer) {
+ mQuantizer = quantizer;
+ return this;
+ }
+
+ /**
* Set a region of the bitmap to be used exclusively when calculating the palette.
* <p>This only works when the original input is a {@link Bitmap}.</p>
*
@@ -818,17 +832,19 @@
}
// Now generate a quantizer from the Bitmap
- final ColorCutQuantizer quantizer = new ColorCutQuantizer(
- getPixelsFromBitmap(bitmap),
- mMaxColors,
- mFilters.isEmpty() ? null : mFilters.toArray(new Palette.Filter[mFilters.size()]));
+ if (mQuantizer == null) {
+ mQuantizer = new ColorCutQuantizer();
+ }
+ mQuantizer.quantize(getPixelsFromBitmap(bitmap),
+ mMaxColors, mFilters.isEmpty() ? null :
+ mFilters.toArray(new Palette.Filter[mFilters.size()]));
// If created a new bitmap, recycle it
if (bitmap != mBitmap) {
bitmap.recycle();
}
- swatches = quantizer.getQuantizedColors();
+ swatches = mQuantizer.getQuantizedColors();
if (logger != null) {
logger.addSplit("Color quantization completed");
diff --git a/core/java/com/android/internal/graphics/palette/Quantizer.java b/core/java/com/android/internal/graphics/palette/Quantizer.java
new file mode 100644
index 0000000..db60f2e
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/Quantizer.java
@@ -0,0 +1,27 @@
+/*
+ * 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.internal.graphics.palette;
+
+import java.util.List;
+
+/**
+ * Definition of an algorithm that receives pixels and outputs a list of colors.
+ */
+public interface Quantizer {
+ void quantize(final int[] pixels, final int maxColors, final Palette.Filter[] filters);
+ List<Palette.Swatch> getQuantizedColors();
+}
diff --git a/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java
new file mode 100644
index 0000000..b035535
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java
@@ -0,0 +1,154 @@
+/*
+ * 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.internal.graphics.palette;
+
+import android.util.Log;
+
+import com.android.internal.graphics.ColorUtils;
+import com.android.internal.ml.clustering.KMeans;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * A quantizer that uses k-means
+ */
+public class VariationalKMeansQuantizer implements Quantizer {
+
+ private static final String TAG = "KMeansQuantizer";
+ private static final boolean DEBUG = false;
+
+ /**
+ * Clusters closer than this value will me merged.
+ */
+ private final float mMinClusterSqDistance;
+
+ /**
+ * K-means can get stuck in local optima, this can be avoided by
+ * repeating it and getting the "best" execution.
+ */
+ private final int mInitializations;
+
+ /**
+ * Initialize KMeans with a fixed random state to have
+ * consistent results across multiple runs.
+ */
+ private final KMeans mKMeans = new KMeans(new Random(0), 30, 0);
+
+ private List<Palette.Swatch> mQuantizedColors;
+
+ public VariationalKMeansQuantizer() {
+ this(0.25f /* cluster distance */);
+ }
+
+ public VariationalKMeansQuantizer(float minClusterDistance) {
+ this(minClusterDistance, 1 /* initializations */);
+ }
+
+ public VariationalKMeansQuantizer(float minClusterDistance, int initializations) {
+ mMinClusterSqDistance = minClusterDistance * minClusterDistance;
+ mInitializations = initializations;
+ }
+
+ /**
+ * K-Means quantizer.
+ *
+ * @param pixels Pixels to quantize.
+ * @param maxColors Maximum number of clusters to extract.
+ * @param filters Colors that should be ignored
+ */
+ @Override
+ public void quantize(int[] pixels, int maxColors, Palette.Filter[] filters) {
+ // Start by converting all colors to HSL.
+ // HLS is way more meaningful for clustering than RGB.
+ final float[] hsl = {0, 0, 0};
+ final float[][] hslPixels = new float[pixels.length][3];
+ for (int i = 0; i < pixels.length; i++) {
+ ColorUtils.colorToHSL(pixels[i], hsl);
+ // Normalize hue so all values go from 0 to 1.
+ hslPixels[i][0] = hsl[0] / 360f;
+ hslPixels[i][1] = hsl[1];
+ hslPixels[i][2] = hsl[2];
+ }
+
+ final List<KMeans.Mean> optimalMeans = getOptimalKMeans(maxColors, hslPixels);
+
+ // Ideally we should run k-means again to merge clusters but it would be too expensive,
+ // instead we just merge all clusters that are closer than a threshold.
+ for (int i = 0; i < optimalMeans.size(); i++) {
+ KMeans.Mean current = optimalMeans.get(i);
+ float[] currentCentroid = current.getCentroid();
+ for (int j = i + 1; j < optimalMeans.size(); j++) {
+ KMeans.Mean compareTo = optimalMeans.get(j);
+ float[] compareToCentroid = compareTo.getCentroid();
+ float sqDistance = KMeans.sqDistance(currentCentroid, compareToCentroid);
+ // Merge them
+ if (sqDistance < mMinClusterSqDistance) {
+ optimalMeans.remove(compareTo);
+ current.getItems().addAll(compareTo.getItems());
+ for (int k = 0; k < currentCentroid.length; k++) {
+ currentCentroid[k] += (compareToCentroid[k] - currentCentroid[k]) / 2.0;
+ }
+ j--;
+ }
+ }
+ }
+
+ // Convert data to final format, de-normalizing the hue.
+ mQuantizedColors = new ArrayList<>();
+ for (KMeans.Mean mean : optimalMeans) {
+ if (mean.getItems().size() == 0) {
+ continue;
+ }
+ float[] centroid = mean.getCentroid();
+ mQuantizedColors.add(new Palette.Swatch(new float[]{
+ centroid[0] * 360f,
+ centroid[1],
+ centroid[2]
+ }, mean.getItems().size()));
+ }
+ }
+
+ private List<KMeans.Mean> getOptimalKMeans(int k, float[][] inputData) {
+ List<KMeans.Mean> optimal = null;
+ double optimalScore = -Double.MAX_VALUE;
+ int runs = mInitializations;
+ while (runs > 0) {
+ if (DEBUG) {
+ Log.d(TAG, "k-means run: " + runs);
+ }
+ List<KMeans.Mean> means = mKMeans.predict(k, inputData);
+ double score = KMeans.score(means);
+ if (optimal == null || score > optimalScore) {
+ if (DEBUG) {
+ Log.d(TAG, "\tnew optimal score: " + score);
+ }
+ optimalScore = score;
+ optimal = means;
+ }
+ runs--;
+ }
+
+ return optimal;
+ }
+
+ @Override
+ public List<Palette.Swatch> getQuantizedColors() {
+ return mQuantizedColors;
+ }
+}
diff --git a/core/java/com/android/internal/ml/clustering/KMeans.java b/core/java/com/android/internal/ml/clustering/KMeans.java
new file mode 100644
index 0000000..4d5b333
--- /dev/null
+++ b/core/java/com/android/internal/ml/clustering/KMeans.java
@@ -0,0 +1,243 @@
+/*
+ * 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.internal.ml.clustering;
+
+import android.annotation.NonNull;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Simple K-Means implementation
+ */
+public class KMeans {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "KMeans";
+ private final Random mRandomState;
+ private final int mMaxIterations;
+ private float mSqConvergenceEpsilon;
+
+ public KMeans() {
+ this(new Random());
+ }
+
+ public KMeans(Random random) {
+ this(random, 30 /* maxIterations */, 0.005f /* convergenceEpsilon */);
+ }
+ public KMeans(Random random, int maxIterations, float convergenceEpsilon) {
+ mRandomState = random;
+ mMaxIterations = maxIterations;
+ mSqConvergenceEpsilon = convergenceEpsilon * convergenceEpsilon;
+ }
+
+ /**
+ * Runs k-means on the input data (X) trying to find k means.
+ *
+ * K-Means is known for getting stuck into local optima, so you might
+ * want to run it multiple time and argmax on {@link KMeans#score(List)}
+ *
+ * @param k The number of points to return.
+ * @param inputData Input data.
+ * @return An array of k Means, each representing a centroid and data points that belong to it.
+ */
+ public List<Mean> predict(final int k, final float[][] inputData) {
+ checkDataSetSanity(inputData);
+ int dimension = inputData[0].length;
+
+ final ArrayList<Mean> means = new ArrayList<>();
+ for (int i = 0; i < k; i++) {
+ Mean m = new Mean(dimension);
+ for (int j = 0; j < dimension; j++) {
+ m.mCentroid[j] = mRandomState.nextFloat();
+ }
+ means.add(m);
+ }
+
+ // Iterate until we converge or run out of iterations
+ boolean converged = false;
+ for (int i = 0; i < mMaxIterations; i++) {
+ converged = step(means, inputData);
+ if (converged) {
+ if (DEBUG) Log.d(TAG, "Converged at iteration: " + i);
+ break;
+ }
+ }
+ if (!converged && DEBUG) Log.d(TAG, "Did not converge");
+
+ return means;
+ }
+
+ /**
+ * Score calculates the inertia between means.
+ * This can be considered as an E step of an EM algorithm.
+ *
+ * @param means Means to use when calculating score.
+ * @return The score
+ */
+ public static double score(@NonNull List<Mean> means) {
+ double score = 0;
+ final int meansSize = means.size();
+ for (int i = 0; i < meansSize; i++) {
+ Mean mean = means.get(i);
+ for (int j = 0; j < meansSize; j++) {
+ Mean compareTo = means.get(j);
+ if (mean == compareTo) {
+ continue;
+ }
+ double distance = Math.sqrt(sqDistance(mean.mCentroid, compareTo.mCentroid));
+ score += distance;
+ }
+ }
+ return score;
+ }
+
+ @VisibleForTesting
+ public void checkDataSetSanity(float[][] inputData) {
+ if (inputData == null) {
+ throw new IllegalArgumentException("Data set is null.");
+ } else if (inputData.length == 0) {
+ throw new IllegalArgumentException("Data set is empty.");
+ } else if (inputData[0] == null) {
+ throw new IllegalArgumentException("Bad data set format.");
+ }
+
+ final int dimension = inputData[0].length;
+ final int length = inputData.length;
+ for (int i = 1; i < length; i++) {
+ if (inputData[i] == null || inputData[i].length != dimension) {
+ throw new IllegalArgumentException("Bad data set format.");
+ }
+ }
+ }
+
+ /**
+ * K-Means iteration.
+ *
+ * @param means Current means
+ * @param inputData Input data
+ * @return True if data set converged
+ */
+ private boolean step(final ArrayList<Mean> means, final float[][] inputData) {
+
+ // Clean up the previous state because we need to compute
+ // which point belongs to each mean again.
+ for (int i = means.size() - 1; i >= 0; i--) {
+ final Mean mean = means.get(i);
+ mean.mClosestItems.clear();
+ }
+ for (int i = inputData.length - 1; i >= 0; i--) {
+ final float[] current = inputData[i];
+ final Mean nearest = nearestMean(current, means);
+ nearest.mClosestItems.add(current);
+ }
+
+ boolean converged = true;
+ // Move each mean towards the nearest data set points
+ for (int i = means.size() - 1; i >= 0; i--) {
+ final Mean mean = means.get(i);
+ if (mean.mClosestItems.size() == 0) {
+ continue;
+ }
+
+ // Compute the new mean centroid:
+ // 1. Sum all all points
+ // 2. Average them
+ final float[] oldCentroid = mean.mCentroid;
+ mean.mCentroid = new float[oldCentroid.length];
+ for (int j = 0; j < mean.mClosestItems.size(); j++) {
+ // Update each centroid component
+ for (int p = 0; p < mean.mCentroid.length; p++) {
+ mean.mCentroid[p] += mean.mClosestItems.get(j)[p];
+ }
+ }
+ for (int j = 0; j < mean.mCentroid.length; j++) {
+ mean.mCentroid[j] /= mean.mClosestItems.size();
+ }
+
+ // We converged if the centroid didn't move for any of the means.
+ if (sqDistance(oldCentroid, mean.mCentroid) > mSqConvergenceEpsilon) {
+ converged = false;
+ }
+ }
+ return converged;
+ }
+
+ @VisibleForTesting
+ public static Mean nearestMean(float[] point, List<Mean> means) {
+ Mean nearest = null;
+ float nearestDistance = Float.MAX_VALUE;
+
+ final int meanCount = means.size();
+ for (int i = 0; i < meanCount; i++) {
+ Mean next = means.get(i);
+ // We don't need the sqrt when comparing distances in euclidean space
+ // because they exist on both sides of the equation and cancel each other out.
+ float nextDistance = sqDistance(point, next.mCentroid);
+ if (nextDistance < nearestDistance) {
+ nearest = next;
+ nearestDistance = nextDistance;
+ }
+ }
+ return nearest;
+ }
+
+ @VisibleForTesting
+ public static float sqDistance(float[] a, float[] b) {
+ float dist = 0;
+ final int length = a.length;
+ for (int i = 0; i < length; i++) {
+ dist += (a[i] - b[i]) * (a[i] - b[i]);
+ }
+ return dist;
+ }
+
+ /**
+ * Definition of a mean, contains a centroid and points on its cluster.
+ */
+ public static class Mean {
+ float[] mCentroid;
+ final ArrayList<float[]> mClosestItems = new ArrayList<>();
+
+ public Mean(int dimension) {
+ mCentroid = new float[dimension];
+ }
+
+ public Mean(float ...centroid) {
+ mCentroid = centroid;
+ }
+
+ public float[] getCentroid() {
+ return mCentroid;
+ }
+
+ public List<float[]> getItems() {
+ return mClosestItems;
+ }
+
+ @Override
+ public String toString() {
+ return "Mean(centroid: " + Arrays.toString(mCentroid) + ", size: "
+ + mClosestItems.size() + ")";
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/TooltipPopup.java b/core/java/com/android/internal/view/TooltipPopup.java
index 52357ac..3930214 100644
--- a/core/java/com/android/internal/view/TooltipPopup.java
+++ b/core/java/com/android/internal/view/TooltipPopup.java
@@ -91,10 +91,6 @@
return mContentView.getParent() != null;
}
- public void updateContent(CharSequence tooltipText) {
- mMessageView.setText(tooltipText);
- }
-
private void computePosition(View anchorView, int anchorX, int anchorY, boolean fromTouch,
WindowManager.LayoutParams outParams) {
outParams.token = anchorView.getWindowToken();
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 226e9e3..1779ada 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1761,6 +1761,14 @@
return nativeToJavaStatus(AudioSystem::systemReady());
}
+static jfloat
+android_media_AudioSystem_getStreamVolumeDB(JNIEnv *env, jobject thiz,
+ jint stream, jint index, jint device)
+{
+ return (jfloat)AudioSystem::getStreamVolumeDB((audio_stream_type_t)stream,
+ (int)index,
+ (audio_devices_t)device);
+}
// ----------------------------------------------------------------------------
@@ -1814,6 +1822,7 @@
{"native_register_recording_callback", "()V",
(void *)android_media_AudioSystem_registerRecordingCallback},
{"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
+ {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
};
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 19f779f..cdd3c09 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -58,6 +58,29 @@
jmethodID onTransactID;
} gFields;
+struct JHwBinderHolder : public RefBase {
+ JHwBinderHolder() {}
+
+ sp<JHwBinder> get(JNIEnv *env, jobject obj) {
+ Mutex::Autolock autoLock(mLock);
+
+ sp<JHwBinder> binder = mBinder.promote();
+
+ if (binder == NULL) {
+ binder = new JHwBinder(env, obj);
+ mBinder = binder;
+ }
+
+ return binder;
+ }
+
+private:
+ Mutex mLock;
+ wp<JHwBinder> mBinder;
+
+ DISALLOW_COPY_AND_ASSIGN(JHwBinderHolder);
+};
+
// static
void JHwBinder::InitClass(JNIEnv *env) {
ScopedLocalRef<jclass> clazz(
@@ -75,10 +98,10 @@
}
// static
-sp<JHwBinder> JHwBinder::SetNativeContext(
- JNIEnv *env, jobject thiz, const sp<JHwBinder> &context) {
- sp<JHwBinder> old =
- (JHwBinder *)env->GetLongField(thiz, gFields.contextID);
+sp<JHwBinderHolder> JHwBinder::SetNativeContext(
+ JNIEnv *env, jobject thiz, const sp<JHwBinderHolder> &context) {
+ sp<JHwBinderHolder> old =
+ (JHwBinderHolder *)env->GetLongField(thiz, gFields.contextID);
if (context != NULL) {
context->incStrong(NULL /* id */);
@@ -94,27 +117,27 @@
}
// static
-sp<JHwBinder> JHwBinder::GetNativeContext(
+sp<JHwBinder> JHwBinder::GetNativeBinder(
JNIEnv *env, jobject thiz) {
- return (JHwBinder *)env->GetLongField(thiz, gFields.contextID);
+ JHwBinderHolder *holder =
+ reinterpret_cast<JHwBinderHolder *>(
+ env->GetLongField(thiz, gFields.contextID));
+
+ return holder->get(env, thiz);
}
JHwBinder::JHwBinder(JNIEnv *env, jobject thiz) {
jclass clazz = env->GetObjectClass(thiz);
CHECK(clazz != NULL);
- mClass = (jclass)env->NewGlobalRef(clazz);
- mObject = env->NewWeakGlobalRef(thiz);
+ mObject = env->NewGlobalRef(thiz);
}
JHwBinder::~JHwBinder() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->DeleteWeakGlobalRef(mObject);
+ env->DeleteGlobalRef(mObject);
mObject = NULL;
-
- env->DeleteGlobalRef(mClass);
- mClass = NULL;
}
status_t JHwBinder::onTransact(
@@ -203,10 +226,10 @@
using namespace android;
static void releaseNativeContext(void *nativeContext) {
- sp<JHwBinder> binder = (JHwBinder *)nativeContext;
+ sp<JHwBinderHolder> context = static_cast<JHwBinderHolder *>(nativeContext);
- if (binder != NULL) {
- binder->decStrong(NULL /* id */);
+ if (context != NULL) {
+ context->decStrong(NULL /* id */);
}
}
@@ -217,8 +240,7 @@
}
static void JHwBinder_native_setup(JNIEnv *env, jobject thiz) {
- sp<JHwBinder> context = new JHwBinder(env, thiz);
-
+ sp<JHwBinderHolder> context = new JHwBinderHolder;
JHwBinder::SetNativeContext(env, thiz, context);
}
@@ -246,7 +268,7 @@
return; // XXX exception already pending?
}
- sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
+ sp<hardware::IBinder> binder = JHwBinder::GetNativeBinder(env, thiz);
/* TODO(b/33440494) this is not right */
sp<hidl::base::V1_0::IBase> base = new hidl::base::V1_0::BpHwBase(binder);
diff --git a/core/jni/android_os_HwBinder.h b/core/jni/android_os_HwBinder.h
index fa8fe01..5352f1e 100644
--- a/core/jni/android_os_HwBinder.h
+++ b/core/jni/android_os_HwBinder.h
@@ -24,13 +24,15 @@
namespace android {
+struct JHwBinderHolder;
+
struct JHwBinder : public hardware::BHwBinder {
static void InitClass(JNIEnv *env);
- static sp<JHwBinder> SetNativeContext(
- JNIEnv *env, jobject thiz, const sp<JHwBinder> &context);
+ static sp<JHwBinderHolder> SetNativeContext(
+ JNIEnv *env, jobject thiz, const sp<JHwBinderHolder> &context);
- static sp<JHwBinder> GetNativeContext(JNIEnv *env, jobject thiz);
+ static sp<JHwBinder> GetNativeBinder(JNIEnv *env, jobject thiz);
JHwBinder(JNIEnv *env, jobject thiz);
@@ -45,7 +47,6 @@
TransactCallback callback);
private:
- jclass mClass;
jobject mObject;
DISALLOW_COPY_AND_ASSIGN(JHwBinder);
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index b21ea828..6ea809a 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -169,7 +169,6 @@
jclass clazz = env->GetObjectClass(thiz);
CHECK(clazz != NULL);
- mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
}
@@ -182,9 +181,6 @@
env->DeleteWeakGlobalRef(mObject);
mObject = NULL;
-
- env->DeleteGlobalRef(mClass);
- mClass = NULL;
}
hardware::Parcel *JHwParcel::getParcel() {
@@ -542,7 +538,7 @@
env, FindClassOrDie(env, PACKAGE_PATH "/HwRemoteBinder"));
if (env->IsInstanceOf(binderObj, hwBinderKlass.get())) {
- binder = JHwBinder::GetNativeContext(env, binderObj);
+ binder = JHwBinder::GetNativeBinder(env, binderObj);
} else if (env->IsInstanceOf(binderObj, hwRemoteBinderKlass.get())) {
binder = JHwRemoteBinder::GetNativeContext(
env, binderObj)->getBinder();
diff --git a/core/jni/android_os_HwParcel.h b/core/jni/android_os_HwParcel.h
index f81de9b..f6e6100 100644
--- a/core/jni/android_os_HwParcel.h
+++ b/core/jni/android_os_HwParcel.h
@@ -53,7 +53,6 @@
virtual ~JHwParcel();
private:
- jclass mClass;
jobject mObject;
hardware::Parcel *mParcel;
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index f2f8e52..9c2ee9c 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -272,7 +272,6 @@
jclass clazz = env->GetObjectClass(thiz);
CHECK(clazz != NULL);
- mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
}
@@ -281,9 +280,6 @@
env->DeleteWeakGlobalRef(mObject);
mObject = NULL;
-
- env->DeleteGlobalRef(mClass);
- mClass = NULL;
}
sp<hardware::IBinder> JHwRemoteBinder::getBinder() const {
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
index 77a0278..63aad0a 100644
--- a/core/jni/android_os_HwRemoteBinder.h
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -68,7 +68,6 @@
virtual ~JHwRemoteBinder();
private:
- jclass mClass;
jobject mObject;
sp<hardware::IBinder> mBinder;
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index bde9134..494e266 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -400,6 +400,16 @@
static jlong nativeCreateFromSurfaceControl(JNIEnv* env, jclass clazz,
jlong surfaceControlNativeObj) {
+ sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
+ sp<Surface> surface(ctrl->createSurface());
+ if (surface != NULL) {
+ surface->incStrong(&sRefBaseOwner);
+ }
+ return reinterpret_cast<jlong>(surface.get());
+}
+
+static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz,
+ jlong surfaceControlNativeObj) {
/*
* This is used by the WindowManagerService just after constructing
* a Surface and is necessary for returning the Surface reference to
@@ -596,6 +606,8 @@
(void*)nativeAllocateBuffers },
{"nativeCreateFromSurfaceControl", "(J)J",
(void*)nativeCreateFromSurfaceControl },
+ {"nativeGetFromSurfaceControl", "(J)J",
+ (void*)nativeGetFromSurfaceControl },
{"nativeReadFromParcel", "(JLandroid/os/Parcel;)J",
(void*)nativeReadFromParcel },
{"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index d509c12..aa80ba5 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -811,7 +811,7 @@
<string name="js_dialog_before_unload_title" msgid="2619376555525116593">"Confirmació de la navegació"</string>
<string name="js_dialog_before_unload_positive_button" msgid="3112752010600484130">"Surt d\'aquesta pàgina"</string>
<string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Queda\'t en aquesta pàgina"</string>
- <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nEstàs segur que vols sortir d\'aquesta pàgina?"</string>
+ <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nConfirmes que vols sortir d\'aquesta pàgina?"</string>
<string name="save_password_label" msgid="6860261758665825069">"Confirma"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Consell: Pica dos cops per ampliar i per reduir."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"Em. aut."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index e64cdd2..eab7b5d 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1290,10 +1290,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveres af <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="1610714069627824309">"Tryk for at administrere netværket."</string>
<string name="vpn_text_long" msgid="4907843483284977618">"Forbundet til <xliff:g id="SESSION">%s</xliff:g>. Tryk for at administrere netværket."</string>
- <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Opretter forbindelse til altid aktiveret VPN…"</string>
- <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Altid aktiveret VPN er forbundet"</string>
- <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Forbindelsen til altid aktiveret VPN er afbrudt"</string>
- <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fejl i altid aktiveret VPN"</string>
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Opretter forbindelse til konstant VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Konstant VPN er forbundet"</string>
+ <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Forbindelsen til konstant VPN er afbrudt"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fejl i konstant VPN"</string>
<string name="vpn_lockdown_config" msgid="5099330695245008680">"Tryk for at konfigurere"</string>
<string name="upload_file" msgid="2897957172366730416">"Vælg fil"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil er valgt"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index b58df81..e72e419 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -447,7 +447,7 @@
<string name="permlab_changeTetherState" msgid="5952584964373017960">"Tethering-Konnektivität ändern"</string>
<string name="permdesc_changeTetherState" msgid="1524441344412319780">"Ermöglicht der App, den Status der Tethering-Konnektivität zu ändern"</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"WLAN-Verbindungen abrufen"</string>
- <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Ermöglicht der App, Informationen zu WLANs abzurufen, etwa ob ein WLAN aktiviert ist, und den Namen verbundener WLAN-Geräte."</string>
+ <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Ermöglicht der App, Informationen zu WLAN-Netzwerken abzurufen, etwa ob ein WLAN aktiviert ist, und den Namen verbundener WLAN-Geräte."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"WLAN-Verbindungen herstellen und trennen"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"Ermöglicht der App, eine Verbindung zu WLAN-Zugangspunkten herzustellen und solche zu trennen und Änderungen an der Gerätekonfiguration für WLAN-Netzwerke vorzunehmen."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"WLAN-Multicast-Empfang zulassen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 002997f..97e85e1 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -90,7 +90,7 @@
<string name="serviceNotProvisioned" msgid="8614830180508686666">"Η υπηρεσία δεν προβλέπεται."</string>
<string name="CLIRPermanent" msgid="3377371145926835671">"Δεν μπορείτε να αλλάξετε τη ρύθμιση του αναγνωριστικού καλούντος."</string>
<string name="RestrictedOnDataTitle" msgid="1322504692764166532">"Δεν υπάρχει υπηρεσία δεδομένων"</string>
- <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"Αδυναμία πραγματοποίησης κλήσεων έκτακτης ανάγκης"</string>
+ <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"Δεν επιτρέπονται οι κλήσεις έκτακτης ανάγκης"</string>
<string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Δεν υπάρχει φωνητική υπηρεσία"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"Δεν υπάρχει φωνητική υπηρεσία/υπηρεσία έκτακτης ανάγκης"</string>
<string name="RestrictedStateContent" msgid="4278821484643362350">"Δεν προσφέρεται προσωρινά από το δίκτυο κινητής τηλεφωνίας στην τοποθεσία σας"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 52ffe6c..a02e8cc 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -90,7 +90,7 @@
<string name="serviceNotProvisioned" msgid="8614830180508686666">"ਸੇਵਾ ਪ੍ਰਬੰਧਿਤ ਨਹੀਂ ਹੈ।"</string>
<string name="CLIRPermanent" msgid="3377371145926835671">"ਤੁਸੀਂ ਕਾਲਰ ID ਸੈਟਿੰਗ ਨਹੀਂ ਬਦਲ ਸਕਦੇ।"</string>
<string name="RestrictedOnDataTitle" msgid="1322504692764166532">"ਕੋਈ ਡੈਟਾ ਸੇਵਾ ਨਹੀਂ"</string>
- <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"ਕੋਈ ਸੰਕਟਕਾਲੀਨ ਕਾਲਿੰਗ ਨਹੀਂ"</string>
+ <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"ਸੰਕਟਕਾਲ ਵਿੱਚ ਕੋਈ ਕਾਲ ਨਹੀਂ"</string>
<string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"ਕੋਈ ਆਵਾਜ਼ੀ ਸੇਵਾ ਨਹੀਂ"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"ਕੋਈ ਆਵਾਜ਼ੀ/ਸੰਕਟਕਾਲੀਨ ਸੇਵਾ ਨਹੀਂ"</string>
<string name="RestrictedStateContent" msgid="4278821484643362350">"ਤੁਹਾਡੇ ਟਿਕਾਣੇ \'ਤੇ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਵੱਲੋਂ ਉਪਲਬਧ ਨਹੀਂ ਕਰਵਾਈ ਗਈ"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 76dc06f..d1e15280 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -90,7 +90,7 @@
<string name="serviceNotProvisioned" msgid="8614830180508686666">"సేవ కేటాయించబడలేదు."</string>
<string name="CLIRPermanent" msgid="3377371145926835671">"మీరు కాలర్ ID సెట్టింగ్ను మార్చలేరు."</string>
<string name="RestrictedOnDataTitle" msgid="1322504692764166532">"డేటా సేవ లేదు"</string>
- <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"అత్యవసర కాలింగ్ లేదు"</string>
+ <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"అత్యవసర కాలింగ్ సదుపాయం లేదు"</string>
<string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"వాయిస్ సేవ లేదు"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"వాయిస్/అత్యవసర సేవ లేదు"</string>
<string name="RestrictedStateContent" msgid="4278821484643362350">"మీ స్థానంలో మొబైల్ నెట్వర్క్ ద్వారా తాత్కాలికంగా అందించబడదు"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 76cee70..062da95 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2968,6 +2968,8 @@
<!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
<string name="config_headlineFontFamily" translatable="false"></string>
+ <!-- Name of a font family to use for headlines. Defaults to sans-serif-light -->
+ <string name="config_headlineFontFamilyLight" translatable="false">sans-serif-light</string>
<!-- An array of packages that need to be treated as type system in battery settings -->
<string-array translatable="false" name="config_batteryPackageTypeSystem">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 241886c..7338902 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3041,6 +3041,7 @@
<java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" />
<java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
<java-symbol type="string" name="config_headlineFontFamily" />
+ <java-symbol type="string" name="config_headlineFontFamilyLight" />
<java-symbol type="drawable" name="stat_sys_vitals" />
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 47ecf32..6ef3091 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -758,6 +758,8 @@
public static native int systemReady();
+ public static native float getStreamVolumeDB(int stream, int index, int device);
+
// Items shared with audio service
/**
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 22f53ea..e38cca7 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -57,6 +57,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
public class CaptivePortalLoginActivity extends Activity {
private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName();
@@ -82,6 +83,8 @@
private ConnectivityManager mCm;
private boolean mLaunchBrowser = false;
private MyWebViewClient mWebViewClient;
+ // Ensures that done() happens once exactly, handling concurrent callers with atomic operations.
+ private final AtomicBoolean isDone = new AtomicBoolean(false);
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -178,13 +181,13 @@
}
private void done(Result result) {
+ if (isDone.getAndSet(true)) {
+ // isDone was already true: done() already called
+ return;
+ }
if (DBG) {
Log.d(TAG, String.format("Result %s for %s", result.name(), mUrl.toString()));
}
- if (mNetworkCallback != null) {
- mCm.unregisterNetworkCallback(mNetworkCallback);
- mNetworkCallback = null;
- }
logMetricsEvent(result.metricsEvent);
switch (result) {
case DISMISSED:
@@ -244,8 +247,8 @@
public void onDestroy() {
super.onDestroy();
if (mNetworkCallback != null) {
+ // mNetworkCallback is not null if mUrl is not null.
mCm.unregisterNetworkCallback(mNetworkCallback);
- mNetworkCallback = null;
}
if (mLaunchBrowser) {
// Give time for this network to become default. After 500ms just proceed.
diff --git a/packages/Osu2/Android.mk b/packages/Osu2/Android.mk
new file mode 100644
index 0000000..05586f0
--- /dev/null
+++ b/packages/Osu2/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := Osu2
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+include $(BUILD_PACKAGE)
+
+########################
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/Osu2/AndroidManifest.xml b/packages/Osu2/AndroidManifest.xml
new file mode 100644
index 0000000..236b120b
--- /dev/null
+++ b/packages/Osu2/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.osu">
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:enabled="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name">
+ <activity android:name=".MainActivity" android:exported="true">
+ </activity>
+ </application>
+
+</manifest>
diff --git a/packages/Osu2/res/layout/activity_main.xml b/packages/Osu2/res/layout/activity_main.xml
new file mode 100644
index 0000000..f9504c9
--- /dev/null
+++ b/packages/Osu2/res/layout/activity_main.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+</LinearLayout>
diff --git a/packages/Osu2/res/mipmap-hdpi/ic_launcher.png b/packages/Osu2/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/packages/Osu2/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/packages/Osu2/res/mipmap-mdpi/ic_launcher.png b/packages/Osu2/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/packages/Osu2/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/packages/Osu2/res/mipmap-xhdpi/ic_launcher.png b/packages/Osu2/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/packages/Osu2/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/Osu2/res/mipmap-xxhdpi/ic_launcher.png b/packages/Osu2/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/packages/Osu2/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/Osu2/res/mipmap-xxxhdpi/ic_launcher.png b/packages/Osu2/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/packages/Osu2/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/packages/Osu2/res/values-w820dp/dimens.xml b/packages/Osu2/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/packages/Osu2/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/packages/Osu2/res/values/colors.xml b/packages/Osu2/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/packages/Osu2/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/packages/Osu2/res/values/dimens.xml b/packages/Osu2/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/packages/Osu2/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/packages/Osu2/res/values/strings.xml b/packages/Osu2/res/values/strings.xml
new file mode 100644
index 0000000..e5b1af6
--- /dev/null
+++ b/packages/Osu2/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Passpoint Online Sign-Up</string>
+</resources>
diff --git a/packages/Osu2/src/com/android/osu/Constants.java b/packages/Osu2/src/com/android/osu/Constants.java
new file mode 100644
index 0000000..cd046d8
--- /dev/null
+++ b/packages/Osu2/src/com/android/osu/Constants.java
@@ -0,0 +1,24 @@
+/*
+ * 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.osu;
+
+public final class Constants {
+ public static final String INTENT_EXTRA_COMMAND = "com.android.osu.extra.COMMAND";
+ public static final String INTENT_EXTRA_OSU_PROVIDER = "com.android.osu.extra.OSU_PROVIDER";
+
+ public static final String COMMAND_PROVISION = "Provision";
+}
\ No newline at end of file
diff --git a/packages/Osu2/src/com/android/osu/MainActivity.java b/packages/Osu2/src/com/android/osu/MainActivity.java
new file mode 100644
index 0000000..4e2136b
--- /dev/null
+++ b/packages/Osu2/src/com/android/osu/MainActivity.java
@@ -0,0 +1,76 @@
+/*
+ * 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.osu;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.wifi.hotspot2.OsuProvider;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Main entry point for the OSU (Online Sign-Up) app.
+ */
+public class MainActivity extends Activity {
+ private static final String TAG = "OSU_MainActivity";
+ private OsuService mService;
+
+ @Override
+ protected void onCreate(Bundle saveInstanceState) {
+ super.onCreate(saveInstanceState);
+
+ Intent intent = getIntent();
+ if (intent == null) {
+ Log.e(TAG, "Intent not provided");
+ finish();
+ }
+
+ if (!intent.hasExtra(Constants.INTENT_EXTRA_COMMAND)) {
+ Log.e(TAG, "Command not provided");
+ finish();
+ }
+
+ String command = intent.getStringExtra(Constants.INTENT_EXTRA_COMMAND);
+ switch (command) {
+ case Constants.COMMAND_PROVISION:
+ if (!startProvisionService(intent.getParcelableExtra(
+ Constants.INTENT_EXTRA_OSU_PROVIDER))) {
+ finish();
+ }
+ break;
+ default:
+ Log.e(TAG, "Unknown command: '" + command + "'");
+ finish();
+ break;
+ }
+ }
+
+ /**
+ * Start the {@link ProvisionService} to perform provisioning tasks.
+ *
+ * @return true if service is started
+ */
+ private boolean startProvisionService(OsuProvider provider) {
+ if (provider == null) {
+ Log.e(TAG, "OSU Provider not provided");
+ return false;
+ }
+ mService = new ProvisionService(this, provider);
+ mService.start();
+ return true;
+ }
+}
diff --git a/packages/Osu2/src/com/android/osu/NetworkConnection.java b/packages/Osu2/src/com/android/osu/NetworkConnection.java
new file mode 100644
index 0000000..9f5b929
--- /dev/null
+++ b/packages/Osu2/src/com/android/osu/NetworkConnection.java
@@ -0,0 +1,206 @@
+/*
+ * 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.osu;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Network;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Responsible for setup/monitor on a Wi-Fi connection.
+ */
+public class NetworkConnection {
+ private static final String TAG = "OSU_NetworkConnection";
+
+ private final WifiManager mWifiManager;
+ private final Callbacks mCallbacks;
+ private final int mNetworkId;
+ private boolean mConnected = false;
+
+ /**
+ * Callbacks on Wi-Fi connection state changes.
+ */
+ public interface Callbacks {
+ /**
+ * Invoked when network connection is established with IP connectivity.
+ *
+ * @param network {@link Network} associated with the connected network.
+ */
+ public void onConnected(Network network);
+
+ /**
+ * Invoked when the targeted network is disconnected.
+ */
+ public void onDisconnected();
+
+ /**
+ * Invoked when network connection is not established within the pre-defined timeout.
+ */
+ public void onTimeout();
+ }
+
+ /**
+ * Create an instance of {@link NetworkConnection} for the specified Wi-Fi network.
+ * The Wi-Fi network (specified by its SSID) will be added/enabled as part of this object
+ * creation.
+ *
+ * {@link #teardown} will need to be invoked once you're done with this connection,
+ * to remove the given Wi-Fi network from the framework.
+ *
+ * @param context The application context
+ * @param handler The handler to dispatch the processing of received broadcast intents
+ * @param ssid The SSID to connect to
+ * @param nai The network access identifier associated with the AP
+ * @param callbacks The callbacks to be invoked on network change events
+ * @throws IOException when failed to add/enable the specified Wi-Fi network
+ */
+ public NetworkConnection(Context context, Handler handler, WifiSsid ssid, String nai,
+ Callbacks callbacks) throws IOException {
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ mCallbacks = callbacks;
+ mNetworkId = connect(ssid, nai);
+
+ // TODO(zqiu): setup alarm to timed out the connection attempt.
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ handleNetworkStateChanged(
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO),
+ intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO));
+ }
+ }
+ };
+ // Provide a Handler so that the onReceive call will be run on the specified handler
+ // thread instead of the main thread.
+ context.registerReceiver(receiver, filter, null, handler);
+ }
+
+ /**
+ * Teardown the network connection by removing the network.
+ */
+ public void teardown() {
+ mWifiManager.removeNetwork(mNetworkId);
+ }
+
+ /**
+ * Connect to a OSU Wi-Fi network specified by the given SSID. The security type of the Wi-Fi
+ * network is either open or OSEN (OSU Server-only authenticated layer 2 Encryption Network).
+ * When network access identifier is provided, OSEN is used.
+ *
+ * @param ssid The SSID to connect to
+ * @param nai Network access identifier of the network
+ *
+ * @return unique ID associated with the network
+ * @throws IOException
+ */
+ private int connect(WifiSsid ssid, String nai) throws IOException {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "\"" + ssid.toString() + "\"";
+ if (TextUtils.isEmpty(nai)) {
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ } else {
+ // TODO(zqiu): configuration setup for OSEN.
+ }
+ int networkId = mWifiManager.addNetwork(config);
+ if (networkId < 0) {
+ throw new IOException("Failed to add OSU network");
+ }
+ if (!mWifiManager.enableNetwork(networkId, true)) {
+ throw new IOException("Failed to enable OSU network");
+ }
+ return networkId;
+ }
+
+ /**
+ * Handle network state changed events.
+ *
+ * @param networkInfo {@link NetworkInfo} indicating the current network state
+ * @param wifiInfo {@link WifiInfo} associated with the current network when connected
+ */
+ private void handleNetworkStateChanged(NetworkInfo networkInfo, WifiInfo wifiInfo) {
+ if (networkInfo == null) {
+ Log.e(TAG, "NetworkInfo not provided for network state changed event");
+ return;
+ }
+ switch (networkInfo.getDetailedState()) {
+ case CONNECTED:
+ handleConnectedEvent(wifiInfo);
+ break;
+ case DISCONNECTED:
+ handleDisconnectedEvent();
+ break;
+ default:
+ Log.d(TAG, "Ignore uninterested state: " + networkInfo.getDetailedState());
+ break;
+ }
+ }
+
+ /**
+ * Handle network connected event.
+ *
+ * @param wifiInfo {@link WifiInfo} associated with the current connection
+ */
+ private void handleConnectedEvent(WifiInfo wifiInfo) {
+ if (mConnected) {
+ // No-op if already connected.
+ return;
+ }
+ if (wifiInfo == null) {
+ Log.e(TAG, "WifiInfo not provided for connected event");
+ return;
+ }
+ if (wifiInfo.getNetworkId() != mNetworkId) {
+ return;
+ }
+ Network network = mWifiManager.getCurrentNetwork();
+ if (network == null) {
+ Log.e(TAG, "Current network is not set");
+ return;
+ }
+ mConnected = true;
+ mCallbacks.onConnected(network);
+ }
+
+ /**
+ * Handle network disconnected event.
+ */
+ private void handleDisconnectedEvent() {
+ if (!mConnected) {
+ // No-op if not connected, most likely a disconnect event for a different network.
+ return;
+ }
+ mConnected = false;
+ mCallbacks.onDisconnected();
+ }
+}
diff --git a/packages/Osu2/src/com/android/osu/OsuService.java b/packages/Osu2/src/com/android/osu/OsuService.java
new file mode 100644
index 0000000..46a3c84
--- /dev/null
+++ b/packages/Osu2/src/com/android/osu/OsuService.java
@@ -0,0 +1,33 @@
+/*
+ * 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.osu;
+
+/**
+ * Abstraction for services that can be performed by the OSU app, such as provisioning,
+ * subscription remediation, and etc.
+ */
+public interface OsuService {
+ /**
+ * Start the service.
+ */
+ public void start();
+
+ /**
+ * Stop the service.
+ */
+ public void stop();
+}
diff --git a/packages/Osu2/src/com/android/osu/ProvisionService.java b/packages/Osu2/src/com/android/osu/ProvisionService.java
new file mode 100644
index 0000000..b1d43b2
--- /dev/null
+++ b/packages/Osu2/src/com/android/osu/ProvisionService.java
@@ -0,0 +1,119 @@
+/*
+ * 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.osu;
+
+import android.content.Context;
+import android.net.Network;
+import android.net.wifi.hotspot2.OsuProvider;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Service responsible for performing Passpoint subscription provisioning tasks.
+ * This service will run on a separate thread to avoid blocking on the Main thread.
+ */
+public class ProvisionService implements OsuService {
+ private static final String TAG = "OSU_ProvisionService";
+ private static final int COMMAND_START = 1;
+ private static final int COMMAND_STOP = 2;
+
+ private final Context mContext;
+ private final HandlerThread mHandlerThread;
+ private final ServiceHandler mServiceHandler;
+ private final OsuProvider mProvider;
+
+ private boolean mStarted = false;
+ private NetworkConnection mNetworkConnection = null;
+
+ public ProvisionService(Context context, OsuProvider provider) {
+ mContext = context;
+ mProvider = provider;
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mServiceHandler = new ServiceHandler(mHandlerThread.getLooper());
+ }
+
+ @Override
+ public void start() {
+ mServiceHandler.sendMessage(mServiceHandler.obtainMessage(COMMAND_START));
+ }
+
+ @Override
+ public void stop() {
+ mServiceHandler.sendMessage(mServiceHandler.obtainMessage(COMMAND_STOP));
+ }
+
+ /**
+ * Handler class for handling commands to the ProvisionService.
+ */
+ private final class ServiceHandler extends Handler {
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case COMMAND_START:
+ if (mStarted) {
+ Log.e(TAG, "Service already started");
+ return;
+ }
+ try {
+ // Initiate network connection to the OSU AP.
+ mNetworkConnection = new NetworkConnection(
+ mContext, this, mProvider.getOsuSsid(),
+ mProvider.getNetworkAccessIdentifier(), new NetworkCallbacks());
+ mStarted = true;
+ } catch (IOException e) {
+ // TODO(zqiu): broadcast failure event via LocalBroadcastManager.
+ }
+ break;
+ case COMMAND_STOP:
+ if (!mStarted) {
+ Log.e(TAG, "Service not started");
+ return;
+ }
+ Log.e(TAG, "Stop provision service");
+ break;
+ default:
+ Log.e(TAG, "Unknown command: " + msg.what);
+ break;
+ }
+ }
+ }
+
+ private final class NetworkCallbacks implements NetworkConnection.Callbacks {
+ @Override
+ public void onConnected(Network network) {
+ Log.d(TAG, "Connected to OSU AP");
+ }
+
+ @Override
+ public void onDisconnected() {
+ }
+
+ @Override
+ public void onTimeout() {
+ }
+ }
+}
diff --git a/packages/Osu2/tests/Android.mk b/packages/Osu2/tests/Android.mk
new file mode 100644
index 0000000..4b6e0e6
--- /dev/null
+++ b/packages/Osu2/tests/Android.mk
@@ -0,0 +1,43 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_JACK_FLAGS := --multi-dex native
+
+LOCAL_PACKAGE_NAME := OsuTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_INSTRUMENTATION_FOR := Osu2
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ mockito-target-minus-junit4 \
+ frameworks-base-testutils
+
+# Code coverage puts us over the dex limit, so enable multi-dex for coverage-enabled builds
+ifeq (true,$(EMMA_INSTRUMENT))
+LOCAL_JACK_FLAGS := --multi-dex native
+LOCAL_DX_FLAGS := --multi-dex
+endif # EMMA_INSTRUMENT
+
+include $(BUILD_PACKAGE)
diff --git a/packages/Osu2/tests/AndroidManifest.xml b/packages/Osu2/tests/AndroidManifest.xml
new file mode 100644
index 0000000..e22c112
--- /dev/null
+++ b/packages/Osu2/tests/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.osu.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:label="OsuTestDummyLabel"
+ android:name="OsuTestDummyName">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.osu"
+ android:label="OSU App Tests">
+ </instrumentation>
+
+</manifest>
diff --git a/packages/Osu2/tests/README.md b/packages/Osu2/tests/README.md
new file mode 100644
index 0000000..dbfa79c
--- /dev/null
+++ b/packages/Osu2/tests/README.md
@@ -0,0 +1,45 @@
+# OSU Unit Tests
+This package contains unit tests for the OSU app based on the
+[Android Testing Support Library](http://developer.android.com/tools/testing-support-library/index.html).
+The test cases are built using the [JUnit](http://junit.org/) and [Mockito](http://mockito.org/)
+libraries.
+
+## Running Tests
+The easiest way to run tests is simply run
+
+```
+frameworks/base/packages/Osu2/tests/runtests.sh
+```
+
+`runtests.sh` will build the test project and all of its dependencies and push the APK to the
+connected device. It will then run the tests on the device.
+
+To enable syncing data to the device for first time after clean reflash:
+1. adb disable-verity
+2. adb reboot
+3. adb remount
+
+See below for a few example of options to limit which tests are run.
+See the
+[AndroidJUnitRunner Documentation](https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html)
+for more details on the supported options.
+
+```
+runtests.sh -e package com.android.osu
+runtests.sh -e class com.android.osu.NetworkConnectionTest
+```
+
+If you manually build and push the test APK to the device you can run tests using
+
+```
+adb shell am instrument -w 'com.android.osu.tests/android.support.test.runner.AndroidJUnitRunner'
+```
+
+## Adding Tests
+Tests can be added by adding classes to the src directory. JUnit4 style test cases can
+be written by simply annotating test methods with `org.junit.Test`.
+
+## Debugging Tests
+If you are trying to debug why tests are not doing what you expected, you can add android log
+statements and use logcat to view them. The beginning and end of every tests is automatically logged
+with the tag `TestRunner`.
diff --git a/packages/Osu2/tests/runtests.sh b/packages/Osu2/tests/runtests.sh
new file mode 100755
index 0000000..3513f5b
--- /dev/null
+++ b/packages/Osu2/tests/runtests.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+if [ -z $ANDROID_BUILD_TOP ]; then
+ echo "You need to source and lunch before you can use this script"
+ exit 1
+fi
+
+echo "Running tests"
+
+set -e # fail early
+
+echo "+ mmma -j32 $ANDROID_BUILD_TOP/frameworks/base/packages/Osu2/tests"
+# NOTE Don't actually run the command above since this shell doesn't inherit functions from the
+# caller.
+make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk MODULES-IN-frameworks-base-packages-Osu2-tests
+
+set -x # print commands
+
+adb root
+adb wait-for-device
+
+adb install -r -g "$OUT/data/app/OsuTests/OsuTests.apk"
+
+adb shell am instrument -w "$@" 'com.android.osu.tests/android.support.test.runner.AndroidJUnitRunner'
diff --git a/packages/Osu2/tests/src/com/android/osu/NetworkConnectionTest.java b/packages/Osu2/tests/src/com/android/osu/NetworkConnectionTest.java
new file mode 100644
index 0000000..2753249
--- /dev/null
+++ b/packages/Osu2/tests/src/com/android/osu/NetworkConnectionTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.osu;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.io.IOException;
+
+/**
+ * Unit tests for {@link com.android.osu.NetworkConnection}.
+ */
+@SmallTest
+public class NetworkConnectionTest {
+ private static final String TEST_SSID = "TEST SSID";
+ private static final String TEST_SSID_WITH_QUOTES = "\"" + TEST_SSID + "\"";
+ private static final int TEST_NETWORK_ID = 1;
+
+ @Mock Context mContext;
+ @Mock Handler mHandler;
+ @Mock WifiManager mWifiManager;
+ @Mock NetworkConnection.Callbacks mCallbacks;
+
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+ }
+
+ /**
+ * Verify that an IOException will be thrown when failed to add the network.
+ *
+ * @throws Exception
+ */
+ @Test(expected = IOException.class)
+ public void networkAddFailed() throws Exception {
+ when(mWifiManager.addNetwork(any(WifiConfiguration.class))).thenReturn(-1);
+ new NetworkConnection(mContext, mHandler, WifiSsid.createFromAsciiEncoded(TEST_SSID),
+ null, mCallbacks);
+ }
+
+ /**
+ * Verify that an IOException will be thrown when failed to enable the network.
+ *
+ * @throws Exception
+ */
+ @Test(expected = IOException.class)
+ public void networkEnableFailed() throws Exception {
+ when(mWifiManager.addNetwork(any(WifiConfiguration.class))).thenReturn(TEST_NETWORK_ID);
+ when(mWifiManager.enableNetwork(eq(TEST_NETWORK_ID), eq(true))).thenReturn(false);
+ new NetworkConnection(mContext, mHandler, WifiSsid.createFromAsciiEncoded(TEST_SSID),
+ null, mCallbacks);
+ }
+
+ /**
+ * Verify that the connection is established after receiving a
+ * WifiManager.NETWORK_STATE_CHANGED_ACTION intent indicating that we are connected.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void openNetworkConnectionEstablished() throws Exception {
+ when(mWifiManager.addNetwork(any(WifiConfiguration.class))).thenReturn(TEST_NETWORK_ID);
+ when(mWifiManager.enableNetwork(eq(TEST_NETWORK_ID), eq(true))).thenReturn(true);
+ NetworkConnection connection = new NetworkConnection(mContext, mHandler,
+ WifiSsid.createFromAsciiEncoded(TEST_SSID), null, mCallbacks);
+
+ // Verify the WifiConfiguration being added.
+ ArgumentCaptor<WifiConfiguration> wifiConfig =
+ ArgumentCaptor.forClass(WifiConfiguration.class);
+ verify(mWifiManager).addNetwork(wifiConfig.capture());
+ assertEquals(wifiConfig.getValue().SSID, TEST_SSID_WITH_QUOTES);
+
+ // Capture the BroadcastReceiver.
+ ArgumentCaptor<BroadcastReceiver> receiver =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mContext).registerReceiver(receiver.capture(), any(), any(), any());
+
+ // Setup intent.
+ Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ NetworkInfo networkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
+ networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
+ WifiInfo wifiInfo = new WifiInfo();
+ wifiInfo.setNetworkId(TEST_NETWORK_ID);
+ intent.putExtra(WifiManager.EXTRA_WIFI_INFO, wifiInfo);
+
+ // Send intent to the receiver.
+ Network network = new Network(0);
+ when(mWifiManager.getCurrentNetwork()).thenReturn(network);
+ receiver.getValue().onReceive(mContext, intent);
+
+ // Verify we are connected.
+ verify(mCallbacks).onConnected(eq(network));
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index 653a453..81f7315 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -16,6 +16,8 @@
package com.android.printspooler.model;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -29,21 +31,27 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.print.PageRange;
import android.print.PrintAttributes;
-import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
import android.print.PrintDocumentInfo;
import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
+
import com.android.internal.annotations.GuardedBy;
import com.android.printspooler.renderer.IPdfRenderer;
import com.android.printspooler.renderer.PdfManipulationService;
import com.android.printspooler.util.BitmapSerializeUtils;
+import com.android.printspooler.util.PageRangeUtils;
+
import dalvik.system.CloseGuard;
+
import libcore.io.IoUtils;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -69,8 +77,9 @@
private RenderSpec mLastRenderSpec;
- private int mScheduledPreloadFirstShownPage = INVALID_PAGE_INDEX;
- private int mScheduledPreloadLastShownPage = INVALID_PAGE_INDEX;
+ @Nullable private PageRange mScheduledPreloadVisiblePages;
+ @Nullable private PageRange[] mScheduledPreloadSelectedPages;
+ @Nullable private PageRange[] mScheduledPreloadWrittenPages;
private int mState;
@@ -129,14 +138,24 @@
}
}
- public void startPreload(int firstShownPage, int lastShownPage) {
+ /**
+ * Preload selected, written pages around visiblePages.
+ *
+ * @param visiblePages The pages currently visible
+ * @param selectedPages The pages currently selected (e.g. they might become visible by
+ * scrolling)
+ * @param writtenPages The pages currently in the document
+ */
+ public void startPreload(@NonNull PageRange visiblePages, @NonNull PageRange[] selectedPages,
+ @NonNull PageRange[] writtenPages) {
// If we do not have a render spec we have no clue what size the
// preloaded bitmaps should be, so just take a note for what to do.
if (mLastRenderSpec == null) {
- mScheduledPreloadFirstShownPage = firstShownPage;
- mScheduledPreloadLastShownPage = lastShownPage;
+ mScheduledPreloadVisiblePages = visiblePages;
+ mScheduledPreloadSelectedPages = selectedPages;
+ mScheduledPreloadWrittenPages = writtenPages;
} else if (mState == STATE_OPENED) {
- mRenderer.startPreload(firstShownPage, lastShownPage, mLastRenderSpec);
+ mRenderer.startPreload(visiblePages, selectedPages, writtenPages, mLastRenderSpec);
}
}
@@ -225,11 +244,12 @@
// We tired to preload but didn't know the bitmap size, now
// that we know let us do the work.
- if (mScheduledPreloadFirstShownPage != INVALID_PAGE_INDEX
- && mScheduledPreloadLastShownPage != INVALID_PAGE_INDEX) {
- startPreload(mScheduledPreloadFirstShownPage, mScheduledPreloadLastShownPage);
- mScheduledPreloadFirstShownPage = INVALID_PAGE_INDEX;
- mScheduledPreloadLastShownPage = INVALID_PAGE_INDEX;
+ if (mScheduledPreloadVisiblePages != null) {
+ startPreload(mScheduledPreloadVisiblePages, mScheduledPreloadSelectedPages,
+ mScheduledPreloadWrittenPages);
+ mScheduledPreloadVisiblePages = null;
+ mScheduledPreloadSelectedPages = null;
+ mScheduledPreloadWrittenPages = null;
}
if (mState == STATE_OPENED) {
@@ -526,10 +546,45 @@
mDestroyed = true;
}
- public void startPreload(int firstShownPage, int lastShownPage, RenderSpec renderSpec) {
+ /**
+ * How many pages are {@code pages} before pageNum. E.g. page 5 in [0-1], [4-7] has the
+ * index 4.
+ *
+ * @param pageNum The number of the page to find
+ * @param pages A normalized array of page ranges
+ *
+ * @return The index or {@link #INVALID_PAGE_INDEX} if not found
+ */
+ private int findIndexOfPage(int pageNum, @NonNull PageRange[] pages) {
+ int pagesBefore = 0;
+ for (int i = 0; i < pages.length; i++) {
+ if (pages[i].contains(pageNum)) {
+ return pagesBefore + pageNum - pages[i].getStart();
+ } else {
+ pagesBefore += pages[i].getSize();
+ }
+ }
+
+ return INVALID_PAGE_INDEX;
+ }
+
+ void startPreload(@NonNull PageRange visiblePages, @NonNull PageRange[] selectedPages,
+ @NonNull PageRange[] writtenPages, RenderSpec renderSpec) {
+ if (PageRangeUtils.isAllPages(selectedPages)) {
+ selectedPages = new PageRange[]{new PageRange(0, mPageCount - 1)};
+ }
+
if (DEBUG) {
- Log.i(LOG_TAG, "Preloading pages around [" + firstShownPage
- + "-" + lastShownPage + "]");
+ Log.i(LOG_TAG, "Preloading pages around " + visiblePages + " from "
+ + Arrays.toString(selectedPages));
+ }
+
+ int firstVisiblePageIndex = findIndexOfPage(visiblePages.getStart(), selectedPages);
+ int lastVisiblePageIndex = findIndexOfPage(visiblePages.getEnd(), selectedPages);
+
+ if (firstVisiblePageIndex == INVALID_PAGE_INDEX
+ || lastVisiblePageIndex == INVALID_PAGE_INDEX) {
+ return;
}
final int bitmapSizeInBytes = renderSpec.bitmapWidth * renderSpec.bitmapHeight
@@ -537,28 +592,33 @@
final int maxCachedPageCount = mPageContentCache.getMaxSizeInBytes()
/ bitmapSizeInBytes;
final int halfPreloadCount = (maxCachedPageCount
- - (lastShownPage - firstShownPage)) / 2 - 1;
+ - (lastVisiblePageIndex - firstVisiblePageIndex)) / 2 - 1;
- final int excessFromStart;
- if (firstShownPage - halfPreloadCount < 0) {
- excessFromStart = halfPreloadCount - firstShownPage;
- } else {
- excessFromStart = 0;
+ final int fromIndex = Math.max(firstVisiblePageIndex - halfPreloadCount, 0);
+ final int toIndex = lastVisiblePageIndex + halfPreloadCount;
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "fromIndex=" + fromIndex + " toIndex=" + toIndex);
}
- final int excessFromEnd;
- if (lastShownPage + halfPreloadCount >= mPageCount) {
- excessFromEnd = (lastShownPage + halfPreloadCount) - mPageCount;
- } else {
- excessFromEnd = 0;
- }
+ int previousRangeSizes = 0;
+ for (int rangeNum = 0; rangeNum < selectedPages.length; rangeNum++) {
+ PageRange range = selectedPages[rangeNum];
- final int fromIndex = Math.max(firstShownPage - halfPreloadCount - excessFromEnd, 0);
- final int toIndex = Math.min(lastShownPage + halfPreloadCount + excessFromStart,
- mPageCount - 1);
+ int thisRangeStart = Math.max(0, fromIndex - previousRangeSizes);
+ int thisRangeEnd = Math.min(range.getSize(), toIndex - previousRangeSizes + 1);
- for (int i = fromIndex; i <= toIndex; i++) {
- renderPage(i, renderSpec, null);
+ for (int i = thisRangeStart; i < thisRangeEnd; i++) {
+ if (PageRangeUtils.contains(writtenPages, range.getStart() + i)) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Preloading " + (range.getStart() + i));
+ }
+
+ renderPage(range.getStart() + i, renderSpec, null);
+ }
+ }
+
+ previousRangeSizes += range.getSize();
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index e172948..ad46b60 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -16,6 +16,7 @@
package com.android.printspooler.ui;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -24,8 +25,8 @@
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
-import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
import android.print.PrintDocumentInfo;
import android.support.v7.widget.RecyclerView.Adapter;
import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -33,11 +34,12 @@
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.MeasureSpec;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.view.View.MeasureSpec;
import android.widget.TextView;
+
import com.android.printspooler.R;
import com.android.printspooler.model.OpenDocumentCallback;
import com.android.printspooler.model.PageContentRepository;
@@ -45,6 +47,7 @@
import com.android.printspooler.util.PageRangeUtils;
import com.android.printspooler.widget.PageContentView;
import com.android.printspooler.widget.PreviewPageFrame;
+
import dalvik.system.CloseGuard;
import java.util.ArrayList;
@@ -797,14 +800,16 @@
page.setTag(null);
}
- public void startPreloadContent(PageRange pageRangeInAdapter) {
- final int startPageInDocument = computePageIndexInDocument(pageRangeInAdapter.getStart());
- final int startPageInFile = computePageIndexInFile(startPageInDocument);
- final int endPageInDocument = computePageIndexInDocument(pageRangeInAdapter.getEnd());
- final int endPageInFile = computePageIndexInFile(endPageInDocument);
- if (startPageInDocument != INVALID_PAGE_INDEX && endPageInDocument != INVALID_PAGE_INDEX) {
- mPageContentRepository.startPreload(startPageInFile, endPageInFile);
+ void startPreloadContent(@NonNull PageRange visiblePagesInAdapter) {
+ int startVisibleDocument = computePageIndexInDocument(visiblePagesInAdapter.getStart());
+ int endVisibleDocument = computePageIndexInDocument(visiblePagesInAdapter.getEnd());
+ if (startVisibleDocument == INVALID_PAGE_INDEX
+ || endVisibleDocument == INVALID_PAGE_INDEX) {
+ return;
}
+
+ mPageContentRepository.startPreload(new PageRange(startVisibleDocument, endVisibleDocument),
+ mSelectedPages, mWrittenPages);
}
public void stopPreloadContent() {
diff --git a/packages/SettingsLib/res/drawable/ic_lockscreen_ime.xml b/packages/SettingsLib/res/drawable/ic_lockscreen_ime.xml
index 4aa8569..4b81a3c 100644
--- a/packages/SettingsLib/res/drawable/ic_lockscreen_ime.xml
+++ b/packages/SettingsLib/res/drawable/ic_lockscreen_ime.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9
diff --git a/packages/SystemUI/res/drawable/ic_mode_edit.xml b/packages/SettingsLib/res/drawable/ic_mode_edit.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_mode_edit.xml
rename to packages/SettingsLib/res/drawable/ic_mode_edit.xml
diff --git a/packages/SettingsLib/res/layout/preference_two_target.xml b/packages/SettingsLib/res/layout/preference_two_target.xml
index 69b7204..7000940 100644
--- a/packages/SettingsLib/res/layout/preference_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_two_target.xml
@@ -32,7 +32,8 @@
android:background="?android:attr/selectableItemBackground"
android:gravity="start|center_vertical"
android:clipToPadding="false"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart">
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<LinearLayout
android:id="@+id/icon_frame"
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index fbdb394..b4c8cec 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -282,7 +282,7 @@
<string name="show_all_anrs" msgid="28462979638729082">"Tots els errors sense resposta"</string>
<string name="show_all_anrs_summary" msgid="641908614413544127">"Informa que una aplicació en segon pla no respon"</string>
<string name="show_notification_channel_warnings" msgid="1399948193466922683">"Mostra avisos del canal de notificacions"</string>
- <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra un avís a la pantalla quan una app publica una notificació sense canal vàlid"</string>
+ <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra un avís a la pantalla quan una aplicació publica una notificació sense un canal vàlid"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"Força permís d\'aplicacions a l\'emmagatzem. extern"</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"Permet que qualsevol aplicació es pugui escriure en un dispositiu d’emmagatzematge extern, independentment dels valors definits"</string>
<string name="force_resizable_activities" msgid="8615764378147824985">"Força l\'ajust de la mida de les activitats"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 66e55f9..2949012 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -28,7 +28,7 @@
<string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"No se estableció conexión debido a la mala calidad de la red"</string>
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Error de conexión Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticación"</string>
- <string name="wifi_cant_connect" msgid="5410016875644565884">"No se puede establecer conexión"</string>
+ <string name="wifi_cant_connect" msgid="5410016875644565884">"No se puede establecer la conexión"</string>
<string name="wifi_cant_connect_to_ap" msgid="1222553274052685331">"No se puede establecer conexión con \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
<string name="wifi_check_password_try_again" msgid="516958988102584767">"Revisa la contraseña y vuelve a intentarlo"</string>
<string name="wifi_not_in_range" msgid="1136191511238508967">"Fuera de alcance"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 414ff31..736579f 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -281,8 +281,8 @@
<string name="app_process_limit_title" msgid="4280600650253107163">"Limite proc. em 2º plano"</string>
<string name="show_all_anrs" msgid="28462979638729082">"Mostrar todos os ANR"</string>
<string name="show_all_anrs_summary" msgid="641908614413544127">"Mostrar erro \"Aplic. não Resp.\" p/ aplic. 2º plano"</string>
- <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Mostrar avisos do canal de notif."</string>
- <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra um aviso no ecrã quando uma aplic. publica uma notific. sem um canal válido"</string>
+ <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Mostrar avisos do canal de notificações"</string>
+ <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra um aviso no ecrã quando uma aplicação publica uma notificação sem o canal ser válido"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"Forçar perm. de aplicações no armazenamento ext."</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"Torna qualquer aplicação elegível para ser gravada no armazenamento externo, independentemente dos valores do manifesto"</string>
<string name="force_resizable_activities" msgid="8615764378147824985">"Forçar as atividades a serem redimensionáveis"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index d29d8d4..edd02c9 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -88,7 +88,7 @@
<string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"配對完成後,所配對的裝置即可在連線後存取你的聯絡人和通話紀錄。"</string>
<string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對。"</string>
- <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 或密碼金鑰不正確。"</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 碼或密碼金鑰不正確。"</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 通訊。"</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」拒絕配對要求。"</string>
<string name="accessibility_wifi_off" msgid="1166761729660614716">"已關閉 Wi-Fi。"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index b21f2fa..5767823 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -35,7 +35,7 @@
private static String sServicesSystemSharedLibPackageName;
private static String sSharedSystemSharedLibPackageName;
- public static final int[] WIFI_PIE_FOR_BADGING = {
+ static final int[] WIFI_PIE_FOR_BADGING = {
com.android.internal.R.drawable.ic_signal_wifi_badged_0_bars,
com.android.internal.R.drawable.ic_signal_wifi_badged_1_bar,
com.android.internal.R.drawable.ic_signal_wifi_badged_2_bars,
@@ -294,12 +294,7 @@
});
}
- /**
- * Returns the resource id for the given badge or {@link View.NO_ID} if no badge is to be shown.
- *
- * @throws IllegalArgumentException if the given badge value is not supported.
- */
- public static int getWifiBadgeResource(int badge) {
+ private static int getWifiBadgeResource(int badge) {
switch (badge) {
case NetworkBadging.BADGING_NONE:
return View.NO_ID;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index f52a6b5..04a3d1f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -165,6 +165,9 @@
* Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
* custom view which should be displayed for the preference. The custom view will be inflated
* as a remote view.
+ *
+ * This also can be used with {@link META_DATA_PREFERENCE_SUMMARY_URI} above, by setting the id
+ * of the summary TextView to '@android:id/summary'.
*/
public static final String META_DATA_PREFERENCE_CUSTOM_VIEW =
"com.android.settings.custom_view";
@@ -315,6 +318,7 @@
PackageManager pm = context.getPackageManager();
List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
PackageManager.GET_META_DATA, user.getIdentifier());
+ Map<String, IContentProvider> providerMap = new HashMap<>();
for (ResolveInfo resolved : results) {
if (!resolved.system) {
// Do not allow any app to add to settings, only system ones.
@@ -346,7 +350,7 @@
tile.priority = usePriority ? resolved.priority : 0;
tile.metaData = activityInfo.metaData;
updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
- pm);
+ pm, providerMap);
if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
addedCache.put(key, tile);
@@ -361,14 +365,14 @@
}
private static boolean updateTileData(Context context, Tile tile,
- ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm) {
+ ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm,
+ Map<String, IContentProvider> providerMap) {
if (applicationInfo.isSystemApp()) {
int icon = 0;
Pair<String, Integer> iconFromUri = null;
CharSequence title = null;
String summary = null;
String keyHint = null;
- Uri uri = null;
RemoteViews remoteViews = null;
// Get the activity's meta-data
@@ -414,6 +418,15 @@
if (metaData.containsKey(META_DATA_PREFERENCE_CUSTOM_VIEW)) {
int layoutId = metaData.getInt(META_DATA_PREFERENCE_CUSTOM_VIEW);
remoteViews = new RemoteViews(applicationInfo.packageName, layoutId);
+ if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {
+ String uriString = metaData.getString(
+ META_DATA_PREFERENCE_SUMMARY_URI);
+ String overrideSummary = getTextFromUri(context, uriString, providerMap,
+ META_DATA_PREFERENCE_SUMMARY);
+ if (overrideSummary != null) {
+ remoteViews.setTextViewText(android.R.id.summary, overrideSummary);
+ }
+ }
}
}
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 0f9b2ff..edb3226 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -540,7 +540,10 @@
}
}
- mSeen = seen;
+ // Only replace the previous value if we have a recent scan result to use
+ if (seen != 0) {
+ mSeen = seen;
+ }
}
/**
@@ -984,8 +987,10 @@
security = getSecurity(result);
if (security == SECURITY_PSK)
pskType = getPskType(result);
- mRssi = result.level;
- mSeen = result.timestamp;
+
+ mScanResultCache.put(result.BSSID, result);
+ updateRssi();
+ mSeen = result.timestamp; // even if the timestamp is old it is still valid
}
public void saveWifiState(Bundle savedState) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index ec94841..0d67ad0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -12,17 +12,13 @@
import android.content.Intent;
import android.net.NetworkInfo;
-import android.net.NetworkKey;
-import android.net.WifiKey;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.util.Log;
import java.util.List;
public class WifiStatusTracker {
- private static final String TAG = "WifiStatusTracker";
private final WifiManager mWifiManager;
public boolean enabled;
@@ -32,7 +28,6 @@
public String ssid;
public int rssi;
public int level;
- public NetworkKey networkKey;
public WifiStatusTracker(WifiManager wifiManager) {
mWifiManager = wifiManager;
@@ -54,32 +49,19 @@
connecting = networkInfo != null && !networkInfo.isConnected()
&& networkInfo.isConnectedOrConnecting();
connected = networkInfo != null && networkInfo.isConnected();
- WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
- ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
- : mWifiManager.getConnectionInfo();
-
// If Connected grab the signal strength and ssid.
- if (connected && info != null) {
- ssid = getSsid(info);
- String bssid = info.getBSSID();
- if ((ssid != null) && (bssid != null)) {
- // Reuse existing network key object if possible.
- if ((networkKey == null)
- || !networkKey.wifiKey.ssid.equals(ssid)
- || !networkKey.wifiKey.bssid.equals(bssid)) {
- try {
- networkKey = new NetworkKey(
- new WifiKey(ssid, bssid));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Cannot create NetworkKey", e);
- }
- }
+ if (connected) {
+ // try getting it out of the intent first
+ WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
+ ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
+ : mWifiManager.getConnectionInfo();
+ if (info != null) {
+ ssid = getSsid(info);
} else {
- networkKey = null;
+ ssid = null;
}
- } else {
+ } else if (!connected) {
ssid = null;
- networkKey = null;
}
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
// Default to -200 as its below WifiManager.MIN_RSSI.
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index a2e0e2c..7cfb32d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -36,6 +36,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Pair;
+import android.widget.RemoteViews;
import com.android.settingslib.R;
import com.android.settingslib.TestConfig;
@@ -51,6 +52,7 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.internal.ShadowExtractor;
import java.util.ArrayList;
import java.util.Collections;
@@ -68,9 +70,13 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
@RunWith(RobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH,
+ sdk = TestConfig.SDK_VERSION,
+ shadows = {TileUtilsTest.TileUtilsShadowRemoteViews.class})
public class TileUtilsTest {
@Mock
@@ -364,6 +370,86 @@
assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
}
+ @Test
+ public void getTilesForIntent_summaryUriSpecified_shouldOverrideRemoteViewSummary()
+ throws RemoteException {
+ Intent intent = new Intent();
+ Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
+ List<Tile> outTiles = new ArrayList<>();
+ List<ResolveInfo> info = new ArrayList<>();
+ ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
+ null, URI_GET_SUMMARY);
+ resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
+ R.layout.user_preference);
+ info.add(resolveInfo);
+
+ when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
+ .thenReturn(info);
+
+ // Mock the content provider interaction.
+ Bundle bundle = new Bundle();
+ bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
+ when(mIContentProvider.call(anyString(),
+ eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
+ any())).thenReturn(bundle);
+ when(mContentResolver.acquireUnstableProvider(anyString()))
+ .thenReturn(mIContentProvider);
+ when(mContentResolver.acquireUnstableProvider(any(Uri.class)))
+ .thenReturn(mIContentProvider);
+
+ TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
+ null /* defaultCategory */, outTiles, false /* usePriority */,
+ false /* checkCategory */);
+
+ assertThat(outTiles.size()).isEqualTo(1);
+ Tile tile = outTiles.get(0);
+ assertThat(tile.remoteViews).isNotNull();
+ assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
+ // Make sure the summary TextView got a new text string.
+ TileUtilsShadowRemoteViews shadowRemoteViews =
+ (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews);
+ assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary);
+ assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text");
+ }
+
+ @Test
+ public void getTilesForIntent_providerUnavailable_shouldNotOverrideRemoteViewSummary()
+ throws RemoteException {
+ Intent intent = new Intent();
+ Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
+ List<Tile> outTiles = new ArrayList<>();
+ List<ResolveInfo> info = new ArrayList<>();
+ ResolveInfo resolveInfo = newInfo(true, null /* category */, null,
+ null, URI_GET_SUMMARY);
+ resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
+ R.layout.user_preference);
+ info.add(resolveInfo);
+
+ when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
+ .thenReturn(info);
+
+ // Mock the content provider interaction.
+ Bundle bundle = new Bundle();
+ bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
+ when(mIContentProvider.call(anyString(),
+ eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
+ any())).thenReturn(bundle);
+
+ TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
+ null /* defaultCategory */, outTiles, false /* usePriority */,
+ false /* checkCategory */);
+
+ assertThat(outTiles.size()).isEqualTo(1);
+ Tile tile = outTiles.get(0);
+ assertThat(tile.remoteViews).isNotNull();
+ assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
+ // Make sure the summary TextView didn't get any text view updates.
+ TileUtilsShadowRemoteViews shadowRemoteViews =
+ (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews);
+ assertThat(shadowRemoteViews.overrideViewId).isNull();
+ assertThat(shadowRemoteViews.overrideText).isNull();
+ }
+
public static ResolveInfo newInfo(boolean systemApp, String category) {
return newInfo(systemApp, category, null);
}
@@ -423,4 +509,17 @@
info.activityInfo.metaData.putString(key, value);
}
}
+
+ @Implements(RemoteViews.class)
+ public static class TileUtilsShadowRemoteViews {
+
+ private Integer overrideViewId;
+ private CharSequence overrideText;
+
+ @Implementation
+ public void setTextViewText(int viewId, CharSequence text) {
+ overrideViewId = viewId;
+ overrideText = text;
+ }
+ }
}
diff --git a/packages/SystemUI/colorextraction/src/com/google/android/colorextraction/types/Tonal.java b/packages/SystemUI/colorextraction/src/com/google/android/colorextraction/types/Tonal.java
index 5b4b3ed..81bc831 100644
--- a/packages/SystemUI/colorextraction/src/com/google/android/colorextraction/types/Tonal.java
+++ b/packages/SystemUI/colorextraction/src/com/google/android/colorextraction/types/Tonal.java
@@ -29,6 +29,8 @@
import com.google.android.colorextraction.ColorExtractor.GradientColors;
+import java.util.List;
+
/**
* Implementation of tonal color extraction
*/
@@ -40,9 +42,6 @@
private static final float FIT_WEIGHT_S = 1.0f;
private static final float FIT_WEIGHT_L = 10.0f;
- // When extracting the main color, only consider colors
- // present in at least MIN_COLOR_OCCURRENCE of the image
- private static final float MIN_COLOR_OCCURRENCE = 0.1f;
private static final boolean DEBUG = true;
// Temporary variable to avoid allocations
@@ -61,7 +60,10 @@
@NonNull GradientColors outColorsNormal, @NonNull GradientColors outColorsDark,
@NonNull GradientColors outColorsExtraDark) {
- if (inWallpaperColors.getColors().size() == 0) {
+ final List<Color> mainColors = inWallpaperColors.getMainColors();
+ final int mainColorsSize = mainColors.size();
+
+ if (mainColorsSize == 0) {
return false;
}
// Tonal is not really a sort, it takes a color from the extracted
@@ -69,30 +71,18 @@
// palettes. The best fit is tweaked to be closer to the source color
// and replaces the original palette
- // First find the most representative color in the image
- populationSort(inWallpaperColors);
- // Calculate total
- int total = 0;
- for (Pair<Color, Integer> weightedColor : inWallpaperColors.getColors()) {
- total += weightedColor.second;
- }
-
- // Get bright colors that occur often enough in this image
- Pair<Color, Integer> bestColor = null;
- float[] hsl = new float[3];
- for (Pair<Color, Integer> weightedColor : inWallpaperColors.getColors()) {
- float colorOccurrence = weightedColor.second / (float) total;
- if (colorOccurrence < MIN_COLOR_OCCURRENCE) {
- break;
- }
-
- int colorValue = weightedColor.first.toArgb();
+ // Get the most preeminent, non-blacklisted color.
+ Color bestColor = null;
+ final float[] hsl = new float[3];
+ for (int i = 0; i < mainColorsSize; i++) {
+ final Color color = mainColors.get(i);
+ final int colorValue = color.toArgb();
ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue),
Color.blue(colorValue), hsl);
// Stop when we find a color that meets our criteria
if (!isBlacklisted(hsl)) {
- bestColor = weightedColor;
+ bestColor = color;
break;
}
}
@@ -102,7 +92,7 @@
return false;
}
- int colorValue = bestColor.first.toArgb();
+ int colorValue = bestColor.toArgb();
ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue), Color.blue(colorValue),
hsl);
@@ -212,10 +202,6 @@
return false;
}
- private static void populationSort(@NonNull WallpaperColors wallpaperColors) {
- wallpaperColors.getColors().sort((a, b) -> b.second - a.second);
- }
-
/**
* Offsets all colors by a delta, clamping values that go beyond what's
* supported on the color space.
@@ -269,7 +255,9 @@
TonalPalette best = null;
float error = Float.POSITIVE_INFINITY;
- for (TonalPalette candidate : TONAL_PALETTES) {
+ for (int i = 0; i < TONAL_PALETTES.length; i++) {
+ final TonalPalette candidate = TONAL_PALETTES[i];
+
if (h >= candidate.minHue && h <= candidate.maxHue) {
best = candidate;
break;
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 9a97162..501d0a5 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -53,7 +53,7 @@
android:gravity="center"
android:layout_centerHorizontal="true"
android:layout_marginRight="72dp"
- androidprv:scaledTextSize="28"
+ androidprv:scaledTextSize="@integer/scaled_password_text_size"
android:textColor="?attr/bgProtectTextColor"
android:contentDescription="@string/keyguard_accessibility_pin_area"
/>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index f0dec76..c4732e4 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -66,7 +66,7 @@
android:gravity="center"
android:layout_centerHorizontal="true"
android:layout_marginRight="72dp"
- androidprv:scaledTextSize="28"
+ androidprv:scaledTextSize="@integer/scaled_password_text_size"
android:textColor="?attr/bgProtectTextColor"
android:contentDescription="@string/keyguard_accessibility_sim_pin_area"
/>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 119b3ee..1c7defd 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -67,7 +67,7 @@
android:gravity="center"
android:layout_centerHorizontal="true"
android:layout_marginRight="72dp"
- androidprv:scaledTextSize="28"
+ androidprv:scaledTextSize="@integer/scaled_password_text_size"
android:textColor="?attr/bgProtectTextColor"
android:contentDescription="@string/keyguard_accessibility_sim_puk_area"
/>
diff --git a/packages/SystemUI/res-keyguard/values/attrs.xml b/packages/SystemUI/res-keyguard/values/attrs.xml
index d3d60a1..802bd30 100644
--- a/packages/SystemUI/res-keyguard/values/attrs.xml
+++ b/packages/SystemUI/res-keyguard/values/attrs.xml
@@ -41,7 +41,4 @@
<declare-styleable name="CarrierText">
<attr name="allCaps" format="boolean" />
</declare-styleable>
-
- <attr name="pinDividerColor" format="color" />
- <attr name="pinDeleteColor" format="color" />
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 41c723e..a721dd0 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -48,6 +48,8 @@
<!-- The size of the dots in the PIN unlock method. -->
<dimen name="password_dot_size">9dp</dimen>
+ <!-- The size of PIN text in the PIN unlock method. -->
+ <integer name="scaled_password_text_size">40</integer>
<!-- The padding between chars of the password view. -->
<dimen name="password_char_padding">8dp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index d7ff349..0c96b0b 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -32,8 +32,8 @@
<item name="android:singleLine">true</item>
<item name="android:gravity">center_horizontal|center_vertical</item>
<item name="android:background">@null</item>
- <item name="android:textSize">36sp</item>
- <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:textSize">32sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
<item name="android:textColor">?attr/bgProtectTextColor</item>
<item name="android:paddingBottom">-16dp</item>
</style>
@@ -53,7 +53,7 @@
</style>
<style name="widget_big_thin">
<item name="android:textSize">@dimen/widget_big_font_size</item>
- <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
</style>
<style name="BouncerSecurityContainer">
diff --git a/packages/SystemUI/res/layout/navigation_bar_window.xml b/packages/SystemUI/res/layout/navigation_bar_window.xml
index 051bf3a..6fa46d4 100644
--- a/packages/SystemUI/res/layout/navigation_bar_window.xml
+++ b/packages/SystemUI/res/layout/navigation_bar_window.xml
@@ -16,11 +16,11 @@
** limitations under the License.
*/
-->
-<FrameLayout
+<com.android.systemui.statusbar.phone.NavigationBarFrame
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation_bar_frame"
android:layout_height="match_parent"
android:layout_width="match_parent">
-</FrameLayout>
+</com.android.systemui.statusbar.phone.NavigationBarFrame>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index c3a9eeb..3974454 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -555,7 +555,7 @@
<string name="notification_channel_disabled" msgid="2139193533791840539">"Aurrerantzean ez duzu jasoko horrelako jakinarazpenik"</string>
<string name="notification_num_channels" msgid="2048144408999179471">"Jakinarazpenen <xliff:g id="NUMBER">%d</xliff:g> kategoria"</string>
<string name="notification_default_channel_desc" msgid="2506053815870808359">"Aplikazio honek ez du jakinarazpen-kategoriarik"</string>
- <string name="notification_unblockable_desc" msgid="3561016061737896906">"Aplikazio honen jakinarazpenak ezin dira desaktibatu"</string>
+ <string name="notification_unblockable_desc" msgid="3561016061737896906">"Ezin dira desaktibatu aplikazio honen jakinarazpenak"</string>
<plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
<item quantity="other">Aplikazio honen 1/<xliff:g id="NUMBER_1">%d</xliff:g> jakinarazpen-kategoria</item>
<item quantity="one">Aplikazio honen 1/<xliff:g id="NUMBER_0">%d</xliff:g> jakinarazpen-kategoria</item>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f942be6..5274b64 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -295,8 +295,6 @@
<item name="darkIconTheme">@style/DualToneDarkTheme</item>
<item name="bgProtectTextColor">?android:attr/textColorPrimaryInverse</item>
<item name="bgProtectSecondaryTextColor">?android:attr/textColorSecondaryInverse</item>
- <item name="pinDividerColor">@color/pin_divider_color</item>
- <item name="pinDeleteColor">@color/pin_delete_color</item>
<item name="*android:lockPatternStyle">@style/LockPatternStyle</item>
</style>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
index 38811ac..ce3068d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
@@ -16,13 +16,19 @@
package com.android.keyguard;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionInfo;
import android.telephony.euicc.EuiccManager;
+import android.util.Log;
import java.lang.ref.WeakReference;
@@ -31,8 +37,26 @@
* the device with no cellular service.
*/
class KeyguardEsimArea extends Button implements View.OnClickListener {
+ private static final String ACTION_DISABLE_ESIM = "com.android.keyguard.disable_esim";
+ private static final String TAG = "KeyguardEsimArea";
+ private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+
private EuiccManager mEuiccManager;
+ private BroadcastReceiver mReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_DISABLE_ESIM.equals(intent.getAction())) {
+ int resultCode = getResultCode();
+ if (resultCode != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
+ // TODO (b/62680294): Surface more info. to the end users for this failure.
+ Log.e(TAG, "Error disabling esim, result code = " + resultCode);
+ }
+ }
+ }
+ };
+
public KeyguardEsimArea(Context context) {
this(context, null);
}
@@ -52,8 +76,10 @@
}
@Override
- public void onClick(View v) {
- // STOPSHIP(b/37353596): use EuiccManager API to disable current carrier.
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_DISABLE_ESIM),
+ PERMISSION_SELF, null /* scheduler */);
}
public static boolean isEsimLocked(Context context, int subId) {
@@ -66,4 +92,23 @@
return sub != null && sub.isEmbedded();
}
+ @Override
+ protected void onDetachedFromWindow() {
+ mContext.unregisterReceiver(mReceiver);
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(mContext, KeyguardEsimArea.class);
+ intent.setAction(ACTION_DISABLE_ESIM);
+ intent.setPackage(mContext.getPackageName());
+ PendingIntent callbackIntent = PendingIntent.getBroadcast(
+ mContext,
+ 0 /* requestCode */,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ mEuiccManager
+ .switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, callbackIntent);
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 7e81173..d8bebab 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -135,7 +135,9 @@
}
mDrawPaint.setFlags(Paint.SUBPIXEL_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
mDrawPaint.setTextAlign(Paint.Align.CENTER);
- mDrawPaint.setTypeface(Typeface.create("sans-serif-light", 0));
+ mDrawPaint.setTypeface(Typeface.create(
+ context.getString(com.android.internal.R.string.config_headlineFontFamilyLight),
+ 0));
mShowPassword = Settings.System.getInt(mContext.getContentResolver(),
Settings.System.TEXT_SHOW_PASSWORD, 1) == 1;
mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 6476416..c4de63b 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -235,6 +235,7 @@
scaledLayoutParams.setMargins(0, 0, 0, marginBottom);
mBatteryIconView.setLayoutParams(scaledLayoutParams);
+ FontSizeUtils.updateFontSize(mBatteryPercentView, R.dimen.qs_time_expanded_size);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
index c4740af..b35efb2 100644
--- a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
+++ b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
@@ -118,7 +118,8 @@
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
+ | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MAGNIFICATION_REGION_EFFECT;
lp.setTitle("RoundedOverlay");
lp.gravity = Gravity.TOP;
return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 2d0fe6f..4d0e60d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -32,6 +32,7 @@
import com.android.systemui.R;
import com.android.systemui.plugins.qs.*;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
@@ -44,6 +45,7 @@
private String mAccessibilityClass;
private boolean mTileState;
private boolean mCollapsedView;
+ private boolean mClicked;
public QSTileBaseView(Context context, QSIconView icon) {
this(context, icon, false);
@@ -153,7 +155,11 @@
setContentDescription(state.contentDescription);
mAccessibilityClass = state.expandedAccessibilityClassName;
if (state instanceof QSTile.BooleanState) {
- mTileState = ((QSTile.BooleanState) state).value;
+ boolean newState = ((BooleanState) state).value;
+ if (mTileState != newState) {
+ mClicked = false;
+ mTileState = newState;
+ }
}
}
@@ -173,15 +179,22 @@
}
@Override
+ public boolean performClick() {
+ mClicked = true;
+ return super.performClick();
+ }
+
+ @Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
if (!TextUtils.isEmpty(mAccessibilityClass)) {
event.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
+ boolean b = mClicked ? !mTileState : mTileState;
String label = getResources()
- .getString(!mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+ .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
event.setContentDescription(label);
- event.setChecked(!mTileState);
+ event.setChecked(b);
}
}
}
@@ -192,10 +205,11 @@
if (!TextUtils.isEmpty(mAccessibilityClass)) {
info.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
+ boolean b = mClicked ? !mTileState : mTileState;
String label = getResources()
- .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+ .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
info.setText(label);
- info.setChecked(mTileState);
+ info.setChecked(b);
info.setCheckable(true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index cb9093a..7391509 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -32,17 +32,16 @@
import com.android.settingslib.wifi.AccessPoint;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.R.string;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.qs.QSDetailItems;
-import com.android.systemui.qs.QSDetailItems.Item;
-import com.android.systemui.qs.QSHost;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.QSDetailItems;
+import com.android.systemui.qs.QSDetailItems.Item;
+import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.SignalTileView;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -188,7 +187,7 @@
minimalContentDescription.append(removeDoubleQuotes(cb.enabledDesc));
}
}
- state.contentDescription = minimalContentDescription;
+ state.contentDescription = minimalContentDescription.toString();
state.dualLabelContentDescription = r.getString(
R.string.accessibility_quick_settings_open_settings, getTileLabel());
state.expandedAccessibilityClassName = Switch.class.getName();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 86b1d3b..4de1214 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -72,6 +72,7 @@
import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent;
import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
+import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
@@ -420,8 +421,7 @@
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(this, loadPlan, loadOpts);
TaskStack stack = loadPlan.getTaskStack();
- mRecentsView.onReload(mIsVisible, stack.getTaskCount() == 0);
- mRecentsView.updateStack(stack, true /* setStackViewTasks */);
+ mRecentsView.onReload(stack, mIsVisible);
// Update the nav bar scrim, but defer the animation until the enter-window event
boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
@@ -755,6 +755,10 @@
ssp.removeTask(event.task.key.id);
}
+ public final void onBusEvent(TaskViewDismissedEvent event) {
+ mRecentsView.updateScrimOpacity();
+ }
+
public final void onBusEvent(AllTaskViewsDismissedEvent event) {
SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.hasDockedTask()) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index d710244..dca4a8d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -31,6 +31,7 @@
import android.graphics.drawable.Drawable;
import android.util.ArraySet;
import android.util.AttributeSet;
+import android.util.MathUtils;
import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -79,6 +80,7 @@
import com.android.systemui.recents.views.RecentsTransitionHelper.AppTransitionAnimationSpecsFuture;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.google.android.colorextraction.ColorExtractor;
import com.google.android.colorextraction.drawable.GradientDrawable;
@@ -96,11 +98,12 @@
private static final String TAG = "RecentsView";
private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
- private static final float DEFAULT_SCRIM_ALPHA = 0.8f;
private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134;
private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100;
+ private static final int BUSY_RECENTS_TASK_COUNT = 3;
+
private TaskStackView mTaskStackView;
private TextView mStackActionButton;
private TextView mEmptyView;
@@ -112,7 +115,7 @@
Rect mSystemInsets = new Rect();
private int mDividerSize;
- private final float mScrimAlpha;
+ private float mBusynessFactor;
private GradientDrawable mBackgroundScrim;
private final SysuiColorExtractor mColorExtractor;
private Animator mBackgroundScrimAnimator;
@@ -143,10 +146,8 @@
mDividerSize = ssp.getDockedDividerSize(context);
mTouchHandler = new RecentsViewTouchHandler(this);
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
- mScrimAlpha = DEFAULT_SCRIM_ALPHA;
mBackgroundScrim = new GradientDrawable(context);
mBackgroundScrim.setCallback(this);
- mBackgroundScrim.setAlpha((int) (mScrimAlpha * 255));
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
LayoutInflater inflater = LayoutInflater.from(context);
@@ -168,9 +169,10 @@
/**
* Called from RecentsActivity when it is relaunched.
*/
- public void onReload(boolean isResumingFromVisible, boolean isTaskStackEmpty) {
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
+ public void onReload(TaskStack stack, boolean isResumingFromVisible) {
+ final RecentsConfiguration config = Recents.getConfiguration();
+ final RecentsActivityLaunchState launchState = config.getLaunchState();
+ final boolean isTaskStackEmpty = stack.getTaskCount() == 0;
if (mTaskStackView == null) {
isResumingFromVisible = false;
@@ -185,17 +187,19 @@
// Update the stack
mTaskStackView.onReload(isResumingFromVisible);
+ updateStack(stack, true /* setStackViewTasks */);
+ updateBusyness();
if (isResumingFromVisible) {
// If we are already visible, then restore the background scrim
- animateBackgroundScrim(1f, DEFAULT_UPDATE_SCRIM_DURATION);
+ animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
} else {
// If we are already occluded by the app, then set the final background scrim alpha now.
// Otherwise, defer until the enter animation completes to animate the scrim alpha with
// the tasks for the home animation.
if (launchState.launchedViaDockGesture || launchState.launchedFromApp
|| isTaskStackEmpty) {
- mBackgroundScrim.setAlpha((int) (mScrimAlpha * 255));
+ mBackgroundScrim.setAlpha((int) (getOpaqueScrimAlpha() * 255));
} else {
mBackgroundScrim.setAlpha(0);
}
@@ -219,13 +223,40 @@
}
/**
+ * Animates the scrim opacity based on how many tasks are visible.
+ * Called from {@link RecentsActivity} when tasks are dismissed.
+ */
+ public void updateScrimOpacity() {
+ if (updateBusyness()) {
+ animateBackgroundScrim(getOpaqueScrimAlpha(), DEFAULT_UPDATE_SCRIM_DURATION);
+ }
+ }
+
+ /**
+ * Updates the busyness factor.
+ *
+ * @return True if it changed.
+ */
+ private boolean updateBusyness() {
+ final int taskCount = mTaskStackView.getStack().getStackTaskCount();
+ final float busyness = Math.min(taskCount, BUSY_RECENTS_TASK_COUNT)
+ / (float) BUSY_RECENTS_TASK_COUNT;
+ if (mBusynessFactor == busyness) {
+ return false;
+ } else {
+ mBusynessFactor = busyness;
+ return true;
+ }
+ }
+
+ /**
* Returns the current TaskStack.
*/
public TaskStack getStack() {
return mTaskStackView.getStack();
}
- /*
+ /**
* Returns the window background scrim.
*/
public Drawable getBackgroundScrim() {
@@ -619,7 +650,7 @@
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp
&& getStack().getTaskCount() > 0) {
- animateBackgroundScrim(1f,
+ animateBackgroundScrim(getOpaqueScrimAlpha(),
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
}
}
@@ -779,13 +810,25 @@
}
/**
+ * Scrim alpha based on how busy recents is:
+ * Scrim will be {@link ScrimController#GRADIENT_SCRIM_ALPHA} when the stack is empty,
+ * and {@link ScrimController#GRADIENT_SCRIM_ALPHA_BUSY} when it's full.
+ *
+ * @return Alpha from 0 to 1.
+ */
+ private float getOpaqueScrimAlpha() {
+ return MathUtils.map(0, 1, ScrimController.GRADIENT_SCRIM_ALPHA,
+ ScrimController.GRADIENT_SCRIM_ALPHA_BUSY, mBusynessFactor);
+ }
+
+ /**
* Animates the background scrim to the given {@param alpha}.
*/
private void animateBackgroundScrim(float alpha, int duration) {
Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator);
// Calculate the absolute alpha to animate from
- int fromAlpha = mBackgroundScrim.getAlpha();
- int toAlpha = (int) (alpha * mScrimAlpha * 255);
+ final int fromAlpha = mBackgroundScrim.getAlpha();
+ final int toAlpha = (int) (alpha * 255);
mBackgroundScrimAnimator = ObjectAnimator.ofInt(mBackgroundScrim, Utilities.DRAWABLE_ALPHA,
fromAlpha, toAlpha);
mBackgroundScrimAnimator.setDuration(duration);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index a7b845e..a2409d1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -50,6 +50,7 @@
import android.os.UserHandle;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
+import android.util.Slog;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -100,6 +101,7 @@
* An AsyncTask that saves an image to the media store in the background.
*/
class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
+ private static final String TAG = "SaveImageInBackgroundTask";
private static final String SCREENSHOTS_DIR_NAME = "Screenshots";
private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
@@ -303,6 +305,7 @@
} catch (Exception e) {
// IOException/UnsupportedOperationException may be thrown if external storage is not
// mounted
+ Slog.e(TAG, "unable to save screenshot", e);
mParams.clearImage();
mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
}
@@ -379,8 +382,6 @@
* An AsyncTask that deletes an image from the media store in the background.
*/
class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
- private static final String TAG = "DeleteImageInBackgroundTask";
-
private Context mContext;
DeleteImageInBackgroundTask(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 169019f..25f3e25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -16,19 +16,15 @@
package com.android.systemui.statusbar;
-import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.net.NetworkBadging;
import android.telephony.SubscriptionInfo;
import android.util.ArraySet;
import android.util.AttributeSet;
@@ -80,10 +76,8 @@
private boolean mEthernetVisible = false;
private int mEthernetIconId = 0;
private int mLastEthernetIconId = -1;
- private int mWifiBadgeId = -1;
private boolean mWifiVisible = false;
private int mWifiStrengthId = 0;
- private int mLastWifiBadgeId = -1;
private int mLastWifiStrengthId = -1;
private boolean mWifiIn;
private boolean mWifiOut;
@@ -291,7 +285,6 @@
boolean activityIn, boolean activityOut, String description, boolean isTransient) {
mWifiVisible = statusIcon.visible && !mBlockWifi;
mWifiStrengthId = statusIcon.icon;
- mWifiBadgeId = statusIcon.iconOverlay;
mWifiDescription = statusIcon.contentDescription;
mWifiIn = activityIn && mActivityEnabled && mWifiVisible;
mWifiOut = activityOut && mActivityEnabled && mWifiVisible;
@@ -428,7 +421,6 @@
mWifi.setImageDrawable(null);
mWifiDark.setImageDrawable(null);
mLastWifiStrengthId = -1;
- mLastWifiBadgeId = -1;
}
for (PhoneState state : mPhoneStates) {
@@ -484,16 +476,10 @@
(mEthernetVisible ? "VISIBLE" : "GONE")));
if (mWifiVisible) {
- if (mWifiStrengthId != mLastWifiStrengthId || mWifiBadgeId != mLastWifiBadgeId) {
- if (mWifiBadgeId == -1) {
- setIconForView(mWifi, mWifiStrengthId);
- setIconForView(mWifiDark, mWifiStrengthId);
- } else {
- setBadgedWifiIconForView(mWifi, mWifiStrengthId, mWifiBadgeId);
- setBadgedWifiIconForView(mWifiDark, mWifiStrengthId, mWifiBadgeId);
- }
+ if (mWifiStrengthId != mLastWifiStrengthId) {
+ setIconForView(mWifi, mWifiStrengthId);
+ setIconForView(mWifiDark, mWifiStrengthId);
mLastWifiStrengthId = mWifiStrengthId;
- mLastWifiBadgeId = mWifiBadgeId;
}
mWifiGroup.setContentDescription(mWifiDescription);
mWifiGroup.setVisibility(View.VISIBLE);
@@ -558,10 +544,6 @@
// Using the imageView's context to retrieve the Drawable so that theme is preserved.
Drawable icon = imageView.getContext().getDrawable(iconId);
- setScaledIcon(imageView, icon);
- }
-
- private void setScaledIcon(ImageView imageView, Drawable icon) {
if (mIconScaleFactor == 1.f) {
imageView.setImageDrawable(icon);
} else {
@@ -569,33 +551,6 @@
}
}
- /**
- * Creates and sets a LayerDrawable from the given ids on the given view.
- *
- * <p>This method will also scale the icon by {@link #mIconScaleFactor} if appropriate.
- */
- private void setBadgedWifiIconForView(ImageView imageView, @DrawableRes int wifiPieId,
- @DrawableRes int badgeId) {
- // TODO(sghuman): Delete this method and revert to N badging logic
- // Using the imageView's context to retrieve the Drawable so that theme is preserved.;
- LayerDrawable icon = new LayerDrawable(new Drawable[] {
- imageView.getContext().getDrawable(wifiPieId),
- imageView.getContext().getDrawable(NetworkBadging.BADGING_NONE)});
-
- // The LayerDrawable shares an underlying state so we must mutate the object to change the
- // color between the light and dark themes.
- icon.mutate().setTint(getColorAttr(imageView.getContext(), R.attr.singleToneColor));
-
- setScaledIcon(imageView, icon);
- }
-
- /** Returns the given color attribute value, or white if not defined. */
- @ColorInt private static int getColorAttr(Context context, int attr) {
- TypedArray ta = context.obtainStyledAttributes(new int[] {attr});
- @ColorInt int colorAccent = ta.getColor(0, Color.WHITE);
- ta.recycle();
- return colorAccent;
- }
@Override
public void onDarkChanged(Rect tintArea, float darkIntensity, int tint) {
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 7eaa290..bf926c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -48,6 +48,7 @@
*/
public class NotificationInflater {
+ public static final String TAG = "NotificationInflater";
@VisibleForTesting
static final int FLAG_REINFLATE_ALL = ~0;
private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0;
@@ -315,7 +316,8 @@
return cancellationSignal;
}
- private static void applyRemoteView(final InflationProgress result,
+ @VisibleForTesting
+ static void applyRemoteView(final InflationProgress result,
final int reInflateFlags, int inflationId,
final ExpandableNotificationRow row,
final boolean redactAmbient, boolean isNewView,
@@ -325,6 +327,7 @@
NotificationViewWrapper existingWrapper,
final HashMap<Integer, CancellationSignal> runningInflations,
ApplyCallback applyCallback) {
+ RemoteViews newContentView = applyCallback.getRemoteView();
RemoteViews.OnViewAppliedListener listener
= new RemoteViews.OnViewAppliedListener() {
@@ -343,12 +346,31 @@
@Override
public void onError(Exception e) {
- runningInflations.remove(inflationId);
- handleInflationError(runningInflations, e, entry.notification, callback);
+ // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this could
+ // actually also be a system issue, so let's try on the UI thread again to be safe.
+ try {
+ View newView = existingView;
+ if (isNewView) {
+ newView = newContentView.apply(
+ result.packageContext,
+ parentLayout,
+ remoteViewClickHandler);
+ } else {
+ newContentView.reapply(
+ result.packageContext,
+ existingView,
+ remoteViewClickHandler);
+ }
+ Log.wtf(TAG, "Async Inflation failed but normal inflation finished normally.",
+ e);
+ onViewApplied(newView);
+ } catch (Exception anotherException) {
+ runningInflations.remove(inflationId);
+ handleInflationError(runningInflations, e, entry.notification, callback);
+ }
}
};
CancellationSignal cancellationSignal;
- RemoteViews newContentView = applyCallback.getRemoteView();
if (isNewView) {
cancellationSignal = newContentView.applyAsync(
result.packageContext,
@@ -620,14 +642,16 @@
}
}
- private static class InflationProgress {
+ @VisibleForTesting
+ static class InflationProgress {
private RemoteViews newContentView;
private RemoteViews newHeadsUpView;
private RemoteViews newExpandedView;
private RemoteViews newAmbientView;
private RemoteViews newPublicView;
- private Context packageContext;
+ @VisibleForTesting
+ Context packageContext;
private View inflatedContentView;
private View inflatedHeadsUpView;
@@ -636,7 +660,8 @@
private View inflatedPublicView;
}
- private abstract static class ApplyCallback {
+ @VisibleForTesting
+ abstract static class ApplyCallback {
public abstract void setResultView(View v);
public abstract RemoteViews getRemoteView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java
new file mode 100644
index 0000000..741f783
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static android.view.MotionEvent.ACTION_OUTSIDE;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+import com.android.systemui.statusbar.policy.DeadZone;
+
+public class NavigationBarFrame extends FrameLayout {
+
+ private DeadZone mDeadZone = null;
+
+ public NavigationBarFrame(@NonNull Context context) {
+ super(context);
+ }
+
+ public NavigationBarFrame(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public NavigationBarFrame(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public void setDeadZone(@NonNull DeadZone deadZone) {
+ mDeadZone = deadZone;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (event.getAction() == ACTION_OUTSIDE) {
+ if (mDeadZone != null) {
+ return mDeadZone.onTouchEvent(event);
+ }
+ }
+ return super.dispatchTouchEvent(event);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index cb3222d6d..a6cd472 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -249,9 +249,6 @@
if (mGestureHelper.onTouchEvent(event)) {
return true;
}
- if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- mDeadZone.poke(event);
- }
return super.onTouchEvent(event);
}
@@ -612,9 +609,8 @@
public void reorient() {
updateCurrentView();
- getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
-
mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
+ ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
mDeadZone.setDisplayRotation(mCurrentRotation);
// force the low profile & disabled states into compliance
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index bc278e0..9275358 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -58,7 +58,7 @@
public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED
= new PathInterpolator(0.3f, 0f, 0.8f, 1f);
// Default alpha value for most scrims, if unsure use this constant
- public static final float GRADIENT_SCRIM_ALPHA = 0.60f;
+ public static final float GRADIENT_SCRIM_ALPHA = 0.45f;
// A scrim varies its opacity based on a busyness factor, for example
// how many notifications are currently visible.
public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.90f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 86d9158..305cf0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -48,6 +48,7 @@
import android.app.RemoteInput;
import android.app.StatusBarManager;
import android.app.TaskStackBuilder;
+import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -103,7 +104,6 @@
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
-import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
@@ -139,6 +139,7 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -213,7 +214,6 @@
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.RowInflaterTask;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -309,6 +309,7 @@
public static final boolean DEBUG_GESTURES = false;
public static final boolean DEBUG_MEDIA = false;
public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
+ public static final boolean DEBUG_CAMERA_LIFT = true; // false once b/62623620 is fixed
public static final boolean DEBUG_WINDOW_STATE = false;
@@ -1300,10 +1301,6 @@
}
public void onOverlayChanged() {
- final boolean usingDarkTheme = isUsingDarkTheme();
- if (DEBUG) {
- Log.d(TAG, "Updating theme because overlay changed. Is theme dark? " + usingDarkTheme);
- }
reevaluateStyles();
// Clock and bottom icons
@@ -2843,6 +2840,17 @@
updateTheme();
}
+ public boolean isUsingDarkText() {
+ OverlayInfo themeInfo = null;
+ try {
+ themeInfo = mOverlayManager.getOverlayInfo("com.android.systemui.theme.lightwallpaper",
+ mCurrentUserId);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ return themeInfo != null && themeInfo.isEnabled();
+ }
+
public boolean isUsingDarkTheme() {
OverlayInfo themeInfo = null;
try {
@@ -4521,30 +4529,49 @@
* Switches theme from light to dark and vice-versa.
*/
private void updateTheme() {
- boolean useDarkTheme;
- // Ignore visibility since we calculate the theme based on the real colors,
- // not the current state.
+
+ int which;
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
- useDarkTheme = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, true /* vis */)
- .supportsDarkText();
+ which = WallpaperManager.FLAG_LOCK;
} else {
- useDarkTheme = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM, true /* vis */)
- .supportsDarkText();
+ which = WallpaperManager.FLAG_SYSTEM;
}
- // Enable/Disable dark overlay
- if (isUsingDarkTheme() != useDarkTheme) {
- if (DEBUG) {
- Log.d(TAG, "Switching theme to: " + (useDarkTheme ? "Dark" : "Light"));
+ // Gradient defines if text color should be light or dark.
+ final boolean useDarkText = mColorExtractor.getColors(which, true /* ignoreVisibility */)
+ .supportsDarkText();
+ // And wallpaper defines if QS should be light or dark.
+ boolean useDarkTheme = false;
+ final WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
+ if (wallpaperManager != null) {
+ WallpaperColors wallpaperColors = wallpaperManager
+ .getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
+ if (wallpaperColors != null) {
+ final int mainColor = wallpaperColors.getPrimaryColor().toArgb();
+ final float[] hsl = new float[3];
+ ColorUtils.colorToHSL(mainColor, hsl);
+ useDarkTheme = hsl[2] < 0.2f;
}
+ }
+
+ // Enable/disable dark UI.
+ if (isUsingDarkTheme() != useDarkTheme) {
try {
mOverlayManager.setEnabled("com.android.systemui.theme.dark",
useDarkTheme, mCurrentUserId);
} catch (RemoteException e) {
Log.w(TAG, "Can't change theme", e);
- return;
}
- mStatusBarWindowManager.setKeyguardDark(useDarkTheme);
+ }
+ // Enable/disable dark text overlay.
+ if (isUsingDarkText() != useDarkText) {
+ try {
+ mOverlayManager.setEnabled("com.android.systemui.theme.lightwallpaper",
+ useDarkText, mCurrentUserId);
+ mStatusBarWindowManager.setKeyguardDark(useDarkText);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't change theme", e);
+ }
}
}
@@ -5163,11 +5190,14 @@
public void onCameraLaunchGestureDetected(int source) {
mLastCameraLaunchSource = source;
if (mStartedGoingToSleep) {
+ if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Finish going to sleep before launching camera");
mLaunchCameraOnFinishedGoingToSleep = true;
return;
}
if (!mNotificationPanel.canCameraGestureBeLaunched(
mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) {
+ if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now, mExpandedVisible: " +
+ mExpandedVisible);
return;
}
if (!mDeviceInteractive) {
@@ -5187,12 +5217,14 @@
mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
+ if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera");
mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
// we will dismiss us too early since we are waiting on an activity to be drawn and
// incorrectly get notified because of the screen on event (which resumes and pauses
// some activities)
+ if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Deferring until screen turns on");
mLaunchCameraOnScreenTurningOn = true;
}
}
@@ -5208,6 +5240,12 @@
mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
|| mFingerprintUnlockController.getMode()
== FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ // When in wake-and-unlock we may not have received a change to mState
+ // but we still should not be dozing, manually set to false.
+ if (mFingerprintUnlockController.getMode() ==
+ FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+ mDozing = false;
+ }
mStatusBarWindowManager.setDozing(mDozing);
mStatusBarKeyguardViewManager.setDozing(mDozing);
updateDozingState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
index 4c879c6..13ee23f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
@@ -127,6 +127,7 @@
final int action = event.getAction();
if (action == MotionEvent.ACTION_OUTSIDE) {
poke(event);
+ return true;
} else if (action == MotionEvent.ACTION_DOWN) {
if (DEBUG) {
Slog.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
@@ -158,7 +159,7 @@
return false;
}
- public void poke(MotionEvent event) {
+ private void poke(MotionEvent event) {
mLastPokeTime = event.getEventTime();
if (DEBUG)
Slog.v(TAG, "poked! size=" + getSize(mLastPokeTime));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index c02ce0e..2771011 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -67,29 +67,15 @@
public static class IconState {
public final boolean visible;
-
public final int icon;
-
- /**
- * Optional iconOverlay resource id.
- *
- * <p>Set to -1 if not present.
- */
- public final int iconOverlay;
-
public final String contentDescription;
- public IconState(boolean visible, int icon, int iconOverlay, String contentDescription) {
+ public IconState(boolean visible, int icon, String contentDescription) {
this.visible = visible;
this.icon = icon;
- this.iconOverlay = iconOverlay;
this.contentDescription = contentDescription;
}
- public IconState(boolean visible, int icon, String contentDescription) {
- this(visible, icon, -1 /* iconOverlay */, contentDescription);
- }
-
public IconState(boolean visible, int icon, int contentDescription,
Context context) {
this(visible, icon, context.getString(contentDescription));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index c21f444..39f7d12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -24,7 +24,6 @@
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
-import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -92,7 +91,6 @@
private final DataSaverController mDataSaverController;
private final CurrentUserTracker mUserTracker;
private Config mConfig;
- private final NetworkScoreManager mNetworkScoreManager;
// Subcontrollers.
@VisibleForTesting
@@ -149,12 +147,9 @@
public NetworkControllerImpl(Context context, Looper bgLooper,
DeviceProvisionedController deviceProvisionedController) {
this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
- context.getSystemService(NetworkScoreManager.class),
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
(WifiManager) context.getSystemService(Context.WIFI_SERVICE),
- SubscriptionManager.from(context),
- Config.readConfig(context),
- bgLooper,
+ SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
new CallbackHandler(),
new AccessPointControllerImpl(context, bgLooper),
new DataUsageController(context),
@@ -165,12 +160,8 @@
@VisibleForTesting
NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
- NetworkScoreManager networkScoreManager,
- TelephonyManager telephonyManager,
- WifiManager wifiManager,
- SubscriptionManager subManager,
- Config config,
- Looper bgLooper,
+ TelephonyManager telephonyManager, WifiManager wifiManager,
+ SubscriptionManager subManager, Config config, Looper bgLooper,
CallbackHandler callbackHandler,
AccessPointControllerImpl accessPointController,
DataUsageController dataUsageController,
@@ -193,7 +184,6 @@
// wifi
mWifiManager = wifiManager;
- mNetworkScoreManager = networkScoreManager;
mLocale = mContext.getResources().getConfiguration().locale;
mAccessPoints = accessPointController;
@@ -207,7 +197,7 @@
}
});
mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
- mCallbackHandler, this, mNetworkScoreManager);
+ mCallbackHandler, this);
mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 2104cb1..2819624 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -17,50 +17,33 @@
import android.content.Context;
import android.content.Intent;
-import android.database.ContentObserver;
-import android.net.NetworkBadging;
import android.net.NetworkCapabilities;
-import android.net.NetworkKey;
-import android.net.NetworkScoreManager;
-import android.net.ScoredNetwork;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiNetworkScoreCache;
-import android.net.wifi.WifiNetworkScoreCache.CacheListener;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
-import android.provider.Settings;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.AsyncChannel;
-import com.android.settingslib.Utils;
import com.android.settingslib.wifi.WifiStatusTracker;
+import com.android.systemui.R;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.R;
-
import java.util.Objects;
-import java.util.List;
public class WifiSignalController extends
SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
-
private final WifiManager mWifiManager;
private final AsyncChannel mWifiChannel;
private final boolean mHasMobileData;
- private final NetworkScoreManager mNetworkScoreManager;
- private final WifiNetworkScoreCache mScoreCache;
private final WifiStatusTracker mWifiTracker;
- private boolean mScoringUiEnabled = false;
-
public WifiSignalController(Context context, boolean hasMobileData,
- CallbackHandler callbackHandler, NetworkControllerImpl networkController,
- NetworkScoreManager networkScoreManager) {
+ CallbackHandler callbackHandler, NetworkControllerImpl networkController) {
super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
callbackHandler, networkController);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
@@ -85,44 +68,6 @@
AccessibilityContentDescriptions.WIFI_NO_CONNECTION
);
- mScoreCache = new WifiNetworkScoreCache(context, new CacheListener(handler) {
- @Override
- public void networkCacheUpdated(List<ScoredNetwork> networks) {
- mCurrentState.badgeEnum = getWifiBadgeEnum();
- notifyListenersIfNecessary();
- }
- });
-
- // Setup scoring
- mNetworkScoreManager = networkScoreManager;
- configureScoringGating();
- registerScoreCache();
- }
-
- private void configureScoringGating() {
- ContentObserver observer = new ContentObserver(new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange) {
- mScoringUiEnabled =
- Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.NETWORK_SCORING_UI_ENABLED, 0) == 1;
- }
- };
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.NETWORK_SCORING_UI_ENABLED),
- false /* notifyForDescendants */,
- observer);
-
- observer.onChange(false /* selfChange */); // Set the initial values
- }
-
- private void registerScoreCache() {
- Log.d(mTag, "Registered score cache");
- mNetworkScoreManager.registerNetworkScoreCache(
- NetworkKey.TYPE_WIFI,
- mScoreCache,
- NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK);
}
@Override
@@ -143,77 +88,27 @@
("," + mContext.getString(R.string.accessibility_quick_settings_no_internet));
}
- IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(),
- Utils.getWifiBadgeResource(mCurrentState.badgeEnum), contentDescription);
- IconState qsIcon = new IconState(
- mCurrentState.connected, getQsCurrentIconId(),
- Utils.getWifiBadgeResource(mCurrentState.badgeEnum), contentDescription);
+ IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
+ IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(),
+ contentDescription);
callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
wifiDesc, mCurrentState.isTransient);
}
- @Override
- public int getCurrentIconId() {
- if (mCurrentState.badgeEnum != NetworkBadging.BADGING_NONE) {
- return Utils.WIFI_PIE_FOR_BADGING[mCurrentState.level];
- }
- return super.getCurrentIconId();
- }
-
/**
* Extract wifi state directly from broadcasts about changes in wifi state.
*/
public void handleBroadcast(Intent intent) {
- // Update the WifiStatusTracker with the new information and update the score cache.
- NetworkKey previousNetworkKey = mWifiTracker.networkKey;
mWifiTracker.handleBroadcast(intent);
- updateScoreCacheIfNecessary(previousNetworkKey);
-
- mCurrentState.isTransient = mWifiTracker.state == WifiManager.WIFI_STATE_ENABLING
- || mWifiTracker.state == WifiManager.WIFI_AP_STATE_DISABLING
- || mWifiTracker.connecting;
mCurrentState.enabled = mWifiTracker.enabled;
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
mCurrentState.level = mWifiTracker.level;
- mCurrentState.badgeEnum = getWifiBadgeEnum();
notifyListenersIfNecessary();
}
- /**
- * Clears old scores out of the cache and requests new scores if the network key has changed.
- *
- * <p>New scores are requested asynchronously.
- */
- private void updateScoreCacheIfNecessary(NetworkKey previousNetworkKey) {
- if (mWifiTracker.networkKey == null) {
- return;
- }
- if ((previousNetworkKey == null) || !mWifiTracker.networkKey.equals(previousNetworkKey)) {
- mScoreCache.clearScores();
- mNetworkScoreManager.requestScores(new NetworkKey[]{mWifiTracker.networkKey});
- }
- }
-
- /**
- * Returns the wifi badge enum for the current {@link #mWifiTracker} state.
- *
- * <p>{@link #updateScoreCacheIfNecessary} should be called prior to this method.
- */
- private int getWifiBadgeEnum() {
- if (!mScoringUiEnabled || mWifiTracker.networkKey == null) {
- return NetworkBadging.BADGING_NONE;
- }
- ScoredNetwork score = mScoreCache.getScoredNetwork(mWifiTracker.networkKey);
-
- if (score != null) {
- return score.calculateBadge(mWifiTracker.rssi);
- }
- return NetworkBadging.BADGING_NONE;
- }
-
@VisibleForTesting
void setActivity(int wifiActivity) {
mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
@@ -254,7 +149,6 @@
static class WifiState extends SignalController.State {
String ssid;
- int badgeEnum;
boolean isTransient;
@Override
@@ -262,7 +156,6 @@
super.copyFrom(s);
WifiState state = (WifiState) s;
ssid = state.ssid;
- badgeEnum = state.badgeEnum;
isTransient = state.isTransient;
}
@@ -270,7 +163,6 @@
protected void toString(StringBuilder builder) {
super.toString(builder);
builder.append(',').append("ssid=").append(ssid);
- builder.append(',').append("badgeEnum=").append(badgeEnum);
builder.append(',').append("isTransient=").append(isTransient);
}
@@ -278,7 +170,6 @@
public boolean equals(Object o) {
return super.equals(o)
&& Objects.equals(((WifiState) o).ssid, ssid)
- && (((WifiState) o).badgeEnum == badgeEnum)
&& (((WifiState) o).isTransient == isTransient);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 7ed1e2c..1ed5f56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -96,9 +96,7 @@
private void simulateEvent(SysuiColorExtractor extractor) {
// Let's fake a color event
- List<Pair<Color, Integer>> dummyColors = new ArrayList<>();
- dummyColors.add(new Pair<>(Color.valueOf(Color.BLACK), 1));
- extractor.onColorsChanged(new WallpaperColors(dummyColors),
+ extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK), null, null, 0),
WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
}
}
\ No newline at end of file
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 c7af0d9..1b42d1b 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
@@ -24,12 +24,17 @@
import android.app.Notification;
import android.content.Context;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.view.ViewGroup;
import android.widget.RemoteViews;
import com.android.systemui.R;
@@ -45,7 +50,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -142,6 +149,41 @@
Assert.assertNull(mRow.getEntry().getRunningTask());
}
+ @Test
+ public void testInflationIsRetriedIfAsyncFails() throws Exception {
+ NotificationInflater.InflationProgress result =
+ new NotificationInflater.InflationProgress();
+ result.packageContext = mContext;
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ NotificationInflater.applyRemoteView(result,
+ NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW, 0, mRow,
+ false /* redactAmbient */, true /* isNewView */, new RemoteViews.OnClickHandler(),
+ new NotificationInflater.InflationCallback() {
+ @Override
+ public void handleInflationException(StatusBarNotification notification,
+ Exception e) {
+ countDownLatch.countDown();
+ throw new RuntimeException("No Exception expected");
+ }
+
+ @Override
+ public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ countDownLatch.countDown();
+ }
+ }, mRow.getEntry(), mRow.getPrivateLayout(), null, null, new HashMap<>(),
+ new NotificationInflater.ApplyCallback() {
+ @Override
+ public void setResultView(View v) {
+ }
+
+ @Override
+ public RemoteViews getRemoteView() {
+ return new AsyncFailRemoteView(mContext.getPackageName(),
+ R.layout.custom_view_dark);
+ }
+ });
+ countDownLatch.await();
+ }
@Test
public void testSupersedesExistingTask() throws Exception {
@@ -200,4 +242,30 @@
mException = exception;
}
}
+
+ private class AsyncFailRemoteView extends RemoteViews {
+ Handler mHandler = new Handler(Looper.getMainLooper());
+
+ public AsyncFailRemoteView(String packageName, int layoutId) {
+ super(packageName, layoutId);
+ }
+
+ @Override
+ public View apply(Context context, ViewGroup parent) {
+ return super.apply(context, parent);
+ }
+
+ @Override
+ public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
+ OnViewAppliedListener listener, OnClickHandler handler) {
+ mHandler.post(() -> listener.onError(new RuntimeException("Failed to inflate async")));
+ return new CancellationSignal();
+ }
+
+ @Override
+ public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
+ OnViewAppliedListener listener) {
+ return applyAsync(context, parent, executor, listener, null);
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index a120cec..4cc83f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -14,7 +14,6 @@
package com.android.systemui.statusbar.phone;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -33,17 +32,13 @@
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.utils.leaks.BaseLeakChecker;
import android.testing.TestableLooper.RunWithLooper;
-import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -54,6 +49,10 @@
super(NavigationBarFragment.class);
}
+ protected void createRootView() {
+ mView = new NavigationBarFrame(mContext);
+ }
+
@Before
public void setup() {
mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index cb20639..51bd7bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -18,6 +18,7 @@
import android.os.HandlerThread;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.SubscriptionInfo;
+import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 505e1d8..a8319a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -19,7 +19,6 @@
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
-import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
import android.os.Looper;
import android.telephony.PhoneStateListener;
@@ -84,7 +83,6 @@
protected Config mConfig;
protected CallbackHandler mCallbackHandler;
protected SubscriptionDefaults mMockSubDefaults;
- protected NetworkScoreManager mMockNetworkScoreManager;
protected DeviceProvisionedController mMockProvisionController;
protected DeviceProvisionedListener mUserCallback;
@@ -113,8 +111,6 @@
mMockCm = mock(ConnectivityManager.class);
mMockSubDefaults = mock(SubscriptionDefaults.class);
mNetCapabilities = new NetworkCapabilities();
- mMockNetworkScoreManager = mock(NetworkScoreManager.class);
-
when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true);
when(mMockCm.getDefaultNetworkCapabilitiesForUser(0)).thenReturn(
new NetworkCapabilities[] { mNetCapabilities });
@@ -135,8 +131,7 @@
return null;
}).when(mMockProvisionController).addCallback(any());
- mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager,
- mMockTm, mMockWm, mMockSm,
+ mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mMockProvisionController);
@@ -177,8 +172,8 @@
protected NetworkControllerImpl setUpNoMobileData() {
when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
NetworkControllerImpl networkControllerNoMobile
- = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager, mMockTm,
- mMockWm, mMockSm, mConfig, mContext.getMainLooper(), mCallbackHandler,
+ = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
+ mConfig, mContext.getMainLooper(), mCallbackHandler,
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
mock(DeviceProvisionedController.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index dfe00f9..8d106b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -91,8 +91,7 @@
public void test4gDataIcon() {
// Switch to showing 4g icon and re-initialize the NetworkController.
mConfig.show4gForLte = true;
- mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager,
- mMockTm, mMockWm, mMockSm,
+ mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 1627925..be3802b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -54,8 +54,7 @@
// Turn off mobile network support.
Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
- mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager,
- mMockTm, mMockWm, mMockSm,
+ mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class));
@@ -117,8 +116,7 @@
// Turn off mobile network support.
Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
- mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager,
- mMockTm, mMockWm, mMockSm,
+ mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
mConfig, Looper.getMainLooper(), mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index dbaa2c5..ffd0165 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -1,47 +1,24 @@
package com.android.systemui.statusbar.policy;
import android.content.Intent;
-import android.net.NetworkBadging;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkKey;
-import android.net.RssiCurve;
-import android.net.ScoredNetwork;
-import android.net.WifiKey;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiNetworkScoreCache;
-import android.os.Bundle;
-import android.provider.Settings;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.settingslib.Utils;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Matchers;
import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -50,13 +27,6 @@
private static final int MIN_RSSI = -100;
private static final int MAX_RSSI = -55;
- private static final int LATCH_TIMEOUT = 2000;
- private static final String TEST_SSID = "\"Test SSID\"";
- private static final String TEST_BSSID = "00:00:00:00:00:00";
-
- private final List<NetworkKey> mRequestedKeys = new ArrayList<>();
- private CountDownLatch mRequestScoresLatch;
-
@Test
public void testWifiIcon() {
String testSsid = "Test SSID";
@@ -77,79 +47,6 @@
}
@Test
- public void testBadgedWifiIcon() throws Exception {
- // TODO(sghuman): Refactor this setup code when creating a test for the badged QsIcon.
- int testLevel = 1;
- RssiCurve mockBadgeCurve = mock(RssiCurve.class);
- Bundle attr = new Bundle();
- attr.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve);
- ScoredNetwork score =
- new ScoredNetwork(
- new NetworkKey(new WifiKey(TEST_SSID, TEST_BSSID)),
- null,
- false /* meteredHint */,
- attr);
-
- // Must set the Settings value before instantiating the NetworkControllerImpl due to bugs in
- // TestableSettingsProvider.
- Settings.Global.putString(mContext.getContentResolver(),
- Settings.Global.NETWORK_SCORING_UI_ENABLED,
- "1");
- super.setUp(); // re-instantiate NetworkControllImpl now that setting has been updated
- setupNetworkScoreManager();
-
- // Test Requesting Scores
- mRequestScoresLatch = new CountDownLatch(1);
- setWifiEnabled(true);
- setWifiState(true, TEST_SSID, TEST_BSSID);
- mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
-
- when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) NetworkBadging.BADGING_SD);
-
- ArgumentCaptor<WifiNetworkScoreCache> scoreCacheCaptor =
- ArgumentCaptor.forClass(WifiNetworkScoreCache.class);
- verify(mMockNetworkScoreManager).registerNetworkScoreCache(
- anyInt(),
- scoreCacheCaptor.capture(),
- Matchers.anyInt());
- scoreCacheCaptor.getValue().updateScores(Arrays.asList(score));
-
- // Test badge is set
- setWifiLevel(testLevel);
-
- ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
- Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
- anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(),
- any(), anyBoolean());
- IconState iconState = iconArg.getValue();
-
- assertEquals("Badged Wifi Resource is set",
- Utils.WIFI_PIE_FOR_BADGING[testLevel],
- iconState.icon);
- assertEquals("SD Badge is set",
- Utils.getWifiBadgeResource(NetworkBadging.BADGING_SD),
- iconState.iconOverlay);
- }
-
- private void setupNetworkScoreManager() {
- // Capture requested keys and count down latch if present
- doAnswer(
- new Answer<Boolean>() {
- @Override
- public Boolean answer(InvocationOnMock input) {
- if (mRequestScoresLatch != null) {
- mRequestScoresLatch.countDown();
- }
- NetworkKey[] keys = (NetworkKey[]) input.getArguments()[0];
- for (NetworkKey key : keys) {
- mRequestedKeys.add(key);
- }
- return true;
- }
- }).when(mMockNetworkScoreManager).requestScores(Matchers.<NetworkKey[]>any());
- }
-
- @Test
public void testQsWifiIcon() {
String testSsid = "Test SSID";
@@ -200,7 +97,7 @@
@Test
public void testRoamingIconDuringWifi() {
// Setup normal connection
- String testSsid = "\"Test SSID\"";
+ String testSsid = "Test SSID";
int testLevel = 2;
setWifiEnabled(true);
setWifiState(true, testSsid);
@@ -241,19 +138,12 @@
}
protected void setWifiState(boolean connected, String ssid) {
- setWifiState(connected, ssid, null);
- }
-
- protected void setWifiState(boolean connected, String ssid, String bssid) {
Intent i = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
NetworkInfo networkInfo = Mockito.mock(NetworkInfo.class);
Mockito.when(networkInfo.isConnected()).thenReturn(connected);
WifiInfo wifiInfo = Mockito.mock(WifiInfo.class);
Mockito.when(wifiInfo.getSSID()).thenReturn(ssid);
- if (bssid != null) {
- Mockito.when(wifiInfo.getBSSID()).thenReturn(bssid);
- }
i.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
i.putExtra(WifiManager.EXTRA_WIFI_INFO, wifiInfo);
@@ -278,7 +168,7 @@
Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
enabledArg.capture(), any(), iconArg.capture(), anyBoolean(),
- anyBoolean(), descArg.capture(), anyBoolean());
+ anyBoolean(), descArg.capture(), anyBoolean());
IconState iconState = iconArg.getValue();
assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue());
assertEquals("WiFi connected, in quick settings", connected, iconState.visible);
diff --git a/packages/SysuiDarkThemeOverlay/Android.mk b/packages/overlays/SysuiDarkThemeOverlay/Android.mk
similarity index 100%
rename from packages/SysuiDarkThemeOverlay/Android.mk
rename to packages/overlays/SysuiDarkThemeOverlay/Android.mk
diff --git a/packages/SysuiDarkThemeOverlay/AndroidManifest.xml b/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
similarity index 100%
rename from packages/SysuiDarkThemeOverlay/AndroidManifest.xml
rename to packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
diff --git a/packages/SysuiDarkThemeOverlay/res/values/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/strings.xml
similarity index 100%
rename from packages/SysuiDarkThemeOverlay/res/values/strings.xml
rename to packages/overlays/SysuiDarkThemeOverlay/res/values/strings.xml
diff --git a/packages/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
similarity index 72%
rename from packages/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
rename to packages/overlays/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
index 28ecfa0..7e2b955 100644
--- a/packages/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
+++ b/packages/overlays/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <!-- Dark theme for a window that should look like the Settings app. -->
<style name="Theme.DeviceDefault.QuickSettings" parent="android:Theme.DeviceDefault">
- <!-- Color palette -->
<item name="android:colorPrimary">@*android:color/primary_device_default_settings</item>
<item name="android:colorPrimaryDark">@*android:color/primary_dark_device_default_settings</item>
- <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_light</item>
- <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_light</item>
+ <!-- textColorPrimaryInverse is used on the lock screen and this means that we can't just
+ invert text colors otherwise we won't have contrast on the keyguard -->
+ <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_dark</item>
+ <!-- same for textColorSecondaryInverse -->
+ <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_dark</item>
<item name="android:colorSecondary">@*android:color/secondary_device_default_settings</item>
<item name="android:colorAccent">@*android:color/accent_device_default_dark</item>
<item name="android:colorControlNormal">?android:attr/textColorPrimary</item>
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/Android.mk b/packages/overlays/SysuiLightWallpaperThemeOverlay/Android.mk
new file mode 100644
index 0000000..4782a16
--- /dev/null
+++ b/packages/overlays/SysuiLightWallpaperThemeOverlay/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := SysuiLightWallpaperTheme
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := SysuiLightWallpaperThemeOverlay
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/AndroidManifest.xml b/packages/overlays/SysuiLightWallpaperThemeOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..1745b4c
--- /dev/null
+++ b/packages/overlays/SysuiLightWallpaperThemeOverlay/AndroidManifest.xml
@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui.theme.lightwallpaper"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android" android:priority="2"/>
+
+ <application android:label="@string/sysui_overlay_light" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/strings.xml b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/strings.xml
new file mode 100644
index 0000000..acc3d16
--- /dev/null
+++ b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <string name="sysui_overlay_light">Light</string>
+
+</resources>
+
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/themes_device_defaults.xml b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/themes_device_defaults.xml
new file mode 100644
index 0000000..877ebf8
--- /dev/null
+++ b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/themes_device_defaults.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="Theme.DeviceDefault.QuickSettings" parent="android:Theme.DeviceDefault.Light">
+ <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_light</item>
+ <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_light</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index fa2b1ee..2eb94d3 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4095,6 +4095,25 @@
// OS: O DR
CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR = 1013;
+ // OPEN: Settings > Network > Tether > Wi-Fi hotspot
+ WIFI_TETHER_SETTINGS = 1014;
+
+ // OPEN: Settings->Connected Devices->Bluetooth->(click on details link for a paired device)
+ // -> Edit name button.
+ // CATEGORY: SETTINGS
+ // OS: O DR
+ DIALOG_BLUETOOTH_PAIRED_DEVICE_RENAME = 1015;
+
+ // ACTION: Settings > Notification Settings > Open application notification
+ // CATEGORY: SETTINGS
+ // OS: O DR
+ ACTION_OPEN_APP_NOTIFICATION_SETTING = 1016;
+
+ // ACTION: Settings > App Info > Open app settings
+ // CATEGORY: SETTINGS
+ // OS: O DR
+ ACTION_OPEN_APP_SETTING = 1017;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9e2f52a..a58ba09 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -243,6 +243,8 @@
private WindowsForAccessibilityCallback mWindowsForAccessibilityCallback;
+ private boolean mIsAccessibilityButtonShown;
+
private UserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
}
@@ -872,21 +874,21 @@
}
/**
- * Invoked remotely over AIDL by SysUi when the availability of the accessibility
+ * Invoked remotely over AIDL by SysUi when the visibility of the accessibility
* button within the system's navigation area has changed.
*
- * @param available {@code true} if the accessibility button is available to the
+ * @param shown {@code true} if the accessibility button is shown to the
* user, {@code false} otherwise
*/
@Override
- public void notifyAccessibilityButtonAvailabilityChanged(boolean available) {
+ public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Caller does not hold permission "
+ android.Manifest.permission.STATUS_BAR_SERVICE);
}
synchronized (mLock) {
- notifyAccessibilityButtonAvailabilityChangedLocked(available);
+ notifyAccessibilityButtonVisibilityChangedLocked(shown);
}
}
@@ -1191,13 +1193,14 @@
}
}
- private void notifyAccessibilityButtonAvailabilityChangedLocked(boolean available) {
+ private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
final UserState state = getCurrentUserStateLocked();
- state.mIsAccessibilityButtonAvailable = available;
+ mIsAccessibilityButtonShown = available;
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final Service service = state.mBoundServices.get(i);
if (service.mRequestAccessibilityButton) {
- service.notifyAccessibilityButtonAvailabilityChangedLocked(available);
+ service.notifyAccessibilityButtonAvailabilityChangedLocked(
+ service.isAccessibilityButtonAvailableLocked(state));
}
}
}
@@ -1724,7 +1727,7 @@
scheduleUpdateInputFilter(userState);
scheduleUpdateClientsIfNeededLocked(userState);
updateRelevantEventsLocked(userState);
- updateAccessibilityButtonTargets(userState);
+ updateAccessibilityButtonTargetsLocked(userState);
}
private void updateAccessibilityFocusBehaviorLocked(UserState userState) {
@@ -2174,18 +2177,12 @@
}
}
- private void updateAccessibilityButtonTargets(UserState userState) {
- final List<Service> services;
- synchronized (mLock) {
- services = userState.mBoundServices;
- int numServices = services.size();
- for (int i = 0; i < numServices; i++) {
- final Service service = services.get(i);
- if (service.mRequestAccessibilityButton) {
- boolean available = service.mComponentName.equals(
- userState.mServiceAssignedToAccessibilityButton);
- service.notifyAccessibilityButtonAvailabilityChangedLocked(available);
- }
+ private void updateAccessibilityButtonTargetsLocked(UserState userState) {
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final Service service = userState.mBoundServices.get(i);
+ if (service.mRequestAccessibilityButton) {
+ service.notifyAccessibilityButtonAvailabilityChangedLocked(
+ service.isAccessibilityButtonAvailableLocked(userState));
}
}
}
@@ -2492,7 +2489,7 @@
case MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER: {
showAccessibilityButtonTargetSelection();
- }
+ } break;
}
}
@@ -2647,6 +2644,10 @@
boolean mRequestAccessibilityButton;
+ boolean mReceivedAccessibilityButtonCallbackSinceBind;
+
+ boolean mLastAccessibilityButtonCallbackState;
+
int mFetchFlags;
long mNotificationTimeout;
@@ -3587,9 +3588,8 @@
return false;
}
userState = getCurrentUserStateLocked();
+ return isAccessibilityButtonAvailableLocked(userState);
}
-
- return mRequestAccessibilityButton && userState.mIsAccessibilityButtonAvailable;
}
@Override
@@ -3647,6 +3647,7 @@
mService = null;
}
mServiceInterface = null;
+ mReceivedAccessibilityButtonCallbackSinceBind = false;
}
public boolean isConnectedLocked() {
@@ -3719,6 +3720,48 @@
}
}
+ private boolean isAccessibilityButtonAvailableLocked(UserState userState) {
+ // If the service does not request the accessibility button, it isn't available
+ if (!mRequestAccessibilityButton) {
+ return false;
+ }
+
+ // If the accessibility button isn't currently shown, it cannot be available to services
+ if (!mIsAccessibilityButtonShown) {
+ return false;
+ }
+
+ // If magnification is on and assigned to the accessibility button, services cannot be
+ if (userState.mIsNavBarMagnificationEnabled
+ && userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ return false;
+ }
+
+ int requestingServices = 0;
+ for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
+ final Service service = userState.mBoundServices.get(i);
+ if (service.mRequestAccessibilityButton) {
+ requestingServices++;
+ }
+ }
+
+ if (requestingServices == 1) {
+ // If only a single service is requesting, it must be this service, and the
+ // accessibility button is available to it
+ return true;
+ } else {
+ // With more than one active service, we derive the target from the user's settings
+ if (userState.mServiceAssignedToAccessibilityButton == null) {
+ // If the user has not made an assignment, we treat the button as available to
+ // all services until the user interacts with the button to make an assignment
+ return true;
+ } else {
+ // If an assignment was made, it defines availability
+ return mComponentName.equals(userState.mServiceAssignedToAccessibilityButton);
+ }
+ }
+ }
+
/**
* Notifies an accessibility service client for a scheduled event given the event type.
*
@@ -3866,6 +3909,13 @@
}
private void notifyAccessibilityButtonAvailabilityChangedInternal(boolean available) {
+ // Only notify the service if it's not been notified or the state has changed
+ if (mReceivedAccessibilityButtonCallbackSinceBind
+ && (mLastAccessibilityButtonCallbackState == available)) {
+ return;
+ }
+ mReceivedAccessibilityButtonCallbackSinceBind = true;
+ mLastAccessibilityButtonCallbackState = available;
final IAccessibilityServiceClient listener;
synchronized (mLock) {
listener = mServiceInterface;
@@ -4865,7 +4915,6 @@
public int mSoftKeyboardShowMode = 0;
- public boolean mIsAccessibilityButtonAvailable;
public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
public ComponentName mServiceAssignedToAccessibilityButton;
@@ -4945,9 +4994,6 @@
mIsNavBarMagnificationAssignedToAccessibilityButton = false;
mIsAutoclickEnabled = false;
mSoftKeyboardShowMode = 0;
-
- // Clear state tracked from system UI
- mIsAccessibilityButtonAvailable = false;
}
public void destroyUiAutomationService() {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 073d7b2..3ae0511 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -255,7 +255,9 @@
final ViewNode node = nodes[i];
if (node == null) {
- Slog.w(TAG, "fillStructureWithAllowedValues(): no node for " + viewState.id);
+ if (sVerbose) {
+ Slog.v(TAG, "fillStructureWithAllowedValues(): no node for " + viewState.id);
+ }
continue;
}
@@ -862,11 +864,9 @@
final int numContexts = mContexts.size();
for (int i = 0; i < numContexts; i++) {
final FillContext context = mContexts.get(i);
- // TODO: create a function that gets just one node so it doesn't create an array
- // unnecessarily
- final ViewNode[] nodes = context.findViewNodesByAutofillIds(id);
- if (nodes != null) {
- AutofillValue candidate = nodes[0].getAutofillValue();
+ final ViewNode node = context.findViewNodeByAutofillId(id);
+ if (node != null) {
+ final AutofillValue candidate = node.getAutofillValue();
if (sDebug) {
Slog.d(TAG, "getValueFromContexts(" + id + ") at " + i + ": " + candidate);
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index b408da8..4c9495a 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -17,7 +17,6 @@
package com.android.server;
import android.app.ActivityManager;
-import android.app.KeyguardManager;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -41,10 +40,12 @@
import android.util.MutableBoolean;
import android.util.Slog;
import android.view.KeyEvent;
+import android.view.WindowManagerInternal;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
/**
@@ -56,6 +57,7 @@
*/
public class GestureLauncherService extends SystemService {
private static final boolean DBG = false;
+ private static final boolean DBG_CAMERA_LIFT = true; // false once b/62623620 is fixed
private static final String TAG = "GestureLauncherService";
/**
@@ -82,7 +84,7 @@
private Context mContext;
private final MetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
- private KeyguardManager mKeyguardManager;
+ private WindowManagerInternal mWindowManagerInternal;
/** The wake lock held when a gesture is detected. */
private WakeLock mWakeLock;
@@ -150,8 +152,7 @@
return;
}
- mKeyguardManager = (KeyguardManager) mContext.getSystemService(
- Context.KEYGUARD_SERVICE);
+ mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mPowerManager = (PowerManager) mContext.getSystemService(
Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -517,33 +518,40 @@
private final class CameraLiftTriggerEventListener extends TriggerEventListener {
@Override
public void onTrigger(TriggerEvent event) {
+ if (DBG_CAMERA_LIFT) Slog.d(TAG, String.format("onTrigger event - time: %d, name: %s",
+ event.timestamp, event.sensor.getName()));
if (!mCameraLiftRegistered) {
- if (DBG) Slog.d(TAG, "Ignoring camera lift event because it's unregistered.");
+ if (DBG_CAMERA_LIFT) Slog.d(TAG, "Ignoring camera lift event because it's " +
+ "unregistered.");
return;
}
if (event.sensor == mCameraLiftTriggerSensor) {
Resources resources = mContext.getResources();
SensorManager sensorManager = (SensorManager) mContext.getSystemService(
Context.SENSOR_SERVICE);
-
- if (DBG) {
+ boolean keyguardShowingAndNotOccluded =
+ mWindowManagerInternal.isKeyguardShowingAndNotOccluded();
+ boolean interactive = mPowerManager.isInteractive();
+ if (DBG_CAMERA_LIFT) {
float[] values = event.values;
- Slog.d(TAG, String.format("Received a camera lift trigger event: " +
- "values=[%.4f].", values[0]));
+ Slog.d(TAG, String.format("Received a camera lift trigger " +
+ "event: values=[%.4f], keyguard showing: %b, interactive: %b", values[0],
+ keyguardShowingAndNotOccluded, interactive));
}
- if (mKeyguardManager.isKeyguardLocked() || !mPowerManager.isInteractive()) {
+ if (keyguardShowingAndNotOccluded || !interactive) {
if (handleCameraGesture(true /* useWakelock */,
StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_CAMERA_LIFT_TRIGGER);
}
- } else if (DBG) {
- Slog.d(TAG, "Ignoring lift event because device is awake");
+ } else {
+ if (DBG_CAMERA_LIFT) Slog.d(TAG, "Ignoring lift event");
}
- mCameraLiftRegistered = sensorManager.requestTriggerSensor(mCameraLiftTriggerListener,
- mCameraLiftTriggerSensor);
+ mCameraLiftRegistered = sensorManager.requestTriggerSensor(
+ mCameraLiftTriggerListener, mCameraLiftTriggerSensor);
- if (DBG) Slog.d(TAG, "Camera lift trigger sensor re-registered: " + mCameraLiftRegistered);
+ if (DBG_CAMERA_LIFT) Slog.d(TAG, "Camera lift trigger sensor re-registered: " +
+ mCameraLiftRegistered);
return;
}
}
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 7080c41..516f8f6 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -23,6 +24,7 @@
import android.os.AsyncTask;
import android.os.RecoverySystem;
import android.os.storage.StorageManager;
+import android.provider.Settings;
import android.telephony.euicc.EuiccManager;
import android.util.Log;
import android.util.Slog;
@@ -31,14 +33,29 @@
import com.android.internal.R;
import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class MasterClearReceiver extends BroadcastReceiver {
private static final String TAG = "MasterClear";
+ private static final String ACTION_WIPE_EUICC_DATA =
+ "com.android.internal.action.wipe_euicc_data";
+ private static final long DEFAULT_EUICC_WIPING_TIMEOUT_MILLIS = 30000L; // 30 s
private boolean mWipeExternalStorage;
- private boolean mWipeEims;
+ private boolean mWipeEsims;
+ private static CountDownLatch mEuiccFactoryResetLatch;
@Override
public void onReceive(final Context context, final Intent intent) {
+ if (ACTION_WIPE_EUICC_DATA.equals(intent.getAction())) {
+ if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
+ int detailedCode = intent.getIntExtra(
+ EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0);
+ Slog.e(TAG, "Error wiping euicc data, Detailed code = " + detailedCode);
+ }
+ mEuiccFactoryResetLatch.countDown();
+ return;
+ }
if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
if (!"google.com".equals(intent.getStringExtra("from"))) {
Slog.w(TAG, "Ignoring master clear request -- not from trusted server.");
@@ -57,7 +74,7 @@
final boolean shutdown = intent.getBooleanExtra("shutdown", false);
final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
- mWipeEims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
+ mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
|| intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false);
@@ -77,7 +94,7 @@
}
};
- if (mWipeExternalStorage || mWipeEims) {
+ if (mWipeExternalStorage || mWipeEsims) {
// thr will be started at the end of this task.
new WipeDataTask(context, thr).execute();
} else {
@@ -112,10 +129,31 @@
Context.STORAGE_SERVICE);
sm.wipeAdoptableDisks();
}
- if (mWipeEims) {
+ if (mWipeEsims) {
EuiccManager euiccManager = (EuiccManager) mContext.getSystemService(
Context.EUICC_SERVICE);
- // STOPSHIP: add EuiccManager API to factory reset eUICC
+ Intent intent = new Intent(mContext, MasterClearReceiver.class);
+ intent.setAction(ACTION_WIPE_EUICC_DATA);
+ PendingIntent callbackIntent = PendingIntent.getBroadcast(
+ mContext,
+ 0 /* requestCode */,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ mEuiccFactoryResetLatch = new CountDownLatch(1);
+ euiccManager.eraseSubscriptions(callbackIntent);
+ try {
+ long waitingTime = Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Settings.Global.EUICC_WIPING_TIMEOUT_MILLIS,
+ DEFAULT_EUICC_WIPING_TIMEOUT_MILLIS);
+
+ if (!mEuiccFactoryResetLatch.await(waitingTime, TimeUnit.MILLISECONDS)) {
+ Slog.e(TAG, "Timeout wiping eUICC data.");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Slog.e(TAG, "Wiping eUICC data interrupted", e);
+ }
}
return null;
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index de144777..756e274 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -626,19 +626,19 @@
!= ActivityManager.APP_START_MODE_NORMAL) {
if (stopping == null) {
stopping = new ArrayList<>();
- String compName = service.name.flattenToShortString();
- EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName);
- StringBuilder sb = new StringBuilder(64);
- sb.append("Stopping service due to app idle: ");
- UserHandle.formatUid(sb, service.appInfo.uid);
- sb.append(" ");
- TimeUtils.formatDuration(service.createTime
- - SystemClock.elapsedRealtime(), sb);
- sb.append(" ");
- sb.append(compName);
- Slog.w(TAG, sb.toString());
- stopping.add(service);
}
+ String compName = service.name.flattenToShortString();
+ EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName);
+ StringBuilder sb = new StringBuilder(64);
+ sb.append("Stopping service due to app idle: ");
+ UserHandle.formatUid(sb, service.appInfo.uid);
+ sb.append(" ");
+ TimeUtils.formatDuration(service.createTime
+ - SystemClock.elapsedRealtime(), sb);
+ sb.append(" ");
+ sb.append(compName);
+ Slog.w(TAG, sb.toString());
+ stopping.add(service);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6515302..5b2e779 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4118,7 +4118,8 @@
ri.activityInfo.packageName, ri.activityInfo.name));
mActivityStarter.startActivityLocked(null, intent, null /*ephemeralIntent*/,
null, ri.activityInfo, null /*rInfo*/, null, null, null, null, 0, 0, 0,
- null, 0, 0, 0, null, false, false, null, null, null);
+ null, 0, 0, 0, null, false, false, null, null, null,
+ "startSetupActivity");
}
}
}
@@ -4457,8 +4458,9 @@
container.checkEmbeddedAllowedInner(userId, intent, mimeType);
intent.addFlags(FORCE_NEW_TASK_FLAGS);
- return mActivityStarter.startActivityMayWait(null, -1, null, intent, mimeType, null, null, null,
- null, 0, 0, null, null, null, null, false, userId, container, null);
+ return mActivityStarter.startActivityMayWait(null, -1, null, intent, mimeType, null, null,
+ null, null, 0, 0, null, null, null, null, false, userId, container, null,
+ "startActivity");
}
@Override
@@ -4471,7 +4473,8 @@
// TODO: Switch to user app stacks here.
return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
- profilerInfo, null, null, bOptions, false, userId, null, null);
+ profilerInfo, null, null, bOptions, false, userId, null, null,
+ "startActivityAsUser");
}
@Override
@@ -4534,7 +4537,8 @@
try {
int ret = mActivityStarter.startActivityMayWait(null, targetUid, targetPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, null,
- null, null, bOptions, ignoreTargetSecurity, userId, null, null);
+ null, null, bOptions, ignoreTargetSecurity, userId, null, null,
+ "startActivityAsCaller");
return ret;
} catch (SecurityException e) {
// XXX need to figure out how to propagate to original app.
@@ -4563,7 +4567,7 @@
// TODO: Switch to user app stacks here.
mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, res, null,
- bOptions, false, userId, null, null);
+ bOptions, false, userId, null, null, "startActivityAndWait");
return res;
}
@@ -4577,7 +4581,7 @@
// TODO: Switch to user app stacks here.
int ret = mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
- null, null, config, bOptions, false, userId, null, null);
+ null, null, config, bOptions, false, userId, null, null, "startActivityWithConfig");
return ret;
}
@@ -4634,7 +4638,7 @@
// TODO: Switch to user app stacks here.
return mActivityStarter.startActivityMayWait(null, callingUid, callingPackage, intent,
resolvedType, session, interactor, null, null, 0, startFlags, profilerInfo, null,
- null, bOptions, false, userId, null, null);
+ null, bOptions, false, userId, null, null, "startVoiceActivity");
}
@Override
@@ -4653,7 +4657,7 @@
ALLOW_FULL_ONLY, "startAssistantActivity", null);
return mActivityStarter.startActivityMayWait(null, callingUid, callingPackage, intent,
resolvedType, null, null, null, null, 0, 0, null, null, null, bOptions, false,
- userId, null, null);
+ userId, null, null, "startAssistantActivity");
}
@Override
@@ -4826,7 +4830,7 @@
null /*ephemeralIntent*/, r.resolvedType, aInfo, null /*rInfo*/, null,
null, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1,
r.launchedFromUid, r.launchedFromPackage, -1, r.launchedFromUid, 0, options,
- false, false, null, null, null);
+ false, false, null, null, null, "startNextMatchingActivity");
Binder.restoreCallingIdentity(origId);
r.finishing = wasFinishing;
@@ -4858,7 +4862,7 @@
final int startActivityInPackage(int uid, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, Bundle bOptions, int userId,
- IActivityContainer container, TaskRecord inTask) {
+ IActivityContainer container, TaskRecord inTask, String reason) {
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
@@ -4866,7 +4870,7 @@
// TODO: Switch to user app stacks here.
int ret = mActivityStarter.startActivityMayWait(null, uid, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
- null, null, null, bOptions, false, userId, container, inTask);
+ null, null, null, bOptions, false, userId, container, inTask, reason);
return ret;
}
@@ -4874,12 +4878,13 @@
public final int startActivities(IApplicationThread caller, String callingPackage,
Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
int userId) {
- enforceNotIsolatedCaller("startActivities");
+ final String reason = "startActivities";
+ enforceNotIsolatedCaller(reason);
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, false, ALLOW_FULL_ONLY, "startActivity", null);
+ userId, false, ALLOW_FULL_ONLY, reason, null);
// TODO: Switch to user app stacks here.
int ret = mActivityStarter.startActivities(caller, -1, callingPackage, intents,
- resolvedTypes, resultTo, bOptions, userId);
+ resolvedTypes, resultTo, bOptions, userId, reason);
return ret;
}
@@ -4887,11 +4892,12 @@
Intent[] intents, String[] resolvedTypes, IBinder resultTo,
Bundle bOptions, int userId) {
+ final String reason = "startActivityInPackage";
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
+ userId, false, ALLOW_FULL_ONLY, reason, null);
// TODO: Switch to user app stacks here.
int ret = mActivityStarter.startActivities(null, uid, callingPackage, intents, resolvedTypes,
- resultTo, bOptions, userId);
+ resultTo, bOptions, userId, reason);
return ret;
}
@@ -12563,6 +12569,7 @@
Binder.restoreCallingIdentity(ident);
}
}
+ closeSystemDialogs("setLockScreenShown");
}
@Override
@@ -15004,6 +15011,10 @@
synchronized (this) {
dumpLastANRLocked(pw);
}
+ } else if ("starter".equals(cmd)) {
+ synchronized (this) {
+ dumpActivityStarterLocked(pw);
+ }
} else if ("recents".equals(cmd) || "r".equals(cmd)) {
synchronized (this) {
dumpRecentsLocked(fd, pw, args, opti, true, dumpPackage);
@@ -15237,6 +15248,11 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
+ dumpActivityStarterLocked(pw);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
if (mAssociations.size() > 0) {
pw.println();
@@ -15302,6 +15318,11 @@
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
+ dumpActivityStarterLocked(pw);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
if (mAssociations.size() > 0) {
pw.println();
@@ -15321,14 +15342,19 @@
}
private void dumpLastANRLocked(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity lastanr)");
if (mLastANRState == null) {
- pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity lastanr)");
pw.println(" <no ANR has occurred since boot>");
} else {
pw.println(mLastANRState);
}
}
+ private void dumpActivityStarterLocked(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity starter)");
+ mActivityStarter.dump(pw, "");
+ }
+
void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
@@ -15355,7 +15381,6 @@
if (needSep) {
pw.println();
}
- needSep = true;
printedAnything = true;
mStackSupervisor.dump(pw, " ");
}
@@ -24079,9 +24104,12 @@
pw.println(" Reason: " + reason);
}
pw.println();
+ mActivityStarter.dump(pw, " ");
+ pw.println();
+ pw.println("-------------------------------------------------------------------------------");
dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
- "ACTIVITY MANAGER ACTIVITIES (dumpsys activity lastanr)");
+ "" /* header */);
pw.println();
pw.close();
@@ -24323,7 +24351,7 @@
}
return mActivityStarter.startActivityMayWait(appThread, -1, callingPackage, intent,
resolvedType, null, null, null, null, 0, 0, null, null,
- null, bOptions, false, callingUser, null, tr);
+ null, bOptions, false, callingUser, null, tr, "AppTaskImpl");
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a45becd..9cde985 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3920,7 +3920,7 @@
destIntent, null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null,
null, parent.appToken, null, 0, -1, parent.launchedFromUid,
parent.launchedFromPackage, -1, parent.launchedFromUid, 0, null,
- false, true, null, null, null);
+ false, true, null, null, null, "navigateUpTo");
foundParentInTask = res == ActivityManager.START_SUCCESS;
} catch (RemoteException e) {
foundParentInTask = false;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 5d5614c..1ccac1b 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2273,6 +2273,31 @@
return null;
}
+ /**
+ * Get next valid stack for launching provided activity in the system. This will search across
+ * displays and stacks in last-focused order for a focusable and visible stack, except those
+ * that are on a currently focused display.
+ *
+ * @param r The activity that is being launched.
+ * @param currentFocus The display that previously had focus and thus needs to be ignored when
+ * searching for the next candidate.
+ * @return Next valid {@link ActivityStack}, null if not found.
+ */
+ ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) {
+ mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+ for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+ final int displayId = mTmpOrderedDisplayIds.get(i);
+ if (displayId == currentFocus) {
+ continue;
+ }
+ final ActivityStack stack = getValidLaunchStackOnDisplay(displayId, r);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
ActivityRecord getHomeActivity() {
return getHomeActivityForUser(mCurrentUser);
}
@@ -5159,7 +5184,7 @@
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
userId = task.userId;
int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null,
- null, null, 0, 0, bOptions, userId, null, task);
+ null, null, 0, 0, bOptions, userId, null, task, "startActivityFromRecents");
if (launchStackId == DOCKED_STACK_ID) {
setResizingDuringAnimation(task);
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 1ed2ac1..be30d5a 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -115,6 +115,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.service.voice.IVoiceInteractionSession;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
@@ -124,7 +125,10 @@
import com.android.server.pm.InstantAppResolver;
import com.android.server.wm.WindowManagerService;
+import java.io.PrintWriter;
+import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Date;
/**
* Controller for interpreting how and then launching activities.
@@ -188,6 +192,19 @@
private boolean mUsingVr2dDisplay;
+ // Last home activity record we attempted to start
+ private final ActivityRecord[] mLastHomeActivityStartRecord = new ActivityRecord[1];
+ // The result of the last home activity we attempted to start.
+ private int mLastHomeActivityStartResult;
+ // Last activity record we attempted to start
+ private final ActivityRecord[] mLastStartActivityRecord = new ActivityRecord[1];
+ // The result of the last activity we attempted to start.
+ private int mLastStartActivityResult;
+ // Time in milli seconds we attempted to start the last activity.
+ private long mLastStartActivityTimeMs;
+ // The reason we were trying to start the last activity
+ private String mLastStartReason;
+
private void reset() {
mStartActivity = null;
mIntent = null;
@@ -236,7 +253,37 @@
mUsingVr2dDisplay = false;
}
- final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+ int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+ String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
+ String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
+ ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
+ ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
+ TaskRecord inTask, String reason) {
+
+ if (TextUtils.isEmpty(reason)) {
+ throw new IllegalArgumentException("Need to specify a reason.");
+ }
+ mLastStartReason = reason;
+ mLastStartActivityTimeMs = System.currentTimeMillis();
+ mLastStartActivityRecord[0] = null;
+
+ mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType,
+ aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
+ callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
+ options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
+ container, inTask);
+
+ if (outActivity != null) {
+ // mLastStartActivityRecord[0] is set in the call to startActivity above.
+ outActivity[0] = mLastStartActivityRecord[0];
+ }
+ return mLastStartActivityResult;
+ }
+
+ /** DO NOT call this method directly. Use {@link #startActivityLocked} instead. */
+ private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
@@ -592,13 +639,14 @@
void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
mSupervisor.moveHomeStackTaskToTop(reason);
- startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,
- null /*resolvedType*/, aInfo, null /*rInfo*/, null /*voiceSession*/,
- null /*voiceInteractor*/, null /*resultTo*/, null /*resultWho*/,
- 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, null /*callingPackage*/,
- 0 /*realCallingPid*/, 0 /*realCallingUid*/, 0 /*startFlags*/, null /*options*/,
- false /*ignoreTargetSecurity*/, false /*componentSpecified*/, null /*outActivity*/,
- null /*container*/, null /*inTask*/);
+ mLastHomeActivityStartResult = startActivityLocked(null /*caller*/, intent,
+ null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
+ null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/,
+ null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/,
+ null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
+ 0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
+ false /*componentSpecified*/, mLastHomeActivityStartRecord /*outActivity*/,
+ null /*container*/, null /*inTask*/, "startHomeActivity: " + reason);
if (mSupervisor.inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
// resumed (to avoid recursive resume) and will stay that way until something pokes it
@@ -623,7 +671,7 @@
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, WaitResult outResult,
Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
- IActivityContainer iContainer, TaskRecord inTask) {
+ IActivityContainer iContainer, TaskRecord inTask, String reason) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -778,7 +826,7 @@
resultTo, resultWho, requestCode, callingPid,
callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
options, ignoreTargetSecurity, componentSpecified, outRecord, container,
- inTask);
+ inTask, reason);
Binder.restoreCallingIdentity(origId);
@@ -841,7 +889,7 @@
final int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle bOptions, int userId) {
+ Bundle bOptions, int userId, String reason) {
if (intents == null) {
throw new NullPointerException("intents is null");
}
@@ -903,7 +951,7 @@
resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
callingPid, callingUid, callingPackage,
realCallingPid, realCallingUid, 0,
- options, false, componentSpecified, outActivity, null, null);
+ options, false, componentSpecified, outActivity, null, null, reason);
if (res < 0) {
return res;
}
@@ -2021,7 +2069,18 @@
return mSupervisor.mFocusedStack;
}
- if (mSourceDisplayId == DEFAULT_DISPLAY) {
+ if (mSourceDisplayId != DEFAULT_DISPLAY) {
+ // Try to put the activity in a stack on a secondary display.
+ stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
+ if (stack == null) {
+ // If source display is not suitable - look for topmost valid stack in the system.
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+ "computeStackFocus: Can't launch on mSourceDisplayId=" + mSourceDisplayId
+ + ", looking on all displays.");
+ stack = mSupervisor.getNextValidLaunchStackLocked(r, mSourceDisplayId);
+ }
+ }
+ if (stack == null) {
// We first try to put the task in the first dynamic stack on home display.
final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
@@ -2037,8 +2096,6 @@
bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
FULLSCREEN_WORKSPACE_STACK_ID;
stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
- } else {
- stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
}
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ r + " stackId=" + stack.mStackId);
@@ -2246,4 +2303,40 @@
}
return didSomething;
}
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "ActivityStarter:");
+ prefix = prefix + " ";
+
+ pw.println(prefix + "mLastStartReason=" + mLastStartReason);
+ pw.println(prefix + "mLastStartActivityTimeMs="
+ + DateFormat.getDateTimeInstance().format(new Date(mLastStartActivityTimeMs)));
+ pw.println(prefix + "mLastStartActivityResult=" + mLastStartActivityResult);
+ ActivityRecord r = mLastStartActivityRecord[0];
+ if (r != null) {
+ pw.println(prefix + "mLastStartActivityRecord:");
+ r.dump(pw, prefix + " ");
+ }
+ pw.println(prefix + "mLastHomeActivityStartResult=" + mLastHomeActivityStartResult);
+ r = mLastHomeActivityStartRecord[0];
+ if (r != null) {
+ pw.println(prefix + "mLastHomeActivityStartRecord:");
+ r.dump(pw, prefix + " ");
+ }
+ if (mStartActivity != null) {
+ pw.println(prefix + "mStartActivity:");
+ mStartActivity.dump(pw, prefix + " ");
+ }
+ if (mIntent != null) {
+ pw.println(prefix + "mIntent=" + mIntent);
+ }
+ if (mOptions != null) {
+ pw.println(prefix + "mOptions=" + mOptions);
+ }
+ pw.println(prefix + "mLaunchSingleTop=" + mLaunchSingleTop
+ + " mLaunchSingleInstance=" + mLaunchSingleInstance
+ + " mLaunchSingleTask=" + mLaunchSingleTask
+ + " mLaunchFlags=0x" + Integer.toHexString(mLaunchFlags)
+ + " mDoResume=" + mDoResume + " mAddingToTask=" + mAddingToTask);
+ }
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index a83f989..0d1c579 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -412,7 +412,7 @@
task.mCallingPackage, task.intent,
null, null, null, 0, 0,
ActivityOptions.makeBasic().toBundle(),
- task.userId, null, null);
+ task.userId, null, null, "AppErrors");
}
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 1705b58..064ca58 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -614,9 +614,10 @@
skip = true;
}
- if (!skip && (filter.receiverList.app == null || filter.receiverList.app.crashing)) {
+ if (!skip && (filter.receiverList.app == null || filter.receiverList.app.killed
+ || filter.receiverList.app.crashing)) {
Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
- + " to " + filter.receiverList + ": process crashing");
+ + " to " + filter.receiverList + ": process gone or crashing");
skip = true;
}
@@ -1317,7 +1318,7 @@
}
// Is this receiver's application already running?
- if (app != null && app.thread != null) {
+ if (app != null && app.thread != null && !app.killed) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 6eca3fa..cad5dcf 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -346,7 +346,7 @@
} else {
owner.startActivityInPackage(uid, key.packageName, finalIntent,
resolvedType, resultTo, resultWho, requestCode, 0,
- options, userId, container, null);
+ options, userId, container, null, "PendingIntentRecord");
}
} catch (RuntimeException e) {
Slog.w(TAG, "Unable to send startActivity intent", e);
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index 9970c82..b89586d 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -69,7 +69,6 @@
public boolean setState(int oldState, int newState) {
if (state == oldState) {
setState(newState);
- EventLogTags.writeAmUserStateChanged(mHandle.getIdentifier(), newState);
return true;
} else {
Slog.w(TAG, "Expected user " + mHandle.getIdentifier() + " in state "
@@ -84,6 +83,7 @@
}
Slog.i(TAG, "User " + mHandle.getIdentifier() + " state changed from "
+ stateToString(state) + " to " + stateToString(newState));
+ EventLogTags.writeAmUserStateChanged(mHandle.getIdentifier(), newState);
lastState = state;
state = newState;
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5a7f6e3..e5ab784 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -166,7 +166,6 @@
/** debug calls to devices APIs */
protected static final boolean DEBUG_DEVICES = Log.isLoggable(TAG + ".DEVICES", Log.DEBUG);
-
/** How long to delay before persisting a change in volume/ringer mode. */
private static final int PERSIST_DELAY = 500;
@@ -692,6 +691,7 @@
// the mcc is read by onConfigureSafeVolume()
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
com.android.internal.R.integer.config_safe_media_volume_index) * 10;
+ mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
mUseFixedVolume = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
@@ -1347,7 +1347,7 @@
// This is simulated by stepping by the full allowed volume range
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
(device & mSafeMediaVolumeDevices) != 0) {
- step = mSafeMediaVolumeIndex;
+ step = safeMediaVolumeIndex(device);
} else {
step = streamState.getMaxIndex();
}
@@ -1693,7 +1693,7 @@
if (index != 0) {
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
(device & mSafeMediaVolumeDevices) != 0) {
- index = mSafeMediaVolumeIndex;
+ index = safeMediaVolumeIndex(device);
} else {
index = streamState.getMaxIndex();
}
@@ -3436,7 +3436,7 @@
MUSIC_ACTIVE_POLL_PERIOD_MS);
int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) &&
- (index > mSafeMediaVolumeIndex)) {
+ (index > safeMediaVolumeIndex(device))) {
// Approximate cumulative active music time
mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) {
@@ -3454,12 +3454,40 @@
mAudioHandler.obtainMessage(MSG_PERSIST_MUSIC_ACTIVE_MS, mMusicActiveMs, 0).sendToTarget();
}
+ private int getSafeUsbMediaVolumeIndex()
+ {
+ // determine UI volume index corresponding to the wanted safe gain in dBFS
+ int min = MIN_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
+ int max = MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
+
+ while (Math.abs(max-min) > 1) {
+ int index = (max + min) / 2;
+ float gainDB = AudioSystem.getStreamVolumeDB(
+ AudioSystem.STREAM_MUSIC, index, AudioSystem.DEVICE_OUT_USB_HEADSET);
+ if (gainDB == Float.NaN) {
+ //keep last min in case of read error
+ break;
+ } else if (gainDB == SAVE_VOLUME_GAIN_DBFS) {
+ min = index;
+ break;
+ } else if (gainDB < SAVE_VOLUME_GAIN_DBFS) {
+ min = index;
+ } else {
+ max = index;
+ }
+ }
+ return min * 10;
+ }
+
private void onConfigureSafeVolume(boolean force, String caller) {
synchronized (mSafeMediaVolumeState) {
int mcc = mContext.getResources().getConfiguration().mcc;
if ((mMcc != mcc) || ((mMcc == 0) && force)) {
mSafeMediaVolumeIndex = mContext.getResources().getInteger(
com.android.internal.R.integer.config_safe_media_volume_index) * 10;
+
+ mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
+
boolean safeMediaVolumeEnabled =
SystemProperties.getBoolean("audio.safemedia.force", false)
|| mContext.getResources().getBoolean(
@@ -5302,38 +5330,20 @@
return delay;
}
- private void sendDeviceConnectionIntent(int device, int state, String address,
- String deviceName) {
- if (DEBUG_DEVICES) {
- Slog.i(TAG, "sendDeviceConnectionIntent(dev:0x" + Integer.toHexString(device) +
- " state:0x" + Integer.toHexString(state) + " address:" + address +
- " name:" + deviceName + ");");
- }
- Intent intent = new Intent();
-
- intent.putExtra(CONNECT_INTENT_KEY_STATE, state);
- intent.putExtra(CONNECT_INTENT_KEY_ADDRESS, address);
- intent.putExtra(CONNECT_INTENT_KEY_PORT_NAME, deviceName);
-
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-
+ private void updateAudioRoutes(int device, int state)
+ {
int connType = 0;
if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
connType = AudioRoutesInfo.MAIN_HEADSET;
- intent.setAction(Intent.ACTION_HEADSET_PLUG);
- intent.putExtra("microphone", 1);
} else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE ||
device == AudioSystem.DEVICE_OUT_LINE) {
- /*do apps care about line-out vs headphones?*/
connType = AudioRoutesInfo.MAIN_HEADPHONES;
- intent.setAction(Intent.ACTION_HEADSET_PLUG);
- intent.putExtra("microphone", 0);
} else if (device == AudioSystem.DEVICE_OUT_HDMI ||
device == AudioSystem.DEVICE_OUT_HDMI_ARC) {
connType = AudioRoutesInfo.MAIN_HDMI;
- configureHdmiPlugIntent(intent, state);
- } else if (device == AudioSystem.DEVICE_OUT_USB_DEVICE) {
+ } else if (device == AudioSystem.DEVICE_OUT_USB_DEVICE||
+ device == AudioSystem.DEVICE_OUT_USB_HEADSET) {
connType = AudioRoutesInfo.MAIN_USB;
}
@@ -5352,6 +5362,34 @@
}
}
}
+ }
+
+ private void sendDeviceConnectionIntent(int device, int state, String address,
+ String deviceName) {
+ if (DEBUG_DEVICES) {
+ Slog.i(TAG, "sendDeviceConnectionIntent(dev:0x" + Integer.toHexString(device) +
+ " state:0x" + Integer.toHexString(state) + " address:" + address +
+ " name:" + deviceName + ");");
+ }
+ Intent intent = new Intent();
+
+ if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
+ intent.setAction(Intent.ACTION_HEADSET_PLUG);
+ intent.putExtra("microphone", 1);
+ } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE ||
+ device == AudioSystem.DEVICE_OUT_LINE) {
+ intent.setAction(Intent.ACTION_HEADSET_PLUG);
+ intent.putExtra("microphone", 0);
+ } else if (device == AudioSystem.DEVICE_OUT_HDMI ||
+ device == AudioSystem.DEVICE_OUT_HDMI_ARC) {
+ configureHdmiPlugIntent(intent, state);
+ }
+
+ intent.putExtra(CONNECT_INTENT_KEY_STATE, state);
+ intent.putExtra(CONNECT_INTENT_KEY_ADDRESS, address);
+ intent.putExtra(CONNECT_INTENT_KEY_PORT_NAME, deviceName);
+
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
final long ident = Binder.clearCallingIdentity();
try {
@@ -5425,6 +5463,7 @@
if (!isUsb && device != AudioSystem.DEVICE_IN_WIRED_HEADSET) {
sendDeviceConnectionIntent(device, state, address, deviceName);
}
+ updateAudioRoutes(device, state);
}
}
@@ -5950,9 +5989,15 @@
private int mMcc = 0;
// mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
private int mSafeMediaVolumeIndex;
+ // mSafeUsbMediaVolumeIndex is used for USB Headsets and is to the music volume
+ // UI index corresponding to a gain of -15 dBFS. This corresponds to a loudness of 85 dB SPL
+ // if the headset is compliant to EN 60950 with a max loudness of 100dB SPL.
+ private int mSafeUsbMediaVolumeIndex;
+ private static final float SAVE_VOLUME_GAIN_DBFS = -15;
// mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET |
- AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
+ AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
+ AudioSystem.DEVICE_OUT_USB_HEADSET;
// mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
// When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
// automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
@@ -5961,6 +6006,17 @@
private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval
private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed
+ private int safeMediaVolumeIndex(int device) {
+ if ((device & mSafeMediaVolumeDevices) == 0) {
+ return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];
+ }
+ if (device == AudioSystem.DEVICE_OUT_USB_HEADSET) {
+ return mSafeUsbMediaVolumeIndex;
+ } else {
+ return mSafeMediaVolumeIndex;
+ }
+ }
+
private void setSafeMediaVolumeEnabled(boolean on, String caller) {
synchronized (mSafeMediaVolumeState) {
if ((mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_NOT_CONFIGURED) &&
@@ -5995,8 +6051,8 @@
continue;
}
int index = streamState.getIndex(device);
- if (index > mSafeMediaVolumeIndex) {
- streamState.setIndex(mSafeMediaVolumeIndex, device, caller);
+ if (index > safeMediaVolumeIndex(device)) {
+ streamState.setIndex(safeMediaVolumeIndex(device), device, caller);
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
@@ -6014,7 +6070,7 @@
if ((mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) &&
(mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
((device & mSafeMediaVolumeDevices) != 0) &&
- (index > mSafeMediaVolumeIndex)) {
+ (index > safeMediaVolumeIndex(device))) {
return false;
}
return true;
@@ -6240,6 +6296,7 @@
pw.print(" mSafeMediaVolumeState=");
pw.println(safeMediaVolumeStateToString(mSafeMediaVolumeState));
pw.print(" mSafeMediaVolumeIndex="); pw.println(mSafeMediaVolumeIndex);
+ pw.print(" mSafeUsbMediaVolumeIndex="); pw.println(mSafeUsbMediaVolumeIndex);
pw.print(" sIndependentA11yVolume="); pw.println(sIndependentA11yVolume);
pw.print(" mPendingVolumeCommand="); pw.println(mPendingVolumeCommand);
pw.print(" mMusicActiveMs="); pw.println(mMusicActiveMs);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index e825f90..3daaf8f 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1247,9 +1247,9 @@
protected void chooseUpstreamType(boolean tryCell) {
updateConfiguration(); // TODO - remove?
- final int upstreamType = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
+ final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
mConfig.preferredUpstreamIfaceTypes);
- if (upstreamType == ConnectivityManager.TYPE_NONE) {
+ if (ns == null) {
if (tryCell) {
mUpstreamNetworkMonitor.registerMobileNetworkRequest();
// We think mobile should be coming up; don't set a retry.
@@ -1257,40 +1257,30 @@
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
}
}
- setUpstreamByType(upstreamType);
+ setUpstreamNetwork(ns);
}
- protected void setUpstreamByType(int upType) {
- final ConnectivityManager cm = getConnectivityManager();
- Network network = null;
+ protected void setUpstreamNetwork(NetworkState ns) {
String iface = null;
- if (upType != ConnectivityManager.TYPE_NONE) {
- LinkProperties linkProperties = cm.getLinkProperties(upType);
- if (linkProperties != null) {
- // Find the interface with the default IPv4 route. It may be the
- // interface described by linkProperties, or one of the interfaces
- // stacked on top of it.
- Log.i(TAG, "Finding IPv4 upstream interface on: " + linkProperties);
- RouteInfo ipv4Default = RouteInfo.selectBestRoute(
- linkProperties.getAllRoutes(), Inet4Address.ANY);
- if (ipv4Default != null) {
- iface = ipv4Default.getInterface();
- Log.i(TAG, "Found interface " + ipv4Default.getInterface());
- } else {
- Log.i(TAG, "No IPv4 upstream interface, giving up.");
- }
- }
-
- if (iface != null) {
- network = cm.getNetworkForType(upType);
- if (network == null) {
- Log.e(TAG, "No Network for upstream type " + upType + "!");
- }
- setDnsForwarders(network, linkProperties);
+ if (ns != null && ns.linkProperties != null) {
+ // Find the interface with the default IPv4 route. It may be the
+ // interface described by linkProperties, or one of the interfaces
+ // stacked on top of it.
+ Log.i(TAG, "Finding IPv4 upstream interface on: " + ns.linkProperties);
+ RouteInfo ipv4Default = RouteInfo.selectBestRoute(
+ ns.linkProperties.getAllRoutes(), Inet4Address.ANY);
+ if (ipv4Default != null) {
+ iface = ipv4Default.getInterface();
+ Log.i(TAG, "Found interface " + ipv4Default.getInterface());
+ } else {
+ Log.i(TAG, "No IPv4 upstream interface, giving up.");
}
}
+
+ if (iface != null) {
+ setDnsForwarders(ns.network, ns.linkProperties);
+ }
notifyTetheredOfNewUpstreamIface(iface);
- NetworkState ns = mUpstreamNetworkMonitor.lookup(network);
if (ns != null && pertainsToCurrentUpstream(ns)) {
// If we already have NetworkState for this network examine
// it immediately, because there likely will be no second
@@ -1789,7 +1779,9 @@
}
}
- mLog.log(String.format("OBSERVED LinkProperties update iface=%s state=%s", iface, state));
+ mLog.log(String.format(
+ "OBSERVED LinkProperties update iface=%s state=%s lp=%s",
+ iface, IControlsTethering.getStateString(state), newLp));
final int which = TetherMasterSM.EVENT_IFACE_UPDATE_LINKPROPERTIES;
mTetherMasterSM.sendMessage(which, state, 0, newLp);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
index aaa63b1..2b81347 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
@@ -33,6 +33,16 @@
public static final int STATE_TETHERED = 2;
public static final int STATE_LOCAL_ONLY = 3;
+ public static String getStateString(int state) {
+ switch (state) {
+ case STATE_UNAVAILABLE: return "UNAVAILABLE";
+ case STATE_AVAILABLE: return "AVAILABLE";
+ case STATE_TETHERED: return "TETHERED";
+ case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
+ }
+ return "UNKNOWN: " + state;
+ }
+
/**
* Notify that |who| has changed its tethering state.
*
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 3aca45f..08deef8 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -85,13 +85,16 @@
mLog.i("tethering offload control not supported");
stop();
}
+ mLog.log("tethering offload started");
}
public void stop() {
+ final boolean wasStarted = started();
mUpstreamLinkProperties = null;
mHwInterface.stopOffloadControl();
mControlInitialized = false;
mConfigInitialized = false;
+ if (wasStarted) mLog.log("tethering offload stopped");
}
public void setUpstreamLinkProperties(LinkProperties lp) {
@@ -161,6 +164,7 @@
}
}
- return mHwInterface.setUpstreamParameters(iface, v4addr, v4gateway, v6gateways);
+ return mHwInterface.setUpstreamParameters(
+ iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways));
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 3ecf0d1..1fc1684 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.tethering;
+import static com.android.internal.util.BitUtils.uint16;
+
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
@@ -33,6 +35,9 @@
*/
public class OffloadHardwareInterface {
private static final String TAG = OffloadHardwareInterface.class.getSimpleName();
+ private static final String NO_INTERFACE_NAME = "";
+ private static final String NO_IPV4_ADDRESS = "";
+ private static final String NO_IPV4_GATEWAY = "";
private static native boolean configOffload();
@@ -107,6 +112,11 @@
public boolean setUpstreamParameters(
String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
+ iface = iface != null ? iface : NO_INTERFACE_NAME;
+ v4addr = v4addr != null ? v4addr : NO_IPV4_ADDRESS;
+ v4gateway = v4gateway != null ? v4gateway : NO_IPV4_GATEWAY;
+ v6gws = v6gws != null ? v6gws : new ArrayList<>();
+
final CbResults results = new CbResults();
try {
mOffloadControl.setUpstreamParameters(
@@ -143,8 +153,8 @@
handler.post(() -> {
controlCb.onNatTimeoutUpdate(
params.proto,
- params.src.addr, params.src.port,
- params.dst.addr, params.dst.port);
+ params.src.addr, uint16(params.src.port),
+ params.dst.addr, uint16(params.dst.port));
});
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index b88361f..881c0ca 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -56,9 +56,10 @@
import java.util.Random;
/**
- * @hide
+ * Provides the interface to IP-layer serving functionality for a given network
+ * interface, e.g. for tethering or "local-only hotspot" mode.
*
- * Tracks the eligibility of a given network interface for tethering.
+ * @hide
*/
public class TetherInterfaceStateMachine extends StateMachine {
private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
@@ -117,6 +118,12 @@
private String mMyUpstreamIfaceName; // may change over time
private NetworkInterface mNetworkInterface;
private byte[] mHwAddr;
+ // TODO: De-duplicate this with mLinkProperties above. Currently, these link
+ // properties are those selected by the IPv6TetheringCoordinator and relayed
+ // to us. By comparison, mLinkProperties contains the addresses and directly
+ // connected routes that have been formed from these properties iff. we have
+ // succeeded in configuring them and are able to announce them within Router
+ // Advertisements (otherwise, we do not add them to mLinkProperties at all).
private LinkProperties mLastIPv6LinkProperties;
private RouterAdvertisementDaemon mRaDaemon;
private RaParams mLastRaParams;
@@ -133,7 +140,7 @@
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mIfaceName);
+ resetLinkProperties();
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
mInitialState = new InitialState();
@@ -160,10 +167,15 @@
* Internals.
*/
- // configured when we start tethering and unconfig'd on error or conclusion
- private boolean configureIfaceIp(boolean enabled) {
- if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
+ private boolean startIPv4() { return configureIPv4(true); }
+ private void stopIPv4() { configureIPv4(false); }
+
+ private boolean configureIPv4(boolean enabled) {
+ if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
+
+ // TODO: Replace this hard-coded information with dynamically selected
+ // config passed down to us by a higher layer IP-coordinating element.
String ipAsString = null;
int prefixLen = 0;
if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
@@ -177,32 +189,45 @@
return true;
}
- InterfaceConfiguration ifcg = null;
+ final LinkAddress linkAddr;
try {
- ifcg = mNMService.getInterfaceConfig(mIfaceName);
- if (ifcg != null) {
- InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
- ifcg.setLinkAddress(new LinkAddress(addr, prefixLen));
- if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
- // The WiFi stack has ownership of the interface up/down state.
- // It is unclear whether the bluetooth or USB stacks will manage their own
- // state.
- ifcg.ignoreInterfaceUpDownStatus();
- } else {
- if (enabled) {
- ifcg.setInterfaceUp();
- } else {
- ifcg.setInterfaceDown();
- }
- }
- ifcg.clearFlag("running");
- mNMService.setInterfaceConfig(mIfaceName, ifcg);
+ final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName);
+ if (ifcg == null) {
+ mLog.e("Received null interface config");
+ return false;
}
+
+ InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
+ linkAddr = new LinkAddress(addr, prefixLen);
+ ifcg.setLinkAddress(linkAddr);
+ if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
+ // The WiFi stack has ownership of the interface up/down state.
+ // It is unclear whether the Bluetooth or USB stacks will manage their own
+ // state.
+ ifcg.ignoreInterfaceUpDownStatus();
+ } else {
+ if (enabled) {
+ ifcg.setInterfaceUp();
+ } else {
+ ifcg.setInterfaceDown();
+ }
+ }
+ ifcg.clearFlag("running");
+ mNMService.setInterfaceConfig(mIfaceName, ifcg);
} catch (Exception e) {
mLog.e("Error configuring interface " + e);
return false;
}
+ // Directly-connected route.
+ final RouteInfo route = new RouteInfo(linkAddr);
+ if (enabled) {
+ mLinkProperties.addLinkAddress(linkAddr);
+ mLinkProperties.addRoute(route);
+ } else {
+ mLinkProperties.removeLinkAddress(linkAddr);
+ mLinkProperties.removeRoute(route);
+ }
return true;
}
@@ -292,7 +317,7 @@
mLastIPv6LinkProperties = v6only;
}
- private void configureLocalRoutes(
+ private void configureLocalIPv6Routes(
HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
// [1] Remove the routes that are deprecated.
if (!deprecatedPrefixes.isEmpty()) {
@@ -307,6 +332,8 @@
} catch (RemoteException e) {
mLog.e("Failed to remove IPv6 routes from local table: " + e);
}
+
+ for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
}
// [2] Add only the routes that have not previously been added.
@@ -338,11 +365,13 @@
} catch (RemoteException e) {
mLog.e("Failed to add IPv6 routes to local table: " + e);
}
+
+ for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
}
}
}
- private void configureLocalDns(
+ private void configureLocalIPv6Dns(
HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
final INetd netd = NetdService.getInstance();
if (netd == null) {
@@ -360,6 +389,8 @@
} catch (ServiceSpecificException | RemoteException e) {
mLog.e("Failed to remove local dns IP " + dnsString + ": " + e);
}
+
+ mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
}
}
@@ -378,6 +409,8 @@
mLog.e("Failed to add local dns IP " + dnsString + ": " + e);
newDnses.remove(dns);
}
+
+ mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
}
}
@@ -394,10 +427,10 @@
final RaParams deprecatedParams =
RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
- configureLocalRoutes(deprecatedParams.prefixes,
+ configureLocalIPv6Routes(deprecatedParams.prefixes,
(newParams != null) ? newParams.prefixes : null);
- configureLocalDns(deprecatedParams.dnses,
+ configureLocalIPv6Dns(deprecatedParams.dnses,
(newParams != null) ? newParams.dnses : null);
mRaDaemon.buildNewRa(deprecatedParams, newParams);
@@ -417,12 +450,19 @@
private void sendInterfaceState(int newInterfaceState) {
mTetherController.updateInterfaceState(
TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
- // TODO: Populate mLinkProperties correctly, and send more sensible
- // updates more frequently (not just here).
+ sendLinkProperties();
+ }
+
+ private void sendLinkProperties() {
mTetherController.updateLinkProperties(
TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties));
}
+ private void resetLinkProperties() {
+ mLinkProperties.clear();
+ mLinkProperties.setInterfaceName(mIfaceName);
+ }
+
class InitialState extends State {
@Override
public void enter() {
@@ -462,7 +502,7 @@
class BaseServingState extends State {
@Override
public void enter() {
- if (!configureIfaceIp(true)) {
+ if (!startIPv4()) {
mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
return;
}
@@ -496,7 +536,9 @@
mLog.e("Failed to untether interface: " + e);
}
- configureIfaceIp(false);
+ stopIPv4();
+
+ resetLinkProperties();
}
@Override
@@ -513,6 +555,7 @@
break;
case CMD_IPV6_TETHER_UPDATE:
updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
+ sendLinkProperties();
break;
case CMD_IP_FORWARDING_ENABLE_ERROR:
case CMD_IP_FORWARDING_DISABLE_ERROR:
@@ -623,7 +666,6 @@
if (super.processMessage(message)) return true;
maybeLogMessage(this, message.what);
- boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLog.e("CMD_TETHER_REQUESTED while already tethering.");
@@ -653,10 +695,9 @@
mMyUpstreamIfaceName = newUpstreamIfaceName;
break;
default:
- retValue = false;
- break;
+ return false;
}
- return retValue;
+ return true;
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index b2d5051..9ebfaf7 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -174,10 +174,6 @@
mMobileNetworkCallback = null;
}
- public NetworkState lookup(Network network) {
- return (network != null) ? mNetworkMap.get(network) : null;
- }
-
// So many TODOs here, but chief among them is: make this functionality an
// integral part of this class such that whenever a higher priority network
// becomes available and useful we (a) file a request to keep it up as
@@ -185,7 +181,7 @@
// passing LinkProperties up to Tethering).
//
// Next TODO: return NetworkState instead of just the type.
- public int selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
+ public NetworkState selectPreferredUpstreamType(Iterable<Integer> preferredTypes) {
final TypeStatePair typeStatePair = findFirstAvailableUpstreamByType(
mNetworkMap.values(), preferredTypes);
@@ -210,7 +206,7 @@
break;
}
- return typeStatePair.type;
+ return typeStatePair.ns;
}
private void handleAvailable(int callbackType, Network network) {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 3ca65cd..e3e2658 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -74,7 +74,6 @@
import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
-import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.EventLog;
import android.util.Log;
@@ -113,6 +112,7 @@
import java.util.Objects;
import java.util.Random;
import java.util.Set;
+import java.util.function.Predicate;
/**
* Implementation details:
@@ -1761,10 +1761,7 @@
protected void dump(FileDescriptor fd, PrintWriter pw) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- dumpPendingSyncs(pw);
- dumpPeriodicSyncs(pw);
dumpSyncState(ipw);
- dumpSyncHistory(ipw);
dumpSyncAdapters(ipw);
}
@@ -1774,9 +1771,58 @@
return tobj.format("%Y-%m-%d %H:%M:%S");
}
+ private final static Comparator<SyncOperation> sOpDumpComparator = (op1, op2) -> {
+ int res = Integer.compare(op1.target.userId, op2.target.userId);
+ if (res != 0) return res;
+
+ final Comparator<String> stringComparator = String.CASE_INSENSITIVE_ORDER;
+
+ res = stringComparator.compare(op1.target.account.type, op2.target.account.type);
+ if (res != 0) return res;
+
+ res = stringComparator.compare(op1.target.account.name, op2.target.account.name);
+ if (res != 0) return res;
+
+ res = stringComparator.compare(op1.target.provider, op2.target.provider);
+ if (res != 0) return res;
+
+ res = Integer.compare(op1.reason, op2.reason);
+ if (res != 0) return res;
+
+ res = Long.compare(op1.periodMillis, op2.periodMillis);
+ if (res != 0) return res;
+
+ res = Long.compare(op1.expectedRuntime, op2.expectedRuntime);
+ if (res != 0) return res;
+
+ res = Long.compare(op1.jobId, op2.jobId);
+ if (res != 0) return res;
+
+ return 0;
+ };
+
+ private final static Comparator<SyncOperation> sOpRuntimeComparator = (op1, op2) -> {
+ int res = Long.compare(op1.expectedRuntime, op2.expectedRuntime);
+ if (res != 0) return res;
+
+ return sOpDumpComparator.compare(op1, op2);
+ };
+
+ private static <T> int countIf(Collection<T> col, Predicate<T> p) {
+ int ret = 0;
+ for (T item : col) {
+ if (p.test(item)) ret++;
+ }
+ return ret;
+ }
+
protected void dumpPendingSyncs(PrintWriter pw) {
- pw.println("Pending Syncs:");
List<SyncOperation> pendingSyncs = getAllPendingSyncs();
+
+ pw.print("Pending Syncs: ");
+ pw.println(countIf(pendingSyncs, op -> !op.isPeriodic));
+
+ Collections.sort(pendingSyncs, sOpRuntimeComparator);
int count = 0;
for (SyncOperation op: pendingSyncs) {
if (!op.isPeriodic) {
@@ -1784,13 +1830,16 @@
count++;
}
}
- pw.println("Total: " + count);
pw.println();
}
protected void dumpPeriodicSyncs(PrintWriter pw) {
- pw.println("Periodic Syncs:");
List<SyncOperation> pendingSyncs = getAllPendingSyncs();
+
+ pw.print("Periodic Syncs: ");
+ pw.println(countIf(pendingSyncs, op -> op.isPeriodic));
+
+ Collections.sort(pendingSyncs, sOpDumpComparator);
int count = 0;
for (SyncOperation op: pendingSyncs) {
if (op.isPeriodic) {
@@ -1798,11 +1847,62 @@
count++;
}
}
- pw.println("Total: " + count);
pw.println();
}
+ /**
+ * Similar to {@link android.util.TimeUtils#formatDuration}, but it's more suitable and concise
+ * for the sync manager dumpsys. (Don't add the leading + sign, don't show milliseconds.)
+ */
+ public static StringBuilder formatDurationHMS(StringBuilder sb, long duration) {
+ duration /= 1000;
+ if (duration < 0) {
+ sb.append('-');
+ duration = -duration;
+ }
+ final long seconds = duration % 60;
+ duration /= 60;
+
+ final long minutes = duration % 60;
+ duration /= 60;
+
+ final long hours = duration % 24;
+ duration /= 24;
+
+ final long days = duration;
+
+ boolean print = false;
+ if (days > 0) {
+ sb.append(days);
+ sb.append('d');
+ print = true;
+ }
+ print = printTwoDigitNumber(sb, hours, 'h', print);
+ print = printTwoDigitNumber(sb, minutes, 'm', print);
+ print = printTwoDigitNumber(sb, seconds, 's', print);
+ if (!print) {
+ sb.append("0s");
+ }
+
+ return sb;
+ }
+
+ private static boolean printTwoDigitNumber(StringBuilder sb, long value, char unit,
+ boolean always) {
+ if (!always && (value == 0)) {
+ return false;
+ }
+ if (always && (value < 10)) {
+ sb.append('0');
+ }
+ sb.append(value);
+ sb.append(unit);
+ return true;
+ }
+
protected void dumpSyncState(PrintWriter pw) {
+ final StringBuilder sb = new StringBuilder();
+
pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
pw.print("auto sync: ");
List<UserInfo> users = getAllUsers();
@@ -1828,13 +1928,16 @@
final long now = SystemClock.elapsedRealtime();
pw.print("now: "); pw.print(now);
pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
- pw.println(" (HH:MM:SS)");
- pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now / 1000));
- pw.println(" (HH:MM:SS)");
+
+ sb.setLength(0);
+ pw.print("uptime: "); pw.print(formatDurationHMS(sb, now));
+ pw.println();
pw.print("time spent syncing: ");
- pw.print(DateUtils.formatElapsedTime(
- mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000));
- pw.print(" (HH:MM:SS), sync ");
+
+ sb.setLength(0);
+ pw.print(formatDurationHMS(sb,
+ mSyncHandler.mSyncTimeTracker.timeSpentSyncing()));
+ pw.print(", sync ");
pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
pw.println("in progress");
@@ -1842,17 +1945,24 @@
pw.println("Active Syncs: " + mActiveSyncContexts.size());
final PackageManager pm = mContext.getPackageManager();
for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
- final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000;
+ final long durationInSeconds = (now - activeSyncContext.mStartTime);
pw.print(" ");
- pw.print(DateUtils.formatElapsedTime(durationInSeconds));
+ sb.setLength(0);
+ pw.print(formatDurationHMS(sb, durationInSeconds));
pw.print(" - ");
pw.print(activeSyncContext.mSyncOperation.dump(pm, false));
pw.println();
}
+ pw.println();
+
+ dumpPendingSyncs(pw);
+ dumpPeriodicSyncs(pw);
// Join the installed sync adapter with the accounts list and emit for everything.
- pw.println();
pw.println("Sync Status");
+
+ final ArrayList<Pair<EndPoint, SyncStatusInfo>> statuses = new ArrayList<>();
+
for (AccountAndUser account : accounts) {
pw.printf("Account %s u%d %s\n",
account.account.name, account.userId, account.account.type);
@@ -1872,7 +1982,7 @@
"Tot", // 9
"Time", // 10
"Last Sync", // 11
- "Etc" // 12
+ "Backoff" // 12
);
final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted =
@@ -1899,11 +2009,14 @@
account.userId));
SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first;
SyncStatusInfo status = syncAuthoritySyncStatus.second;
+ statuses.add(Pair.create(settings.target, status));
String authority = settings.target.provider;
if (authority.length() > 50) {
authority = authority.substring(authority.length() - 50);
}
table.set(row, 0, authority, settings.syncable, settings.enabled);
+
+ sb.setLength(0);
table.set(row, 4,
status.numSourceLocal,
status.numSourcePoll,
@@ -1911,7 +2024,7 @@
status.numSourceServer,
status.numSourceUser,
status.numSyncs,
- DateUtils.formatElapsedTime(status.totalElapsedTime / 1000));
+ formatDurationHMS(sb, status.totalElapsedTime));
int row1 = row;
if (settings.delayUntil > now) {
@@ -1938,6 +2051,34 @@
}
table.writeTo(pw);
}
+
+ dumpSyncHistory(pw);
+
+ pw.println();
+ pw.println("Per Adapter History");
+
+ for (int i = 0; i < statuses.size(); i++) {
+ final Pair<EndPoint, SyncStatusInfo> event = statuses.get(i);
+
+ pw.print(" ");
+ pw.print(event.first.account.name);
+ pw.print('/');
+ pw.print(event.first.account.type);
+ pw.print(" u");
+ pw.print(event.first.userId);
+ pw.print(" [");
+ pw.print(event.first.provider);
+ pw.print("]");
+ pw.println();
+
+ for (int j = 0; j < event.second.getEventCount(); j++) {
+ pw.print(" ");
+ pw.print(formatTime(event.second.getEventTime(j)));
+ pw.print(' ');
+ pw.print(event.second.getEvent(j));
+ pw.println();
+ }
+ }
}
private void dumpTimeSec(PrintWriter pw, long time) {
@@ -3403,7 +3544,7 @@
}
static class PrintTable {
- private ArrayList<Object[]> mTable = Lists.newArrayList();
+ private ArrayList<String[]> mTable = Lists.newArrayList();
private final int mCols;
PrintTable(int cols) {
@@ -3416,13 +3557,17 @@
" columns. can't set " + values.length + " at column " + col);
}
for (int i = mTable.size(); i <= row; i++) {
- final Object[] list = new Object[mCols];
+ final String[] list = new String[mCols];
mTable.add(list);
for (int j = 0; j < mCols; j++) {
list[j] = "";
}
}
- System.arraycopy(values, 0, mTable.get(row), col, values.length);
+ final String[] rowArray = mTable.get(row);
+ for (int i = 0; i < values.length; i++) {
+ final Object value = values[i];
+ rowArray[col + i] = (value == null) ? "" : value.toString();
+ }
}
void writeTo(PrintWriter out) {
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index c371f97..b46426c 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -18,10 +18,11 @@
import android.accounts.Account;
import android.app.job.JobInfo;
-import android.content.pm.PackageManager;
import android.content.ContentResolver;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
@@ -350,37 +351,46 @@
return dump(null, true);
}
- String dump(PackageManager pm, boolean useOneLine) {
+ String dump(PackageManager pm, boolean shorter) {
StringBuilder sb = new StringBuilder();
- sb.append("JobId: ").append(jobId)
- .append(", ")
+ sb.append("JobId=").append(jobId)
+ .append(" ")
.append(target.account.name)
- .append(" u")
- .append(target.userId).append(" (")
+ .append("/")
.append(target.account.type)
- .append(")")
- .append(", ")
+ .append(" u")
+ .append(target.userId)
+ .append(" [")
.append(target.provider)
- .append(", ");
+ .append("] ");
sb.append(SyncStorageEngine.SOURCES[syncSource]);
- if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
- sb.append(", EXPEDITED");
+ if (expectedRuntime != 0) {
+ sb.append(" ExpectedIn=");
+ SyncManager.formatDurationHMS(sb,
+ (expectedRuntime - SystemClock.elapsedRealtime()));
}
- sb.append(", reason: ");
+ if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
+ sb.append(" EXPEDITED");
+ }
+ sb.append(" Reason=");
sb.append(reasonToString(pm, reason));
if (isPeriodic) {
- sb.append(", period: " + periodMillis).append(", flexMillis: " + flexMillis);
+ sb.append(" (period=");
+ SyncManager.formatDurationHMS(sb, periodMillis);
+ sb.append(" flex=");
+ SyncManager.formatDurationHMS(sb, flexMillis);
+ sb.append(")");
}
- if (!useOneLine) {
- sb.append("\n ");
- sb.append("owningUid=");
+ if (!shorter) {
+ sb.append(" Owner={");
UserHandle.formatUid(sb, owningUid);
- sb.append(" owningPackage=");
+ sb.append(" ");
sb.append(owningPackage);
- }
- if (!useOneLine && !extras.keySet().isEmpty()) {
- sb.append("\n ");
- extrasToStringBuilder(extras, sb);
+ sb.append("}");
+ if (!extras.keySet().isEmpty()) {
+ sb.append(" ");
+ extrasToStringBuilder(extras, sb);
+ }
}
return sb.toString();
}
@@ -434,7 +444,7 @@
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
}
- private static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
+ static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
sb.append("[");
for (String key : bundle.keySet()) {
sb.append(key).append("=").append(bundle.get(key)).append(" ");
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index f804fa1..7b277c0 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -1183,6 +1183,16 @@
ds.failureCount++;
ds.failureTime += elapsedTime;
}
+ final StringBuilder event = new StringBuilder();
+ event.append("" + resultMessage + " Source=" + SyncStorageEngine.SOURCES[item.source]
+ + " Elapsed=");
+ SyncManager.formatDurationHMS(event, elapsedTime);
+ event.append(" Reason=");
+ event.append(SyncOperation.reasonToString(null, item.reason));
+ event.append(" Extras=");
+ SyncOperation.extrasToStringBuilder(item.extras, event);
+
+ status.addEvent(event.toString());
if (writeStatusNow) {
writeStatusLocked();
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 5d3f6f7..107475f 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -438,7 +438,21 @@
switch (message.what) {
case MSG_TIMEOUT:
synchronized (mLock) {
- handleOpTimeoutLocked();
+ if (message.obj == mRunningCallback) {
+ handleOpTimeoutLocked();
+ } else {
+ JobCallback jc = (JobCallback)message.obj;
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Ignoring timeout of no longer active job");
+ if (jc.mStoppedReason != null) {
+ sb.append(", stopped ");
+ TimeUtils.formatDuration(SystemClock.elapsedRealtime()
+ - jc.mStoppedTime, sb);
+ sb.append(" because: ");
+ sb.append(jc.mStoppedReason);
+ }
+ Slog.w(TAG, sb.toString());
+ }
}
break;
default:
@@ -621,7 +635,7 @@
private void handleOpTimeoutLocked() {
switch (mVerb) {
case VERB_BINDING:
- Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
+ Slog.w(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
", dropping.");
closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while binding");
break;
@@ -629,26 +643,28 @@
// Client unresponsive - wedged or failed to respond in time. We don't really
// know what happened so let's log it and notify the JobScheduler
// FINISHED/NO-RETRY.
- Slog.e(TAG, "No response from client for onStartJob '" +
- mRunningJob.toShortString());
+ Slog.w(TAG, "No response from client for onStartJob " +
+ mRunningJob != null ? mRunningJob.toShortString() : "<null>");
closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting");
break;
case VERB_STOPPING:
// At least we got somewhere, so fail but ask the JobScheduler to reschedule.
- Slog.e(TAG, "No response from client for onStopJob, '" +
- mRunningJob.toShortString());
+ Slog.w(TAG, "No response from client for onStopJob " +
+ mRunningJob != null ? mRunningJob.toShortString() : "<null>");
closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
break;
case VERB_EXECUTING:
// Not an error - client ran out of time.
- Slog.i(TAG, "Client timed out while executing (no jobFinished received)." +
- " sending onStop. " + mRunningJob.toShortString());
+ Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
+ "sending onStop: " +
+ mRunningJob != null ? mRunningJob.toShortString() : "<null>");
mParams.setStopReason(JobParameters.REASON_TIMEOUT);
sendStopMessageLocked("timeout while executing");
break;
default:
Slog.e(TAG, "Handling timeout for an invalid job state: " +
- mRunningJob.toShortString() + ", dropping.");
+ mRunningJob != null ? mRunningJob.toShortString() : "<null>"
+ + ", dropping.");
closeAndCleanupJobLocked(false /* needsReschedule */, "invalid timeout");
}
}
@@ -749,7 +765,7 @@
mRunningJob.getServiceComponent().getShortClassName() + "' jId: " +
mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s");
}
- Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
+ Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, mRunningCallback);
mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 029790b..5ee7ac4 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -307,11 +307,15 @@
// used as a mutex for access to all active notifications & listeners
final Object mNotificationLock = new Object();
+ @GuardedBy("mNotificationLock")
final ArrayList<NotificationRecord> mNotificationList =
new ArrayList<NotificationRecord>();
+ @GuardedBy("mNotificationLock")
final ArrayMap<String, NotificationRecord> mNotificationsByKey =
new ArrayMap<String, NotificationRecord>();
+ @GuardedBy("mNotificationLock")
final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
+ @GuardedBy("mNotificationLock")
final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
@@ -2806,7 +2810,8 @@
// Clear summary.
final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg));
if (removed != null) {
- cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
+ boolean wasPosted = removeFromNotificationListsLocked(removed);
+ cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted);
}
}
}
@@ -3420,7 +3425,8 @@
.setType(MetricsEvent.TYPE_CLOSE)
.addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
mSnoozeCriterionId == null ? 0 : 1));
- cancelNotificationLocked(r, false, REASON_SNOOZED);
+ boolean wasPosted = removeFromNotificationListsLocked(r);
+ cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted);
updateLightsLocked();
if (mSnoozeCriterionId != null) {
mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
@@ -4206,15 +4212,18 @@
manager.sendAccessibilityEvent(event);
}
+ /**
+ * Removes all NotificationsRecords with the same key as the given notification record
+ * from both lists. Do not call this method while iterating over either list.
+ */
@GuardedBy("mNotificationLock")
- private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) {
- final String canceledKey = r.getKey();
-
- // Remove from both lists, either list could have a separate Record for what is effectively
- // the same notification.
+ private boolean removeFromNotificationListsLocked(NotificationRecord r) {
+ // Remove from both lists, either list could have a separate Record for what is
+ // effectively the same notification.
boolean wasPosted = false;
NotificationRecord recordInList = null;
- if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey())) != null) {
+ if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
+ != null) {
mNotificationList.remove(recordInList);
mNotificationsByKey.remove(recordInList.sbn.getKey());
wasPosted = true;
@@ -4223,6 +4232,13 @@
!= null) {
mEnqueuedNotifications.remove(recordInList);
}
+ return wasPosted;
+ }
+
+ @GuardedBy("mNotificationLock")
+ private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
+ boolean wasPosted) {
+ final String canceledKey = r.getKey();
// Record caller.
recordCallerLocked(r);
@@ -4363,7 +4379,8 @@
}
// Cancel the notification.
- cancelNotificationLocked(r, sendDelete, reason);
+ boolean wasPosted = removeFromNotificationListsLocked(r);
+ cancelNotificationLocked(r, sendDelete, reason, wasPosted);
cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
sendDelete);
updateLightsLocked();
@@ -4440,11 +4457,11 @@
cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
- listenerName);
+ listenerName, true /* wasPosted */);
cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
flagChecker, false /*includeCurrentProfiles*/, userId,
- false /*sendDelete*/, reason, listenerName);
+ false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
mSnoozeHelper.cancel(userId, pkg);
}
}
@@ -4460,7 +4477,7 @@
private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
- boolean sendDelete, int reason, String listenerName) {
+ boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
ArrayList<NotificationRecord> canceledNotifications = null;
for (int i = notificationList.size() - 1; i >= 0; --i) {
NotificationRecord r = notificationList.get(i);
@@ -4488,8 +4505,9 @@
if (canceledNotifications == null) {
canceledNotifications = new ArrayList<>();
}
+ notificationList.remove(i);
canceledNotifications.add(r);
- cancelNotificationLocked(r, sendDelete, reason);
+ cancelNotificationLocked(r, sendDelete, reason, wasPosted);
}
if (canceledNotifications != null) {
final int M = canceledNotifications.size();
@@ -4548,11 +4566,11 @@
cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker,
includeCurrentProfiles, userId, true /*sendDelete*/, reason,
- listenerName);
+ listenerName, true);
cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null,
flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/,
- reason, listenerName);
+ reason, listenerName, false);
mSnoozeHelper.cancel(userId, includeCurrentProfiles);
}
}
@@ -4569,7 +4587,6 @@
}
String pkg = r.sbn.getPackageName();
- int userId = r.getUserId();
if (pkg == null) {
if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey());
@@ -4577,15 +4594,15 @@
}
cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName,
- sendDelete);
+ sendDelete, true);
cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid,
- listenerName, sendDelete);
+ listenerName, sendDelete, false);
}
@GuardedBy("mNotificationLock")
private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
NotificationRecord parentNotification, int callingUid, int callingPid,
- String listenerName, boolean sendDelete) {
+ String listenerName, boolean sendDelete, boolean wasPosted) {
final String pkg = parentNotification.sbn.getPackageName();
final int userId = parentNotification.getUserId();
final int reason = REASON_GROUP_SUMMARY_CANCELED;
@@ -4597,7 +4614,8 @@
&& (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
childSbn.getTag(), userId, 0, 0, reason, listenerName);
- cancelNotificationLocked(childR, sendDelete, reason);
+ notificationList.remove(i);
+ cancelNotificationLocked(childR, sendDelete, reason, wasPosted);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 8952870..6953ffd 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,6 +25,7 @@
import android.app.NotificationChannel;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -170,6 +171,11 @@
private Uri calculateSound() {
final Notification n = sbn.getNotification();
+ // No notification sounds on tv
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+ return null;
+ }
+
Uri sound = mChannel.getSound();
if (mPreChannelsNotification && (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_SOUND) == 0) {
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 2c0cc95..d7b36aa 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -53,6 +53,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -524,12 +525,11 @@
if (r == null) {
throw new IllegalArgumentException("Invalid package");
}
- LogMaker lm = new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL_GROUP)
- .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
- .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
- group.getId())
- .setPackageName(pkg);
- MetricsLogger.action(lm);
+ final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
+ if (!group.equals(oldGroup)) {
+ // will log for new entries as well as name changes
+ MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
+ }
r.groups.put(group.getId(), group);
updateConfig();
}
@@ -557,13 +557,16 @@
if (existing != null && fromTargetApp) {
if (existing.isDeleted()) {
existing.setDeleted(false);
+
+ // log a resurrected channel as if it's new again
+ MetricsLogger.action(getChannelLog(channel, pkg).setType(
+ MetricsProto.MetricsEvent.TYPE_OPEN));
}
existing.setName(channel.getName().toString());
existing.setDescription(channel.getDescription());
existing.setBlockableSystem(channel.isBlockableSystem());
- MetricsLogger.action(getChannelLog(channel, pkg));
updateConfig();
return;
}
@@ -621,7 +624,10 @@
r.showBadge = updatedChannel.canShowBadge();
}
- MetricsLogger.action(getChannelLog(updatedChannel, pkg));
+ if (!channel.equals(updatedChannel)) {
+ // only log if there are real changes
+ MetricsLogger.action(getChannelLog(updatedChannel, pkg));
+ }
updateConfig();
}
@@ -1140,6 +1146,14 @@
channel.getImportance());
}
+ private LogMaker getChannelGroupLog(String groupId, String pkg) {
+ return new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL_GROUP)
+ .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
+ .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
+ groupId)
+ .setPackageName(pkg);
+ }
+
public void updateBadgingEnabled() {
if (mBadgingEnabled == null) {
mBadgingEnabled = new SparseBooleanArray();
@@ -1186,6 +1200,6 @@
boolean showBadge = DEFAULT_SHOW_BADGE;
ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
- ArrayMap<String, NotificationChannelGroup> groups = new ArrayMap<>();
+ Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index ef3e7bc..2940a6e 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -714,11 +714,17 @@
final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size());
synchronized (mLock) {
+ final List<String> frameworkOverlays =
+ mImpl.getEnabledOverlayPackageNames("android", userId);
final int N = targetPackageNames.size();
for (int i = 0; i < N; i++) {
final String targetPackageName = targetPackageNames.get(i);
- pendingChanges.put(targetPackageName,
- mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+ List<String> list = new ArrayList<>();
+ if (!"android".equals(targetPackageName)) {
+ list.addAll(frameworkOverlays);
+ }
+ list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+ pendingChanges.put(targetPackageName, list);
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 261bcc5..db6e974 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -170,6 +170,7 @@
final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
updateAllOverlaysForTarget(packageName, userId, targetPackage);
+ mListener.onOverlaysChanged(packageName, userId);
}
void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
@@ -178,7 +179,9 @@
}
final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
- updateAllOverlaysForTarget(packageName, userId, targetPackage);
+ if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
@@ -186,7 +189,9 @@
Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId);
}
- updateAllOverlaysForTarget(packageName, userId, null);
+ if (updateAllOverlaysForTarget(packageName, userId, null)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) {
@@ -195,7 +200,9 @@
}
final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
- updateAllOverlaysForTarget(packageName, userId, targetPackage);
+ if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+ mListener.onOverlaysChanged(packageName, userId);
+ }
}
void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f5808af..5c54ba8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -862,8 +862,15 @@
mResolvedInstructionSets.add(archSubDir.getName());
List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
- if (!oatFiles.isEmpty()) {
- mResolvedInheritedFiles.addAll(oatFiles);
+
+ // Only add compiled files associated with the base.
+ // Once b/62269291 is resolved, we can add all compiled files again.
+ for (File oatFile : oatFiles) {
+ if (oatFile.getName().equals("base.art")
+ || oatFile.getName().equals("base.odex")
+ || oatFile.getName().equals("base.vdex")) {
+ mResolvedInheritedFiles.add(oatFile);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d5b06ec..682d666 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18164,8 +18164,10 @@
// step during installation. Instead, we'll take extra time the first time the
// instant app starts. It's preferred to do it this way to provide continuous
// progress to the user instead of mysteriously blocking somewhere in the
- // middle of running an instant app.
- if (!instantApp) {
+ // middle of running an instant app. The default behaviour can be overridden
+ // via gservices.
+ if (!instantApp || Global.getInt(
+ mContext.getContentResolver(), Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
// Do not run PackageDexOptimizer through the local performDexOpt
// method because `pkg` may not be in `mPackages` yet.
@@ -21654,8 +21656,7 @@
public static final int DUMP_FROZEN = 1 << 19;
public static final int DUMP_DEXOPT = 1 << 20;
public static final int DUMP_COMPILER_STATS = 1 << 21;
- public static final int DUMP_ENABLED_OVERLAYS = 1 << 22;
- public static final int DUMP_CHANGES = 1 << 23;
+ public static final int DUMP_CHANGES = 1 << 22;
public static final int OPTION_SHOW_FILTERS = 1 << 0;
@@ -21899,8 +21900,6 @@
dumpState.setDump(DumpState.DUMP_DEXOPT);
} else if ("compiler-stats".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
- } else if ("enabled-overlays".equals(cmd)) {
- dumpState.setDump(DumpState.DUMP_ENABLED_OVERLAYS);
} else if ("changes".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_CHANGES);
} else if ("write".equals(cmd)) {
@@ -24680,12 +24679,7 @@
}
final PackageSetting ps = mSettings.mPackages.get(targetPackageName);
- String[] frameworkOverlayPaths = null;
- if (!"android".equals(targetPackageName)) {
- frameworkOverlayPaths =
- mSettings.mPackages.get("android").getOverlayPaths(userId);
- }
- ps.setOverlayPaths(overlayPaths, frameworkOverlayPaths, userId);
+ ps.setOverlayPaths(overlayPaths, userId);
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index d17267f..f685127 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -330,21 +330,9 @@
modifyUserState(userId).installReason = installReason;
}
- void setOverlayPaths(List<String> overlayPaths, String[] frameworkOverlayPaths, int userId) {
- if (overlayPaths == null && frameworkOverlayPaths == null) {
- modifyUserState(userId).overlayPaths = null;
- return;
- }
- final List<String> paths;
- if (frameworkOverlayPaths == null) {
- paths = overlayPaths;
- } else {
- paths = Lists.newArrayList(frameworkOverlayPaths);
- if (overlayPaths != null) {
- paths.addAll(overlayPaths);
- }
- }
- modifyUserState(userId).overlayPaths = paths.toArray(new String[paths.size()]);
+ void setOverlayPaths(List<String> overlayPaths, int userId) {
+ modifyUserState(userId).overlayPaths = overlayPaths == null ? null :
+ overlayPaths.toArray(new String[overlayPaths.size()]);
}
String[] getOverlayPaths(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b006c2d..45d0c58 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4861,9 +4861,9 @@
String[] overlayPaths = ps.getOverlayPaths(user.id);
if (overlayPaths != null && overlayPaths.length > 0) {
- pw.println("Overlay paths:");
+ pw.print(prefix); pw.println(" overlay paths:");
for (String path : overlayPaths) {
- pw.println(path);
+ pw.print(prefix); pw.print(" "); pw.println(path);
}
}
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index 7a28081..b179235 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -166,8 +166,14 @@
return change || stateChanged;
}
- void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener) {
+ void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener,
+ boolean invokeWithState) {
mVisibilityChangeListener = listener;
+ if (invokeWithState) {
+ // Optionally report the initial window state for initialization purposes
+ mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED,
+ (mState == StatusBarManager.WINDOW_STATE_SHOWING) ? 1 : 0, 0).sendToTarget();
+ }
}
protected boolean skipAnimation() {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 126e3ec..5c6521b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1033,7 +1033,7 @@
new BarController.OnBarVisibilityChangedListener() {
@Override
public void onBarVisibilityChanged(boolean visible) {
- mAccessibilityManager.notifyAccessibilityButtonAvailabilityChanged(visible);
+ mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible);
}
};
@@ -3017,7 +3017,7 @@
mNavigationBar = win;
mNavigationBarController.setWindow(win);
mNavigationBarController.setOnBarVisibilityChangedListener(
- mNavBarVisibilityListener);
+ mNavBarVisibilityListener, true);
if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
break;
case TYPE_NAVIGATION_BAR_PANEL:
@@ -7713,6 +7713,8 @@
return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
default:
return null;
}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b937f9d..55d4719 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -24,9 +24,12 @@
import android.app.Vr2dDisplayProperties;
import android.app.NotificationManager;
import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -39,6 +42,7 @@
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
@@ -140,7 +144,13 @@
private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager();
/** Tracks the state of the screen and keyguard UI.*/
private int mSystemSleepFlags = FLAG_AWAKE;
+ /**
+ * Set when ACTION_USER_UNLOCKED is fired. We shouldn't try to bind to the
+ * vr service before then.
+ */
+ private boolean mUserUnlocked;
private Vr2dDisplay mVr2dDisplay;
+ private boolean mBootsToVr;
private static final int MSG_VR_STATE_CHANGE = 0;
private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
@@ -152,15 +162,20 @@
* If VR mode is not allowed to be enabled, calls to set VR mode will be cached. When VR mode
* is again allowed to be enabled, the most recent cached state will be applied.
*
- * @param allowed {@code true} if calling any of the setVrMode methods may cause the device to
- * enter VR mode.
*/
- private void setVrModeAllowedLocked(boolean allowed) {
+ private void updateVrModeAllowedLocked() {
+ boolean allowed = mSystemSleepFlags == FLAG_ALL && mUserUnlocked;
if (mVrModeAllowed != allowed) {
mVrModeAllowed = allowed;
if (DBG) Slog.d(TAG, "VR mode is " + ((allowed) ? "allowed" : "disallowed"));
if (mVrModeAllowed) {
+ if (mBootsToVr) {
+ setPersistentVrModeEnabled(true);
+ }
consumeAndApplyPendingStateLocked();
+ if (mBootsToVr && !mVrModeEnabled) {
+ setVrMode(true, mDefaultVrService, 0, null);
+ }
} else {
// Disable persistent mode when VR mode isn't allowed, allows an escape hatch to
// exit persistent VR mode when screen is turned off.
@@ -187,7 +202,7 @@
mSystemSleepFlags &= ~FLAG_AWAKE;
}
- setVrModeAllowedLocked(mSystemSleepFlags == FLAG_ALL);
+ updateVrModeAllowedLocked();
}
}
@@ -198,7 +213,14 @@
} else {
mSystemSleepFlags &= ~FLAG_SCREEN_ON;
}
- setVrModeAllowedLocked(mSystemSleepFlags == FLAG_ALL);
+ updateVrModeAllowedLocked();
+ }
+ }
+
+ private void setUserUnlocked() {
+ synchronized(mLock) {
+ mUserUnlocked = true;
+ updateVrModeAllowedLocked();
}
}
@@ -563,6 +585,7 @@
mContext = getContext();
}
+ mBootsToVr = SystemProperties.getBoolean("ro.boot.vr", false);
publishLocalService(VrManagerInternal.class, new LocalService());
publishBinderService(Context.VR_SERVICE, mVrManager.asBinder());
}
@@ -597,10 +620,17 @@
ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
mVr2dDisplay = new Vr2dDisplay(dm, ami, mVrManager);
mVr2dDisplay.init(getContext());
- } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
- synchronized (mLock) {
- mVrModeAllowed = true;
- }
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
+ getContext().registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+ VrManagerService.this.setUserUnlocked();
+ }
+ }
+ }, intentFilter);
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 71c92b8..929f28d 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -52,7 +52,6 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
-import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
@@ -92,7 +91,6 @@
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.graphics.palette.Palette;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
@@ -168,7 +166,6 @@
static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
static final String WALLPAPER_LOCK_CROP = "wallpaper_lock";
static final String WALLPAPER_INFO = "wallpaper_info.xml";
- static final int MAX_WALLPAPER_EXTRACTION_AREA = 112 * 112;
// All the various per-user state files we need to be aware of
static final String[] sPerUserFiles = new String[] {
@@ -397,8 +394,9 @@
// It prevents color extraction on big bitmaps.
int wallpaperId = -1;
+ boolean imageWallpaper = false;
synchronized (mLock) {
- final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
+ imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
|| wallpaper.wallpaperComponent == null;
if (imageWallpaper) {
if (wallpaper.cropFile != null && wallpaper.cropFile.exists()) {
@@ -422,45 +420,33 @@
wallpaperId = wallpaper.wallpaperId;
}
- Bitmap bitmap = null;
+ WallpaperColors colors = null;
if (cropFile != null) {
- bitmap = BitmapFactory.decodeFile(cropFile);
+ Bitmap bitmap = BitmapFactory.decodeFile(cropFile);
+ colors = WallpaperColors.fromBitmap(bitmap);
+ bitmap.recycle();
} else if (thumbnail != null) {
- // Calculate how big the bitmap needs to be.
- // This avoids unnecessary processing and allocation inside Palette.
- final int requestedArea = thumbnail.getIntrinsicWidth() *
- thumbnail.getIntrinsicHeight();
- double scale = 1;
- if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
- scale = Math.sqrt(MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea);
- }
- bitmap = Bitmap.createBitmap((int) (thumbnail.getIntrinsicWidth() * scale),
- (int) (thumbnail.getIntrinsicHeight() * scale), Bitmap.Config.ARGB_8888);
- final Canvas bmpCanvas = new Canvas(bitmap);
- thumbnail.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
- thumbnail.draw(bmpCanvas);
+ colors = WallpaperColors.fromDrawable(thumbnail);
}
- if (bitmap == null) {
+ if (colors == null) {
Slog.w(TAG, "Cannot extract colors because wallpaper could not be read.");
return;
}
- Palette palette = Palette
- .from(bitmap)
- .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA)
- .generate();
- bitmap.recycle();
-
- final List<Pair<Color, Integer>> colors = new ArrayList<>();
- for (Palette.Swatch swatch : palette.getSwatches()) {
- colors.add(new Pair<>(Color.valueOf(swatch.getRgb()),
- swatch.getPopulation()));
+ // Even though we can extract colors from live wallpaper thumbnails,
+ // it's risky to assume that it might support dark text on top of it:
+ // • Thumbnail might not be accurate.
+ // • Colors might change over time.
+ if (!imageWallpaper) {
+ int colorHints = colors.getColorHints();
+ colorHints &= ~WallpaperColors.HINT_SUPPORTS_DARK_TEXT;
+ colors.setColorHints(colorHints);
}
synchronized (mLock) {
if (wallpaper.wallpaperId == wallpaperId) {
- wallpaper.primaryColors = new WallpaperColors(colors);
+ wallpaper.primaryColors = colors;
} else {
Slog.w(TAG, "Not setting primary colors since wallpaper changed");
}
@@ -2224,17 +2210,16 @@
}
if (wallpaper.primaryColors != null) {
- int colorsCount = wallpaper.primaryColors.getColors().size();
+ int colorsCount = wallpaper.primaryColors.getMainColors().size();
out.attribute(null, "colorsCount", Integer.toString(colorsCount));
if (colorsCount > 0) {
for (int i = 0; i < colorsCount; i++) {
- Pair<Color, Integer> wc = wallpaper.primaryColors.getColors().get(i);
- out.attribute(null, "colorValue"+i, Integer.toString(wc.first.toArgb()));
- out.attribute(null, "colorWeight"+i, Integer.toString(wc.second));
+ final Color wc = wallpaper.primaryColors.getMainColors().get(i);
+ out.attribute(null, "colorValue"+i, Integer.toString(wc.toArgb()));
}
}
out.attribute(null, "supportsDarkText",
- Integer.toString(wallpaper.primaryColors.supportsDarkText() ? 1 : 0));
+ Integer.toString(wallpaper.primaryColors.getColorHints()));
}
out.attribute(null, "name", wallpaper.name);
@@ -2469,15 +2454,22 @@
wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
int colorsCount = getAttributeInt(parser, "colorsCount", 0);
if (colorsCount > 0) {
- List<Pair<Color, Integer>> colors = new ArrayList<>();
+ Color primary = null, secondary = null, tertiary = null;
+ final List<Color> colors = new ArrayList<>();
for (int i = 0; i < colorsCount; i++) {
- colors.add(new Pair<>(
- Color.valueOf(getAttributeInt(parser, "colorValue"+i, 0)),
- getAttributeInt(parser, "colorWeight"+i, 0)
- ));
+ Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0));
+ if (i == 0) {
+ primary = color;
+ } else if (i == 1) {
+ secondary = color;
+ } else if (i == 2) {
+ tertiary = color;
+ } else {
+ break;
+ }
}
- boolean dark = getAttributeInt(parser, "supportsDarkText", 0) == 1;
- wallpaper.primaryColors = new WallpaperColors(colors, dark);
+ int colorHints = getAttributeInt(parser, "colorHints", 0);
+ wallpaper.primaryColors = new WallpaperColors(primary, secondary, tertiary, colorHints);
}
wallpaper.name = parser.getAttributeValue(null, "name");
wallpaper.allowBackup = "true".equals(parser.getAttributeValue(null, "backup"));
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index f138add..78f2195 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MAGNIFICATION_REGION_EFFECT;
+
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -418,13 +421,7 @@
public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
if (spec != null && !spec.isNop()) {
- WindowManagerPolicy policy = mWindowManagerService.mPolicy;
- final int windowType = windowState.mAttrs.type;
- if (!policy.isTopLevelWindow(windowType) && windowState.isChildWindow()
- && !policy.canMagnifyWindow(windowType)) {
- return null;
- }
- if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
+ if (!mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
return null;
}
}
@@ -540,8 +537,9 @@
final int visibleWindowCount = visibleWindows.size();
for (int i = visibleWindowCount - 1; i >= 0; i--) {
WindowState windowState = visibleWindows.valueAt(i);
- if (windowState.mAttrs.type == WindowManager
- .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
+ if ((windowState.mAttrs.type == TYPE_MAGNIFICATION_OVERLAY)
+ || ((windowState.mAttrs.privateFlags
+ & PRIVATE_FLAG_NO_MAGNIFICATION_REGION_EFFECT) != 0)) {
continue;
}
@@ -715,7 +713,7 @@
mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
.getLayerStack());
mSurfaceControl.setLayer(mWindowManagerService.mPolicy.getWindowLayerFromTypeLw(
- WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
+ TYPE_MAGNIFICATION_OVERLAY)
* WindowManagerService.TYPE_LAYER_MULTIPLIER);
mSurfaceControl.setPosition(0, 0);
mSurface.copyFrom(mSurfaceControl);
@@ -1313,7 +1311,7 @@
&& windowType != WindowManager.LayoutParams.TYPE_DRAG
&& windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
&& windowType != WindowManager.LayoutParams.TYPE_POINTER
- && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
+ && windowType != TYPE_MAGNIFICATION_OVERLAY
&& windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
&& windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
&& windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c1c72ca..b1ed358 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -2044,7 +2044,10 @@
if (forceOverride || isKeyguardTransit(transit) || !isTransitionSet()
|| mNextAppTransition == TRANSIT_NONE) {
setAppTransition(transit, flags);
- } else if (!alwaysKeepCurrent) {
+ }
+ // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
+ // relies on the fact that we always execute a Keyguard transition after preparing one.
+ else if (!alwaysKeepCurrent && !isKeyguardTransit(transit)) {
if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) {
// Opening a new task always supersedes a close for the anim.
setAppTransition(transit, flags);
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 9a9e29a..6d33ce2 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -155,6 +155,10 @@
mSnapAlgorithm = new PipSnapAlgorithm(service.mContext);
mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
reloadResources();
+ // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
+ // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
+ // triggers a configuration change and the resources to be reloaded.
+ mAspectRatio = mDefaultAspectRatio;
}
void onConfigurationChanged() {
@@ -171,7 +175,6 @@
mCurrentMinSize = mDefaultMinSize;
mDefaultAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
- mAspectRatio = mDefaultAspectRatio;
final String screenEdgeInsetsDpString = res.getString(
com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets);
final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 17e66e2..0c2ca85 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3045,6 +3045,10 @@
return mPolicy.isKeyguardLocked();
}
+ public boolean isKeyguardShowingAndNotOccluded() {
+ return mPolicy.isKeyguardShowingAndNotOccluded();
+ }
+
@Override
public boolean isKeyguardSecure() {
int userId = UserHandle.getCallingUserId();
@@ -7310,6 +7314,11 @@
}
@Override
+ public boolean isKeyguardShowingAndNotOccluded() {
+ return WindowManagerService.this.isKeyguardShowingAndNotOccluded();
+ }
+
+ @Override
public void showGlobalActions() {
WindowManagerService.this.showGlobalActions();
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 4442bb8..82c862f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -25,6 +25,7 @@
import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE;
import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_OPEN;
+import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -239,7 +240,7 @@
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
int transit = mService.mAppTransition.getAppTransition();
- if (mService.mSkipAppTransitionAnimation) {
+ if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
transit = AppTransition.TRANSIT_UNSET;
}
mService.mSkipAppTransitionAnimation = false;
@@ -598,42 +599,47 @@
+ ", openingApps=" + openingApps
+ ", closingApps=" + closingApps);
mService.mAnimateWallpaperWithTarget = false;
- if (closingAppHasWallpaper && openingAppHasWallpaper) {
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
- switch (transit) {
- case TRANSIT_ACTIVITY_OPEN:
- case TRANSIT_TASK_OPEN:
- case TRANSIT_TASK_TO_FRONT:
- transit = TRANSIT_WALLPAPER_INTRA_OPEN;
- break;
- case TRANSIT_ACTIVITY_CLOSE:
- case TRANSIT_TASK_CLOSE:
- case TRANSIT_TASK_TO_BACK:
- transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
- break;
- }
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
- "New transit: " + AppTransition.appTransitionToString(transit));
- } else if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
+ if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New transit: " + AppTransition.appTransitionToString(transit));
- } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty()
- && !openingApps.contains(oldWallpaper.mAppToken)
- && closingApps.contains(oldWallpaper.mAppToken)) {
- // We are transitioning from an activity with a wallpaper to one without.
- transit = TRANSIT_WALLPAPER_CLOSE;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
- + AppTransition.appTransitionToString(transit));
- } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() &&
- openingApps.contains(wallpaperTarget.mAppToken)) {
- // We are transitioning from an activity without
- // a wallpaper to now showing the wallpaper
- transit = TRANSIT_WALLPAPER_OPEN;
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
- + AppTransition.appTransitionToString(transit));
- } else {
- mService.mAnimateWallpaperWithTarget = true;
+ }
+ // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
+ // relies on the fact that we always execute a Keyguard transition after preparing one.
+ else if (!isKeyguardGoingAwayTransit(transit)) {
+ if (closingAppHasWallpaper && openingAppHasWallpaper) {
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
+ switch (transit) {
+ case TRANSIT_ACTIVITY_OPEN:
+ case TRANSIT_TASK_OPEN:
+ case TRANSIT_TASK_TO_FRONT:
+ transit = TRANSIT_WALLPAPER_INTRA_OPEN;
+ break;
+ case TRANSIT_ACTIVITY_CLOSE:
+ case TRANSIT_TASK_CLOSE:
+ case TRANSIT_TASK_TO_BACK:
+ transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
+ break;
+ }
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
+ "New transit: " + AppTransition.appTransitionToString(transit));
+ } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty()
+ && !openingApps.contains(oldWallpaper.mAppToken)
+ && closingApps.contains(oldWallpaper.mAppToken)) {
+ // We are transitioning from an activity with a wallpaper to one without.
+ transit = TRANSIT_WALLPAPER_CLOSE;
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
+ + AppTransition.appTransitionToString(transit));
+ } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() &&
+ openingApps.contains(wallpaperTarget.mAppToken)) {
+ // We are transitioning from an activity without
+ // a wallpaper to now showing the wallpaper
+ transit = TRANSIT_WALLPAPER_OPEN;
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
+ + AppTransition.appTransitionToString(transit));
+ } else {
+ mService.mAnimateWallpaperWithTarget = true;
+ }
}
return transit;
}
diff --git a/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp b/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
index 241ccf6..4e5c27f 100644
--- a/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
+++ b/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
@@ -71,7 +71,7 @@
// auto-close it (otherwise there would be double-close problems).
//
// Rely upon the compiler to eliminate the constexprs used for clarity.
-hidl_handle&& handleFromFileDescriptor(base::unique_fd fd) {
+hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
hidl_handle h;
NATIVE_HANDLE_DECLARE_STORAGE(storage, 0, 0);
@@ -83,7 +83,7 @@
static constexpr bool kTakeOwnership = true;
h.setTo(nh, kTakeOwnership);
- return std::move(h);
+ return h;
}
} // namespace
@@ -116,13 +116,14 @@
bool rval;
hidl_string msg;
- configInterface->setHandles(h1, h2,
+ const auto status = configInterface->setHandles(h1, h2,
[&rval, &msg](bool success, const hidl_string& errMsg) {
rval = success;
msg = errMsg;
});
- if (!rval) {
- ALOGE("IOffloadConfig::setHandles() error: %s", msg.c_str());
+ if (!status.isOk() || !rval) {
+ ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
+ status.description().c_str(), msg.c_str());
}
return rval;
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 75df892..5770c50 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -159,10 +159,12 @@
readInstalledPrintServicesLocked();
upgradePersistentStateIfNeeded();
readDisabledPrintServicesLocked();
+ }
- // Some print services might have gotten installed before the User State came up
- prunePrintServices();
+ // Some print services might have gotten installed before the User State came up
+ prunePrintServices();
+ synchronized (mLock) {
onConfigurationChangedLocked();
}
}
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index ec08874..ae98274 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -20,40 +20,9 @@
import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
-import com.android.server.lights.Light;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.app.ActivityManager;
-import android.app.Notification;
-import android.app.Notification.Builder;
-import android.app.NotificationManager;
-import android.app.NotificationChannel;
-import android.graphics.Color;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.Vibrator;
-import android.os.VibrationEffect;
-import android.provider.Settings;
-import android.service.notification.StatusBarNotification;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
@@ -61,11 +30,43 @@
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.lights.Light;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BuzzBeepBlinkTest extends NotificationTestCase {
@@ -163,6 +164,11 @@
true /* noisy */, false /* buzzy*/, false /* lights */);
}
+ private NotificationRecord getInsistentBeepyLeanbackNotification() {
+ return getLeanbackNotificationRecord(mId, true /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
private NotificationRecord getBuzzyNotification() {
return getNotificationRecord(mId, false /* insistent */, false /* once */,
false /* noisy */, true /* buzzy*/, false /* lights */);
@@ -192,23 +198,30 @@
return getNotificationRecord(mId, false /* insistent */, true /* once */,
false /* noisy */, true /* buzzy*/, true /* lights */,
true /* defaultVibration */, true /* defaultSound */, false /* defaultLights */,
- null, Notification.GROUP_ALERT_ALL);
+ null, Notification.GROUP_ALERT_ALL, false);
}
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights) {
return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
- null, Notification.GROUP_ALERT_ALL);
+ null, Notification.GROUP_ALERT_ALL, false);
+ }
+
+ private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, boolean once,
+ boolean noisy, boolean buzzy, boolean lights) {
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
+ null, Notification.GROUP_ALERT_ALL, true);
}
private NotificationRecord getBeepyNotificationRecord(String groupKey, int groupAlertBehavior) {
return getNotificationRecord(mId, false, false, true, false, false, true, true, true,
- groupKey, groupAlertBehavior);
+ groupKey, groupAlertBehavior, false);
}
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
- boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior) {
+ boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
+ boolean isLeanback) {
NotificationChannel channel =
new NotificationChannel("test", "test", IMPORTANCE_HIGH);
final Builder builder = new Builder(getContext())
@@ -257,9 +270,15 @@
n.flags |= Notification.FLAG_INSISTENT;
}
+ Context context = spy(getContext());
+ PackageManager packageManager = spy(context.getPackageManager());
+ when(context.getPackageManager()).thenReturn(packageManager);
+ when(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+ .thenReturn(isLeanback);
+
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+ NotificationRecord r = new NotificationRecord(context, sbn, channel);
mService.addNotification(r);
return r;
}
@@ -367,6 +386,15 @@
}
@Test
+ public void testNoLeanbackBeep() throws Exception {
+ NotificationRecord r = getInsistentBeepyLeanbackNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ }
+
+ @Test
public void testNoInterruptionForMin() throws Exception {
NotificationRecord r = getBeepyNotification();
r.setImportance(NotificationManager.IMPORTANCE_MIN, "foo");
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 0a4cb10..6090e35 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -359,6 +359,43 @@
}
@Test
+ public void testCancelAllNotificationsMultipleEnqueuedDoesNotCrash() throws Exception {
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ for (int i = 0; i < 10; i++) {
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ }
+ mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
+ waitForIdle();
+ }
+
+ @Test
+ public void testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash() throws Exception {
+ final NotificationRecord parent = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group1", true);
+ final NotificationRecord parentAsChild = generateNotificationRecord(
+ mTestNotificationChannel, 1, "group1", false);
+ final NotificationRecord child = generateNotificationRecord(
+ mTestNotificationChannel, 2, "group1", false);
+
+ // fully post parent notification
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
+ waitForIdle();
+
+ // enqueue the child several times
+ for (int i = 0; i < 10; i++) {
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
+ }
+ // make the parent a child, which will cancel the child notification
+ mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(),
+ parentAsChild.sbn.getUserId());
+ waitForIdle();
+ }
+
+ @Test
public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
diff --git a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
index 07280bc..62b0ca8 100644
--- a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
@@ -21,16 +21,22 @@
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
+import android.os.Looper;
import android.os.UserHandle;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
import com.android.server.content.ContentService.ObserverCall;
import com.android.server.content.ContentService.ObserverNode;
+/**
+ * bit FrameworksServicesTests:com.android.server.content.ObserverNodeTest
+ */
+@SmallTest
public class ObserverNodeTest extends AndroidTestCase {
static class TestObserver extends ContentObserver {
public TestObserver() {
- super(new Handler());
+ super(new Handler(Looper.getMainLooper()));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
index be6861c..d093e79 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java
@@ -1,9 +1,32 @@
+/*
+ * 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.content;
import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
+/**
+ * Tests for SyncManager.
+ *
+ * bit FrameworksServicesTests:com.android.server.content.SyncManagerTest
+ */
+@SmallTest
public class SyncManagerTest extends TestCase {
final String KEY_1 = "key_1";
@@ -61,4 +84,42 @@
assertFalse("Extras considered equal when they are different.",
SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */));
}
+
+ public void testFormatDurationHMS() {
+ checkFormatDurationHMS("0s", 0, 0, 0, 0);
+ checkFormatDurationHMS("1s", 0, 0, 0, 1);
+ checkFormatDurationHMS("9s", 0, 0, 0, 9);
+ checkFormatDurationHMS("10s", 0, 0, 0, 10);
+ checkFormatDurationHMS("59s", 0, 0, 0, 59);
+ checkFormatDurationHMS("1m00s", 0, 0, 1, 0);
+ checkFormatDurationHMS("1m01s", 0, 0, 1, 1);
+ checkFormatDurationHMS("1m09s", 0, 0, 1, 9);
+ checkFormatDurationHMS("1m10s", 0, 0, 1, 10);
+ checkFormatDurationHMS("1m59s", 0, 0, 1, 59);
+ checkFormatDurationHMS("1h00m00s", 0, 1, 0, 0);
+ checkFormatDurationHMS("1h00m01s", 0, 1, 0, 1);
+ checkFormatDurationHMS("1h01m01s", 0, 1, 1, 1);
+ checkFormatDurationHMS("1h09m10s", 0, 1, 9, 10);
+ checkFormatDurationHMS("1h10m59s", 0, 1, 10, 59);
+ checkFormatDurationHMS("1h59m00s", 0, 1, 59, 0);
+
+ checkFormatDurationHMS("1d00h00m00s", 1, 0, 0, 0);
+ checkFormatDurationHMS("1d00h00m00s", 1, 0, 0, 0);
+ checkFormatDurationHMS("1d01h00m00s", 1, 1, 0, 0);
+ checkFormatDurationHMS("1d09h00m00s", 1, 9, 0, 0);
+ checkFormatDurationHMS("1d10h00m00s", 1, 10, 0, 0);
+ checkFormatDurationHMS("1d23h00m00s", 1, 23, 0, 0);
+ checkFormatDurationHMS("123d01h00m00s", 123, 1, 0, 0);
+
+ final StringBuilder sb = new StringBuilder();
+ assertEquals("-1m01s", SyncManager.formatDurationHMS(sb, -61000L).toString());
+ }
+
+ private void checkFormatDurationHMS(String expected,
+ int d, int h, int m, int s) {
+ final long time = (d * 24 * 3600) + (h * 3600) + (m * 60) + s;
+
+ final StringBuilder sb = new StringBuilder();
+ assertEquals(expected, SyncManager.formatDurationHMS(sb, time * 1000).toString());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index e45b92a..deaa34c 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -17,24 +17,17 @@
package com.android.server.content;
import android.accounts.Account;
-import android.content.ContentResolver;
-import android.content.Context;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.os.SystemClock;
-import android.provider.Settings;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
/**
- * You can run those tests with:
+ * Test for SyncOperation.
*
- * adb shell am instrument
- * -e debug false
- * -w
- * -e class android.content.SyncOperationTest com.android.frameworks.coretests/android.test.InstrumentationTestRunner
+ * bit FrameworksServicesTests:com.android.server.content.SyncOperationTest
*/
-
+@SmallTest
public class SyncOperationTest extends AndroidTestCase {
Account mDummy;
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
index 91c0de6..85de1f1 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
-import android.content.PeriodicSync;
import android.content.res.Resources;
import android.os.Bundle;
import android.test.AndroidTestCase;
@@ -33,14 +32,18 @@
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
-import com.android.server.content.SyncStorageEngine.EndPoint;
-
import com.android.internal.os.AtomicFile;
import java.io.File;
import java.io.FileOutputStream;
-import java.util.List;
+/**
+ * Test for SyncStorageEngine.
+ *
+ * bit FrameworksServicesTests:com.android.server.content.SyncStorageEngineTest
+ *
+ * TODO Broken. Fix it. b/62485315
+ */
public class SyncStorageEngineTest extends AndroidTestCase {
protected Account account1;
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 4390fae..31bb064 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -76,6 +76,13 @@
public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING =
"android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
+ /**
+ * Indicating flag for phone account whether to use voip audio mode for voip calls
+ * @hide
+ */
+ public static final String EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE =
+ "android.telecom.extra.ALWAYS_USE_VOIP_AUDIO_MODE";
+
/**
* Boolean {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which
* indicates whether this {@link PhoneAccount} is capable of supporting a request to handover a
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
index 0cb4cff..f15fde8 100644
--- a/telephony/java/android/telephony/NetworkScan.java
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -34,12 +34,26 @@
public static final String TAG = "NetworkScan";
- public static final int SUCCESS = 0;
- public static final int ERROR_INVALID_SCAN = 1;
- public static final int ERROR_UNSUPPORTED = 2;
- public static final int ERROR_INTERRUPTED = 3;
- public static final int ERROR_CANCELLED = 4;
+ // Below errors are mapped from RadioError which is returned from RIL. We will consolidate
+ // RadioErrors during the mapping if those RadioErrors mean no difference to the users.
+ public static final int SUCCESS = 0; // RadioError:NONE
+ public static final int ERROR_MODEM_ERROR = 1; // RadioError:RADIO_NOT_AVAILABLE
+ // RadioError:NO_MEMORY
+ // RadioError:INTERNAL_ERR
+ // RadioError:MODEM_ERR
+ // RadioError:OPERATION_NOT_ALLOWED
+ public static final int ERROR_INVALID_SCAN = 2; // RadioError:INVALID_ARGUMENTS
+ public static final int ERROR_MODEM_BUSY = 3; // RadioError:DEVICE_IN_USE
+ public static final int ERROR_UNSUPPORTED = 4; // RadioError:REQUEST_NOT_SUPPORTED
+ // Below errors are generated at the Telephony.
+ public static final int ERROR_RIL_ERROR = 10000; // Nothing or only exception is
+ // returned from RIL.
+ public static final int ERROR_INVALID_SCANID = 10001; // The scanId is invalid. The user is
+ // either trying to stop a scan which
+ // does not exist or started by others.
+ public static final int ERROR_INTERRUPTED = 10002; // Scan was interrupted by another scan
+ // with higher priority.
private final int mScanId;
private final int mSubId;
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index 0a542a7..d2aef20 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -31,6 +31,14 @@
*/
public final class NetworkScanRequest implements Parcelable {
+ // Below size limits for RAN/Band/Channel are for pre-treble modems and will be removed later.
+ /** @hide */
+ public static final int MAX_RADIO_ACCESS_NETWORKS = 8;
+ /** @hide */
+ public static final int MAX_BANDS = 8;
+ /** @hide */
+ public static final int MAX_CHANNELS = 32;
+
/** Performs the scan only once */
public static final int SCAN_TYPE_ONE_SHOT = 0;
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 74327ce..4f78087 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -869,6 +869,20 @@
public static final String EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC =
"android.telephony.event.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC";
+ /**
+ * {@link android.telecom.Connection} event used to indicate that an outgoing call has been
+ * forwarded to another number.
+ * <p>
+ * Sent in response to an IMS supplementary service notification indicating the call has been
+ * forwarded.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_CALL_FORWARDED =
+ "android.telephony.event.EVENT_CALL_FORWARDED";
+
/* Visual voicemail protocols */
/**
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 6367772..11770fb 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -153,6 +153,12 @@
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT";
+ /**
+ * Optional meta-data attribute for a carrier app providing an icon to use to represent the
+ * carrier. If not provided, the app's launcher icon will be used as a fallback.
+ */
+ public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
+
private final Context mContext;
private final IEuiccController mController;
diff --git a/tests/Internal/Android.mk b/tests/Internal/Android.mk
new file mode 100644
index 0000000..f59a624
--- /dev/null
+++ b/tests/Internal/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+
+# Include some source files directly to be able to access package members
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test android-support-test
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PACKAGE_NAME := InternalTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/Internal/AndroidManifest.xml b/tests/Internal/AndroidManifest.xml
new file mode 100644
index 0000000..a2c95fb
--- /dev/null
+++ b/tests/Internal/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.internal.tests">
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.internal.tests"
+ android:label="Internal Tests" />
+</manifest>
diff --git a/tests/Internal/AndroidTest.xml b/tests/Internal/AndroidTest.xml
new file mode 100644
index 0000000..6531c93
--- /dev/null
+++ b/tests/Internal/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<configuration description="Runs tests for internal classes/utilities.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="InternalTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="InternalTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.internal.tests" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/Internal/src/com/android/internal/ml/clustering/KMeansTest.java b/tests/Internal/src/com/android/internal/ml/clustering/KMeansTest.java
new file mode 100644
index 0000000..a64f8a6
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/ml/clustering/KMeansTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.internal.ml.clustering;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.SuppressLint;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KMeansTest {
+
+ // Error tolerance (epsilon)
+ private static final double EPS = 0.01;
+
+ private KMeans mKMeans;
+
+ @Before
+ public void setUp() {
+ // Setup with a random seed to have predictable results
+ mKMeans = new KMeans(new Random(0), 30, 0);
+ }
+
+ @Test
+ public void getCheckDataSanityTest() {
+ try {
+ mKMeans.checkDataSetSanity(new float[][] {
+ {0, 1, 2},
+ {1, 2, 3}
+ });
+ } catch (IllegalArgumentException e) {
+ Assert.fail("Valid data didn't pass sanity check");
+ }
+
+ try {
+ mKMeans.checkDataSetSanity(new float[][] {
+ null,
+ {1, 2, 3}
+ });
+ Assert.fail("Data has null items and passed");
+ } catch (IllegalArgumentException e) {}
+
+ try {
+ mKMeans.checkDataSetSanity(new float[][] {
+ {0, 1, 2, 4},
+ {1, 2, 3}
+ });
+ Assert.fail("Data has invalid shape and passed");
+ } catch (IllegalArgumentException e) {}
+
+ try {
+ mKMeans.checkDataSetSanity(null);
+ Assert.fail("Null data should throw exception");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ @Test
+ public void sqDistanceTest() {
+ float a[] = {4, 10};
+ float b[] = {5, 2};
+ float sqDist = (float) (Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));
+
+ assertEquals("Squared distance not valid", mKMeans.sqDistance(a, b), sqDist, EPS);
+ }
+
+ @Test
+ public void nearestMeanTest() {
+ KMeans.Mean meanA = new KMeans.Mean(0, 1);
+ KMeans.Mean meanB = new KMeans.Mean(1, 1);
+ List<KMeans.Mean> means = Arrays.asList(meanA, meanB);
+
+ KMeans.Mean nearest = mKMeans.nearestMean(new float[] {1, 1}, means);
+
+ assertEquals("Unexpected nearest mean for point {1, 1}", nearest, meanB);
+ }
+
+ @SuppressLint("DefaultLocale")
+ @Test
+ public void scoreTest() {
+ List<KMeans.Mean> closeMeans = Arrays.asList(new KMeans.Mean(0, 0.1f, 0.1f),
+ new KMeans.Mean(0, 0.1f, 0.15f),
+ new KMeans.Mean(0.1f, 0.2f, 0.1f));
+ List<KMeans.Mean> farMeans = Arrays.asList(new KMeans.Mean(0, 0, 0),
+ new KMeans.Mean(0, 0.5f, 0.5f),
+ new KMeans.Mean(1, 0.9f, 0.9f));
+
+ double closeScore = KMeans.score(closeMeans);
+ double farScore = KMeans.score(farMeans);
+ assertTrue(String.format("Score of well distributed means should be greater than "
+ + "close means but got: %f, %f", farScore, closeScore), farScore > closeScore);
+ }
+
+ @Test
+ public void predictTest() {
+ float[] expectedCentroid1 = {1, 1, 1};
+ float[] expectedCentroid2 = {0, 0, 0};
+ float[][] X = new float[][] {
+ {1, 1, 1},
+ {1, 1, 1},
+ {1, 1, 1},
+ {0, 0, 0},
+ {0, 0, 0},
+ {0, 0, 0},
+ };
+
+ final int numClusters = 2;
+
+ // Here we assume that we won't get stuck into a local optima.
+ // It's fine because we're seeding a random, we won't ever have
+ // unstable results but in real life we need multiple initialization
+ // and score comparison
+ List<KMeans.Mean> means = mKMeans.predict(numClusters, X);
+
+ assertEquals("Expected number of clusters is invalid", numClusters, means.size());
+
+ boolean exists1 = false, exists2 = false;
+ for (KMeans.Mean mean : means) {
+ if (Arrays.equals(mean.getCentroid(), expectedCentroid1)) {
+ exists1 = true;
+ } else if (Arrays.equals(mean.getCentroid(), expectedCentroid2)) {
+ exists2 = true;
+ } else {
+ throw new AssertionError("Unexpected mean: " + mean);
+ }
+ }
+ assertTrue("Expected means were not predicted, got: " + means,
+ exists1 && exists2);
+ }
+}
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 4d340d1..1ddaf66 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -62,7 +62,8 @@
@Mock private OffloadHardwareInterface mHardware;
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
- final ArgumentCaptor<ArrayList> mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class);
+ private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
+ ArgumentCaptor.forClass(ArrayList.class);
private MockContentResolver mContentResolver;
@Before public void setUp() throws Exception {
@@ -155,8 +156,7 @@
lp.setInterfaceName(testIfName);
offload.setUpstreamLinkProperties(lp);
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(testIfName), eq(null), eq(null), mStringArrayCaptor.capture());
- assertTrue(mStringArrayCaptor.getValue().isEmpty());
+ eq(testIfName), eq(null), eq(null), eq(null));
inOrder.verifyNoMoreInteractions();
final String ipv4Addr = "192.0.2.5";
@@ -164,16 +164,14 @@
lp.addLinkAddress(new LinkAddress(linkAddr));
offload.setUpstreamLinkProperties(lp);
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(testIfName), eq(ipv4Addr), eq(null), mStringArrayCaptor.capture());
- assertTrue(mStringArrayCaptor.getValue().isEmpty());
+ eq(testIfName), eq(ipv4Addr), eq(null), eq(null));
inOrder.verifyNoMoreInteractions();
final String ipv4Gateway = "192.0.2.1";
lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway)));
offload.setUpstreamLinkProperties(lp);
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
- assertTrue(mStringArrayCaptor.getValue().isEmpty());
+ eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null));
inOrder.verifyNoMoreInteractions();
final String ipv6Gw1 = "fe80::cafe";
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index ce419a5..db5373a 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.tethering;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -40,17 +42,23 @@
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.RouteInfo;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+
+import java.net.Inet4Address;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -69,6 +77,8 @@
@Mock private SharedLog mSharedLog;
private final TestLooper mLooper = new TestLooper();
+ private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
+ ArgumentCaptor.forClass(LinkProperties.class);
private TetherInterfaceStateMachine mTestedSm;
private void initStateMachine(int interfaceType) throws Exception {
@@ -77,7 +87,7 @@
mNMService, mStatsService, mTetherHelper);
mTestedSm.start();
// Starting the state machine always puts us in a consistent state and notifies
- // the test of the world that we've changed from an unknown to available state.
+ // the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
reset(mNMService, mStatsService, mTetherHelper);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
@@ -181,7 +191,8 @@
inOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
}
@@ -281,7 +292,8 @@
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
}
@@ -298,7 +310,8 @@
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@Test
@@ -313,7 +326,8 @@
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@Test
@@ -360,4 +374,28 @@
upstreamIface);
mLooper.dispatchAll();
}
+
+ private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) {
+ // Find the first IPv4 LinkAddress.
+ LinkAddress addr4 = null;
+ for (LinkAddress addr : lp.getLinkAddresses()) {
+ if (!(addr.getAddress() instanceof Inet4Address)) continue;
+ addr4 = addr;
+ break;
+ }
+ assertTrue("missing IPv4 address", addr4 != null);
+
+ // Assert the presence of the associated directly connected route.
+ final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
+ assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'",
+ lp.getRoutes().contains(directlyConnected));
+ }
+
+ private void assertNoAddressesNorRoutes(LinkProperties lp) {
+ assertTrue(lp.getLinkAddresses().isEmpty());
+ assertTrue(lp.getRoutes().isEmpty());
+ // We also check that interface name is non-empty, because we should
+ // never see an empty interface name in any LinkProperties update.
+ assertFalse(TextUtils.isEmpty(lp.getInterfaceName()));
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index fb6066e..fb5c577 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -47,6 +47,7 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.NetworkState;
import android.net.util.SharedLog;
import android.support.test.filters.SmallTest;
@@ -253,31 +254,32 @@
mUNM.start();
// There are no networks, so there is nothing to select.
- assertEquals(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
wifiAgent.fakeConnect();
// WiFi is up, we should prefer it.
- assertEquals(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
wifiAgent.fakeDisconnect();
// There are no networks, so there is nothing to select.
- assertEquals(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
cellAgent.fakeConnect();
- assertEquals(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
preferredTypes.add(TYPE_MOBILE_DUN);
// This is coupled with preferred types in TetheringConfiguration.
mUNM.updateMobileRequiresDun(true);
// DUN is available, but only use regular cell: no upstream selected.
- assertEquals(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
preferredTypes.remove(TYPE_MOBILE_DUN);
// No WiFi, but our preferred flavour of cell is up.
preferredTypes.add(TYPE_MOBILE_HIPRI);
// This is coupled with preferred types in TetheringConfiguration.
mUNM.updateMobileRequiresDun(false);
- assertEquals(TYPE_MOBILE_HIPRI, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertSatisfiesLegacyType(TYPE_MOBILE_HIPRI,
+ mUNM.selectPreferredUpstreamType(preferredTypes));
// Check to see we filed an explicit request.
assertEquals(1, mCM.requested.size());
NetworkRequest netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
@@ -286,25 +288,26 @@
wifiAgent.fakeConnect();
// WiFi is up, and we should prefer it over cell.
- assertEquals(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
assertEquals(0, mCM.requested.size());
preferredTypes.remove(TYPE_MOBILE_HIPRI);
preferredTypes.add(TYPE_MOBILE_DUN);
// This is coupled with preferred types in TetheringConfiguration.
mUNM.updateMobileRequiresDun(true);
- assertEquals(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
dunAgent.fakeConnect();
// WiFi is still preferred.
- assertEquals(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertSatisfiesLegacyType(TYPE_WIFI, mUNM.selectPreferredUpstreamType(preferredTypes));
// WiFi goes down, cell and DUN are still up but only DUN is preferred.
wifiAgent.fakeDisconnect();
- assertEquals(TYPE_MOBILE_DUN, mUNM.selectPreferredUpstreamType(preferredTypes));
+ assertSatisfiesLegacyType(TYPE_MOBILE_DUN,
+ mUNM.selectPreferredUpstreamType(preferredTypes));
// Check to see we filed an explicit request.
assertEquals(1, mCM.requested.size());
netReq = (NetworkRequest) mCM.requested.values().toArray()[0];
@@ -312,6 +315,16 @@
assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
}
+ private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
+ if (legacyType == TYPE_NONE) {
+ assertTrue(ns == null);
+ return;
+ }
+
+ final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType);
+ assertTrue(nc.satisfiedByNetworkCapabilities(ns.networkCapabilities));
+ }
+
private void assertUpstreamTypeRequested(int upstreamType) throws Exception {
assertEquals(1, mCM.requested.size());
assertEquals(1, mCM.legacyTypeMap.size());
diff --git a/tests/testables/src/android/testing/BaseFragmentTest.java b/tests/testables/src/android/testing/BaseFragmentTest.java
index 5cedbdf..5fa065a 100644
--- a/tests/testables/src/android/testing/BaseFragmentTest.java
+++ b/tests/testables/src/android/testing/BaseFragmentTest.java
@@ -50,7 +50,7 @@
private static final int VIEW_ID = 42;
private final Class<? extends Fragment> mCls;
private Handler mHandler;
- private FrameLayout mView;
+ protected FrameLayout mView;
protected FragmentController mFragments;
protected Fragment mFragment;
@@ -61,9 +61,13 @@
mCls = cls;
}
+ protected void createRootView() {
+ mView = new FrameLayout(mContext);
+ }
+
@Before
public void setupFragment() throws Exception {
- mView = new FrameLayout(mContext);
+ createRootView();
mView.setId(VIEW_ID);
assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ec8d91b..613c529 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -676,16 +676,28 @@
@SystemApi
public static final int CHANGE_REASON_CONFIG_CHANGE = 2;
/**
- * An access point scan has completed, and results are available from the supplicant.
- * Call {@link #getScanResults()} to obtain the results. {@link #EXTRA_RESULTS_UPDATED}
- * indicates if the scan was completed successfully.
+ * An access point scan has completed, and results are available.
+ * Call {@link #getScanResults()} to obtain the results.
+ * The broadcast intent may contain an extra field with the key {@link #EXTRA_RESULTS_UPDATED}
+ * and a {@code boolean} value indicating if the scan was successful.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
/**
- * Lookup key for a {@code boolean} representing the result of previous {@link #startScan}
- * operation, reported with {@link #SCAN_RESULTS_AVAILABLE_ACTION}.
+ * Lookup key for a {@code boolean} extra in intent {@link #SCAN_RESULTS_AVAILABLE_ACTION}
+ * representing if the scan was successful or not.
+ * Scans may fail for multiple reasons, these may include:
+ * <ol>
+ * <li>A non-privileged app requested too many scans in a certain period of time.
+ * This may lead to additional scan request rejections via "scan throttling".
+ * See
+ * <a href="https://developer.android.com/preview/features/background-location-limits.html">
+ * here</a> for details.
+ * </li>
+ * <li>The device is idle and scanning is disabled.</li>
+ * <li>Wifi hardware reported a scan failure.</li>
+ * </ol>
* @return true scan was successful, results are updated
* @return false scan was not successful, results haven't been updated since previous scan
*/