Merge "Fixed bug with wrong task bounds when device is rotated 180°"
diff --git a/api/current.txt b/api/current.txt
index 358d4d5..05c40d1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38229,7 +38229,8 @@
ctor public SuggestionSpan(android.os.Parcel);
method public int describeContents();
method public int getFlags();
- method public java.lang.String getLocale();
+ method public deprecated java.lang.String getLocale();
+ method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
method public void setFlags(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 9b66945..fb47833 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -40588,7 +40588,8 @@
ctor public SuggestionSpan(android.os.Parcel);
method public int describeContents();
method public int getFlags();
- method public java.lang.String getLocale();
+ method public deprecated java.lang.String getLocale();
+ method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
method public void setFlags(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 41a77b3..3fb9b5f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -38245,7 +38245,8 @@
ctor public SuggestionSpan(android.os.Parcel);
method public int describeContents();
method public int getFlags();
- method public java.lang.String getLocale();
+ method public deprecated java.lang.String getLocale();
+ method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
method public void setFlags(int);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 7f9a5d3..e721de9 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -17,6 +17,7 @@
package android.animation;
import android.annotation.CallSuper;
+import android.annotation.IntDef;
import android.os.Looper;
import android.os.Trace;
import android.util.AndroidRuntimeException;
@@ -25,6 +26,8 @@
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
@@ -234,6 +237,11 @@
* Public constants
*/
+ /** @hide */
+ @IntDef({RESTART, REVERSE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RepeatMode {}
+
/**
* When the animation reaches the end and <code>repeatCount</code> is INFINITE
* or a positive value, the animation restarts from the beginning.
@@ -807,7 +815,7 @@
*
* @param value {@link #RESTART} or {@link #REVERSE}
*/
- public void setRepeatMode(int value) {
+ public void setRepeatMode(@RepeatMode int value) {
mRepeatMode = value;
}
@@ -816,6 +824,7 @@
*
* @return either one of {@link #REVERSE} or {@link #RESTART}
*/
+ @RepeatMode
public int getRepeatMode() {
return mRepeatMode;
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 624131e..94b4e7f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2842,6 +2842,14 @@
reply.writeNoException();
return true;
}
+ case IS_APP_FOREGROUND_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int userHandle = data.readInt();
+ final boolean isForeground = isAppForeground(userHandle);
+ reply.writeNoException();
+ reply.writeInt(isForeground ? 1 : 0);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -6639,5 +6647,18 @@
reply.recycle();
}
+ @Override
+ public boolean isAppForeground(int uid) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(uid);
+ mRemote.transact(IS_APP_FOREGROUND_TRANSACTION, data, reply, 0);
+ final boolean isForeground = reply.readInt() == 1 ? true : false;
+ data.recycle();
+ reply.recycle();
+ return isForeground;
+ };
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 1ae91a6..10885f2 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -589,6 +589,8 @@
public void setVrMode(IBinder token, boolean enabled) throws RemoteException;
+ public boolean isAppForeground(int uid) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -960,4 +962,5 @@
int SET_VR_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 359;
int GET_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 360;
int CLEAR_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 361;
+ int IS_APP_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 362;
}
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 13e27e2..bd10267 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -28,6 +28,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.view.IWindowManager;
import android.view.InputEvent;
import android.view.SurfaceControl;
@@ -167,9 +168,10 @@
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
+ int callingUserId = UserHandle.getCallingUserId();
final long identity = Binder.clearCallingIdentity();
try {
- IBinder token = mAccessibilityManager.getWindowToken(windowId);
+ IBinder token = mAccessibilityManager.getWindowToken(windowId, callingUserId);
if (token == null) {
return false;
}
@@ -186,9 +188,10 @@
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
+ int callingUserId = UserHandle.getCallingUserId();
final long identity = Binder.clearCallingIdentity();
try {
- IBinder token = mAccessibilityManager.getWindowToken(windowId);
+ IBinder token = mAccessibilityManager.getWindowToken(windowId, callingUserId);
if (token == null) {
return null;
}
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 4416415..56b4249 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -16,12 +16,16 @@
package android.app;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This class provides access to the system uimode services. These services
* allow applications to control UI modes of the device.
@@ -92,18 +96,26 @@
* when the user exits desk mode.
*/
public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE";
-
- /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+
+ /** @hide */
+ @IntDef({MODE_NIGHT_AUTO, MODE_NIGHT_NO, MODE_NIGHT_YES})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NightMode {}
+
+ /**
+ * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
* automatically switch night mode on and off based on the time.
*/
public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_UNDEFINED >> 4;
- /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+ /**
+ * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
* never run in night mode.
*/
public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
- /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+ /**
+ * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
* always run in night mode.
*/
public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
@@ -195,20 +207,28 @@
}
/**
- * Sets the night mode. Changes to the night mode are only effective when
- * the car or desk mode is enabled on a device.
- *
- * <p>The mode can be one of:
+ * Sets the night mode.
+ * <p>
+ * The mode can be one of:
* <ul>
- * <li><em>{@link #MODE_NIGHT_NO}<em> - sets the device into notnight
- * mode.</li>
- * <li><em>{@link #MODE_NIGHT_YES}</em> - sets the device into night mode.
- * </li>
- * <li><em>{@link #MODE_NIGHT_AUTO}</em> - automatic night/notnight switching
- * depending on the location and certain other sensors.</li>
+ * <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into
+ * {@code notnight} mode</li>
+ * <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
+ * {@code night} mode</li>
+ * <li><em>{@link #MODE_NIGHT_AUTO}</em> automatically switches between
+ * {@code night} and {@code notnight} based on the device's current
+ * location and certain other sensors</li>
* </ul>
+ * <p>
+ * <strong>Note:</strong> On API 22 and below, changes to the night mode
+ * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
+ * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
+ * device. Starting in API 23, changes to night mode are always effective.
+ *
+ * @param mode the night mode to set
+ * @see #getNightMode()
*/
- public void setNightMode(int mode) {
+ public void setNightMode(@NightMode int mode) {
if (mService != null) {
try {
mService.setNightMode(mode);
@@ -219,11 +239,20 @@
}
/**
- * @return the currently configured night mode. May be one of
- * {@link #MODE_NIGHT_NO}, {@link #MODE_NIGHT_YES},
- * {@link #MODE_NIGHT_AUTO}, or -1 on error.
+ * Returns the currently configured night mode.
+ * <p>
+ * May be one of:
+ * <ul>
+ * <li>{@link #MODE_NIGHT_NO}</li>
+ * <li>{@link #MODE_NIGHT_YES}</li>
+ * <li>{@link #MODE_NIGHT_AUTO}</li>
+ * <li>{@code -1} on error</li>
+ * </ul>
+ *
+ * @return the current night mode, or {@code -1} on error
+ * @see #setNightMode(int)
*/
- public int getNightMode() {
+ public @NightMode int getNightMode() {
if (mService != null) {
try {
return mService.getNightMode();
@@ -250,9 +279,13 @@
}
/**
- * @return If Night mode is locked or not. When Night mode is locked, changing Night mode
- * is only allowed to privileged system components and normal application's call
- * to change Night mode using {@link #setNightMode(int)} will silently fail.
+ * Returns whether night mode is locked or not.
+ * <p>
+ * When night mode is locked, only privileged system components may change
+ * night mode and calls from non-privileged applications to change night
+ * mode will fail silently.
+ *
+ * @return {@code true} if night mode is locked or {@code false} otherwise
*/
public boolean isNightModeLocked() {
if (mService != null) {
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 6b449f9..1b00db2 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -16,6 +16,8 @@
package android.text.style;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -84,7 +86,15 @@
private int mFlags;
private final String[] mSuggestions;
- private final String mLocaleString;
+ /**
+ * Kept for compatibility for apps that rely on invalid locale strings e.g.
+ * {@code new Locale(" an ", " i n v a l i d ", "data")}, which cannot be handled by
+ * {@link #mLanguageTag}.
+ */
+ @NonNull
+ private final String mLocaleStringForCompatibility;
+ @NonNull
+ private final String mLanguageTag;
private final String mNotificationTargetClassName;
private final String mNotificationTargetPackageName;
private final int mHashCode;
@@ -130,14 +140,18 @@
final int N = Math.min(SUGGESTIONS_MAX_SIZE, suggestions.length);
mSuggestions = Arrays.copyOf(suggestions, N);
mFlags = flags;
+ final Locale sourceLocale;
if (locale != null) {
- mLocaleString = locale.toString();
+ sourceLocale = locale;
} else if (context != null) {
- mLocaleString = context.getResources().getConfiguration().locale.toString();
+ // TODO: Consider to context.getResources().getResolvedLocale() instead.
+ sourceLocale = context.getResources().getConfiguration().locale;
} else {
Log.e("SuggestionSpan", "No locale or context specified in SuggestionSpan constructor");
- mLocaleString = "";
+ sourceLocale = null;
}
+ mLocaleStringForCompatibility = sourceLocale == null ? "" : sourceLocale.toString();
+ mLanguageTag = sourceLocale == null ? "" : sourceLocale.toLanguageTag();
if (context != null) {
mNotificationTargetPackageName = context.getPackageName();
@@ -150,7 +164,8 @@
} else {
mNotificationTargetClassName = "";
}
- mHashCode = hashCodeInternal(mSuggestions, mLocaleString, mNotificationTargetClassName);
+ mHashCode = hashCodeInternal(mSuggestions, mLanguageTag, mLocaleStringForCompatibility,
+ mNotificationTargetClassName);
initStyle(context);
}
@@ -194,7 +209,8 @@
public SuggestionSpan(Parcel src) {
mSuggestions = src.readStringArray();
mFlags = src.readInt();
- mLocaleString = src.readString();
+ mLocaleStringForCompatibility = src.readString();
+ mLanguageTag = src.readString();
mNotificationTargetClassName = src.readString();
mNotificationTargetPackageName = src.readString();
mHashCode = src.readInt();
@@ -214,10 +230,29 @@
}
/**
- * @return the locale of the suggestions
+ * @deprecated use {@link #getLocaleObject()} instead.
+ * @return the locale of the suggestions. An empty string is returned if no locale is specified.
*/
+ @NonNull
+ @Deprecated
public String getLocale() {
- return mLocaleString;
+ return mLocaleStringForCompatibility;
+ }
+
+ /**
+ * Returns a well-formed BCP 47 language tag representation of the suggestions, as a
+ * {@link Locale} object.
+ *
+ * <p><b>Caveat</b>: The returned object is guaranteed to be a a well-formed BCP 47 language tag
+ * representation. For example, this method can return an empty locale rather than returning a
+ * malformed data when this object is initialized with an malformed {@link Locale} object, e.g.
+ * {@code new Locale(" a ", " b c d ", " "}.</p>
+ *
+ * @return the locale of the suggestions. {@code null} is returned if no locale is specified.
+ */
+ @Nullable
+ public Locale getLocaleObject() {
+ return mLanguageTag.isEmpty() ? null : Locale.forLanguageTag(mLanguageTag);
}
/**
@@ -255,7 +290,8 @@
public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeStringArray(mSuggestions);
dest.writeInt(mFlags);
- dest.writeString(mLocaleString);
+ dest.writeString(mLocaleStringForCompatibility);
+ dest.writeString(mLanguageTag);
dest.writeString(mNotificationTargetClassName);
dest.writeString(mNotificationTargetPackageName);
dest.writeInt(mHashCode);
@@ -290,10 +326,10 @@
return mHashCode;
}
- private static int hashCodeInternal(String[] suggestions, String locale,
- String notificationTargetClassName) {
+ private static int hashCodeInternal(String[] suggestions, @NonNull String languageTag,
+ @NonNull String localeStringForCompatibility, String notificationTargetClassName) {
return Arrays.hashCode(new Object[] {Long.valueOf(SystemClock.uptimeMillis()), suggestions,
- locale, notificationTargetClassName});
+ languageTag, localeStringForCompatibility, notificationTargetClassName});
}
public static final Parcelable.Creator<SuggestionSpan> CREATOR =
diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java
index b10f6a0..817541c 100644
--- a/core/java/android/transition/SidePropagation.java
+++ b/core/java/android/transition/SidePropagation.java
@@ -16,6 +16,7 @@
package android.transition;
import android.graphics.Rect;
+import android.transition.Slide.GravityFlag;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -45,7 +46,7 @@
* {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT},
* {@link Gravity#BOTTOM}, {@link Gravity#START}, or {@link Gravity#END}.
*/
- public void setSide(int side) {
+ public void setSide(@GravityFlag int side) {
mSide = side;
}
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index 9063b43..9af65e4 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -17,6 +17,7 @@
import android.animation.Animator;
import android.animation.TimeInterpolator;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
@@ -27,6 +28,9 @@
import android.view.animation.DecelerateInterpolator;
import com.android.internal.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This transition tracks changes to the visibility of target views in the
* start and end scenes and moves views in or out from one of the edges of the
@@ -42,7 +46,12 @@
private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
private CalculateSlide mSlideCalculator = sCalculateBottom;
- private int mSlideEdge = Gravity.BOTTOM;
+ private @GravityFlag int mSlideEdge = Gravity.BOTTOM;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Gravity.LEFT, Gravity.TOP, Gravity.RIGHT, Gravity.BOTTOM, Gravity.START, Gravity.END})
+ public @interface GravityFlag {}
private interface CalculateSlide {
@@ -176,7 +185,7 @@
* {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
* @attr ref android.R.styleable#Slide_slideEdge
*/
- public void setSlideEdge(int slideEdge) {
+ public void setSlideEdge(@GravityFlag int slideEdge) {
switch (slideEdge) {
case Gravity.LEFT:
mSlideCalculator = sCalculateLeft;
@@ -214,6 +223,7 @@
* {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
* @attr ref android.R.styleable#Slide_slideEdge
*/
+ @GravityFlag
public int getSlideEdge() {
return mSlideEdge;
}
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index e711812..eb95a02 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -16,17 +16,21 @@
package android.transition;
-import com.android.internal.R;
-
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.Animator.AnimatorPauseListener;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This transition tracks changes to the visibility of target views in the
* start and end scenes. Visibility is determined not just by the
@@ -46,6 +50,11 @@
private static final String PROPNAME_PARENT = "android:visibility:parent";
private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag=true, value={MODE_IN, MODE_OUT})
+ @interface VisibilityMode {}
+
/**
* Mode used in {@link #setMode(int)} to make the transition
* operate on targets that are appearing. Maybe be combined with
@@ -99,7 +108,7 @@
* {@link #MODE_IN} and {@link #MODE_OUT}.
* @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
*/
- public void setMode(int mode) {
+ public void setMode(@VisibilityMode int mode) {
if ((mode & ~(MODE_IN | MODE_OUT)) != 0) {
throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed");
}
@@ -113,6 +122,7 @@
* {@link #MODE_IN} and {@link #MODE_OUT}.
* @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
*/
+ @VisibilityMode
public int getMode() {
return mMode;
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index b6570cc..9e79057 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -58,5 +58,5 @@
void temporaryEnableAccessibilityStateUntilKeyguardRemoved(in ComponentName service,
boolean touchExplorationEnabled);
- IBinder getWindowToken(int windowId);
+ IBinder getWindowToken(int windowId, int userId);
}
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 2aaa356..cde7604 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -16,8 +16,11 @@
package android.widget;
+import android.annotation.AttrRes;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.StyleRes;
import android.annotation.Widget;
import android.content.Context;
@@ -37,9 +40,13 @@
import java.util.TimeZone;
/**
- * This class is a calendar widget for displaying and selecting dates. The range
- * of dates supported by this calendar is configurable. A user can select a date
- * by taping on it and can scroll and fling the calendar to a desired date.
+ * This class is a calendar widget for displaying and selecting dates. The
+ * range of dates supported by this calendar is configurable.
+ * <p>
+ * The exact appearance and interaction model of this widget may vary between
+ * OS versions and themes (e.g. Holo versus Material), but in general a user
+ * can select a date by tapping on it and can scroll or fling the calendar to a
+ * desired date.
*
* @attr ref android.R.styleable#CalendarView_showWeekNumber
* @attr ref android.R.styleable#CalendarView_firstDayOfWeek
@@ -77,22 +84,24 @@
* @param month The month that was set [0-11].
* @param dayOfMonth The day of the month that was set.
*/
- public void onSelectedDayChange(CalendarView view, int year, int month, int dayOfMonth);
+ void onSelectedDayChange(@NonNull CalendarView view, int year, int month, int dayOfMonth);
}
- public CalendarView(Context context) {
+ public CalendarView(@NonNull Context context) {
this(context, null);
}
- public CalendarView(Context context, AttributeSet attrs) {
+ public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, R.attr.calendarViewStyle);
}
- public CalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public CalendarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(
@@ -322,7 +331,7 @@
*
* @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
*/
- public void setWeekDayTextAppearance(int resourceId) {
+ public void setWeekDayTextAppearance(@StyleRes int resourceId) {
mDelegate.setWeekDayTextAppearance(resourceId);
}
@@ -333,7 +342,7 @@
*
* @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
*/
- public int getWeekDayTextAppearance() {
+ public @StyleRes int getWeekDayTextAppearance() {
return mDelegate.getWeekDayTextAppearance();
}
@@ -344,7 +353,7 @@
*
* @attr ref android.R.styleable#CalendarView_dateTextAppearance
*/
- public void setDateTextAppearance(int resourceId) {
+ public void setDateTextAppearance(@StyleRes int resourceId) {
mDelegate.setDateTextAppearance(resourceId);
}
@@ -355,7 +364,7 @@
*
* @attr ref android.R.styleable#CalendarView_dateTextAppearance
*/
- public int getDateTextAppearance() {
+ public @StyleRes int getDateTextAppearance() {
return mDelegate.getDateTextAppearance();
}
@@ -552,36 +561,29 @@
int getShownWeekCount();
void setSelectedWeekBackgroundColor(@ColorInt int color);
- @ColorInt
- int getSelectedWeekBackgroundColor();
+ @ColorInt int getSelectedWeekBackgroundColor();
void setFocusedMonthDateColor(@ColorInt int color);
- @ColorInt
- int getFocusedMonthDateColor();
+ @ColorInt int getFocusedMonthDateColor();
void setUnfocusedMonthDateColor(@ColorInt int color);
- @ColorInt
- int getUnfocusedMonthDateColor();
+ @ColorInt int getUnfocusedMonthDateColor();
void setWeekNumberColor(@ColorInt int color);
- @ColorInt
- int getWeekNumberColor();
+ @ColorInt int getWeekNumberColor();
void setWeekSeparatorLineColor(@ColorInt int color);
- @ColorInt
- int getWeekSeparatorLineColor();
+ @ColorInt int getWeekSeparatorLineColor();
void setSelectedDateVerticalBar(@DrawableRes int resourceId);
void setSelectedDateVerticalBar(Drawable drawable);
Drawable getSelectedDateVerticalBar();
void setWeekDayTextAppearance(@StyleRes int resourceId);
- @StyleRes
- int getWeekDayTextAppearance();
+ @StyleRes int getWeekDayTextAppearance();
void setDateTextAppearance(@StyleRes int resourceId);
- @StyleRes
- int getDateTextAppearance();
+ @StyleRes int getDateTextAppearance();
void setMinDate(long minDate);
long getMinDate();
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 8e711b0..06daf61 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -1946,6 +1946,9 @@
public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
mActionMenuPresenterCallback = pcb;
mMenuBuilderCallback = mcb;
+ if (mMenuView != null) {
+ mMenuView.setMenuCallbacks(pcb, mcb);
+ }
}
/**
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 78424e3..c0dfe77 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -204,11 +204,13 @@
/**
* Sets the X position around which the drawable is rotated.
+ * <p>
+ * If the X pivot is relative (as specified by
+ * {@link #setPivotXRelative(boolean)}), then the position represents a
+ * fraction of the drawable width. Otherwise, the position represents an
+ * absolute value in pixels.
*
- * @param pivotX X position around which to rotate. If the X pivot is
- * relative, the position represents a fraction of the drawable
- * width. Otherwise, the position represents an absolute value in
- * pixels.
+ * @param pivotX X position around which to rotate
* @see #setPivotXRelative(boolean)
* @attr ref android.R.styleable#RotateDrawable_pivotX
*/
@@ -254,11 +256,13 @@
/**
* Sets the Y position around which the drawable is rotated.
+ * <p>
+ * If the Y pivot is relative (as specified by
+ * {@link #setPivotYRelative(boolean)}), then the position represents a
+ * fraction of the drawable height. Otherwise, the position represents an
+ * absolute value in pixels.
*
- * @param pivotY Y position around which to rotate. If the Y pivot is
- * relative, the position represents a fraction of the drawable
- * height. Otherwise, the position represents an absolute value
- * in pixels.
+ * @param pivotY Y position around which to rotate
* @see #getPivotY()
* @attr ref android.R.styleable#RotateDrawable_pivotY
*/
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 43282c9..72c7e4e 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -450,6 +450,11 @@
} else {
fprintf(file, "\nNo caches instance.\n");
}
+#if HWUI_NEW_OPS
+ fprintf(file, "\nPipeline=FrameBuilder\n");
+#else
+ fprintf(file, "\nPipeline=OpenGLRenderer\n");
+#endif
fflush(file);
return nullptr;
}
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index 05b4a72..dcc4946 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -18,6 +18,7 @@
#include "AnimationContext.h"
#include "DisplayListCanvas.h"
#include "IContextFactory.h"
+#include "RecordingCanvas.h"
#include "RenderNode.h"
#include "SkTypes.h"
#include "gui/BufferQueue.h"
@@ -88,9 +89,11 @@
mProxy->setup(mSize.width(), mSize.height(), 800.0f,
255 * 0.075f, 255 * 0.15f);
mProxy->setLightCenter(lightVector);
- mCanvas.reset(new
- android::uirenderer::DisplayListCanvas(mSize.width(),
- mSize.height()));
+#if HWUI_NEW_OPS
+ mCanvas.reset(new android::uirenderer::RecordingCanvas(mSize.width(), mSize.height()));
+#else
+ mCanvas.reset(new android::uirenderer::DisplayListCanvas(mSize.width(), mSize.height()));
+#endif
}
SkCanvas* prepareToDraw() {
@@ -168,7 +171,11 @@
std::unique_ptr<android::uirenderer::RenderNode> mRootNode;
std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy;
+#if HWUI_NEW_OPS
+ std::unique_ptr<android::uirenderer::RecordingCanvas> mCanvas;
+#else
std::unique_ptr<android::uirenderer::DisplayListCanvas> mCanvas;
+#endif
android::sp<android::IGraphicBufferProducer> mProducer;
android::sp<android::IGraphicBufferConsumer> mConsumer;
android::sp<android::CpuConsumer> mCpuConsumer;
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 96c616b..dfe024a 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -17,6 +17,7 @@
package android.media;
import android.content.ContentProviderClient;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -37,6 +38,7 @@
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.sax.Element;
import android.sax.ElementListener;
import android.sax.RootElement;
@@ -328,8 +330,6 @@
// used when scanning the image database so we know whether we have to prune
// old thumbnail files
private int mOriginalCount;
- /** Whether the database had any entries in it before the scan started */
- private boolean mWasEmptyPriorToScan = false;
/** Whether the scanner has set a default sound for the ringer ringtone. */
private boolean mDefaultRingtoneSet;
/** Whether the scanner has set a default sound for the notification ringtone. */
@@ -562,12 +562,29 @@
FileEntry entry = beginFile(path, mimeType, lastModified,
fileSize, isDirectory, noMedia);
+ if (entry == null) {
+ return null;
+ }
+
// if this file was just inserted via mtp, set the rowid to zero
// (even though it already exists in the database), to trigger
// the correct code path for updating its entry
if (mMtpObjectHandle != 0) {
entry.mRowId = 0;
}
+
+ if (entry.mPath != null &&
+ ((!mDefaultNotificationSet &&
+ doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
+ || (!mDefaultRingtoneSet &&
+ doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
+ || (!mDefaultAlarmSet &&
+ doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
+ Log.w(TAG, "forcing rescan of " + entry.mPath +
+ "since ringtone setting didn't finish");
+ scanAlways = true;
+ }
+
// rescan for metadata if file was modified since last scan
if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
if (noMedia) {
@@ -947,6 +964,26 @@
}
Uri result = null;
boolean needToSetSettings = false;
+ // Setting a flag in order not to use bulk insert for the file related with
+ // notifications, ringtones, and alarms, because the rowId of the inserted file is
+ // needed.
+ if (notifications && !mDefaultNotificationSet) {
+ if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
+ needToSetSettings = true;
+ }
+ } else if (ringtones && !mDefaultRingtoneSet) {
+ if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
+ needToSetSettings = true;
+ }
+ } else if (alarms && !mDefaultAlarmSet) {
+ if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
+ needToSetSettings = true;
+ }
+ }
+
if (rowId == 0) {
if (mMtpObjectHandle != 0) {
values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
@@ -958,28 +995,6 @@
}
values.put(Files.FileColumns.FORMAT, format);
}
- // Setting a flag in order not to use bulk insert for the file related with
- // notifications, ringtones, and alarms, because the rowId of the inserted file is
- // needed.
- if (mWasEmptyPriorToScan) {
- if (notifications && !mDefaultNotificationSet) {
- if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
- needToSetSettings = true;
- }
- } else if (ringtones && !mDefaultRingtoneSet) {
- if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
- needToSetSettings = true;
- }
- } else if (alarms && !mDefaultAlarmSet) {
- if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
- needToSetSettings = true;
- }
- }
- }
-
// New file, insert it.
// Directories need to be inserted before the files they contain, so they
// get priority when bulk inserting.
@@ -1049,14 +1064,18 @@
private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
- String existingSettingValue = Settings.System.getString(mContext.getContentResolver(),
- settingName);
+ if(wasSettingAlreadySet(settingName)) {
+ return;
+ }
+ ContentResolver cr = mContext.getContentResolver();
+ String existingSettingValue = Settings.System.getString(cr, settingName);
if (TextUtils.isEmpty(existingSettingValue)) {
// Set the setting to the given URI
- Settings.System.putString(mContext.getContentResolver(), settingName,
+ Settings.System.putString(cr, settingName,
ContentUris.withAppendedId(uri, rowId).toString());
}
+ Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
}
private int getFileTypeFromDrm(String path) {
@@ -1083,6 +1102,20 @@
}; // end of anonymous MediaScannerClient instance
+ private String settingSetIndicatorName(String base) {
+ return base + "_set";
+ }
+
+ private boolean wasSettingAlreadySet(String name) {
+ ContentResolver cr = mContext.getContentResolver();
+ String indicatorName = settingSetIndicatorName(name);
+ try {
+ return Settings.System.getInt(cr, indicatorName) != 0;
+ } catch (SettingNotFoundException e) {
+ return false;
+ }
+ }
+
private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
Cursor c = null;
String where = null;
@@ -1100,6 +1133,10 @@
selectionArgs = new String[] { "" };
}
+ mDefaultRingtoneSet = wasSettingAlreadySet(Settings.System.RINGTONE);
+ mDefaultNotificationSet = wasSettingAlreadySet(Settings.System.NOTIFICATION_SOUND);
+ mDefaultAlarmSet = wasSettingAlreadySet(Settings.System.ALARM_ALERT);
+
// Tell the provider to not delete the file.
// If the file is truly gone the delete is unnecessary, and we want to avoid
// accidentally deleting files that are really there (this may happen if the
@@ -1117,7 +1154,6 @@
// with CursorWindow positioning.
long lastId = Long.MIN_VALUE;
Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
- mWasEmptyPriorToScan = true;
while (true) {
selectionArgs[0] = "" + lastId;
@@ -1136,7 +1172,6 @@
if (num == 0) {
break;
}
- mWasEmptyPriorToScan = false;
while (c.moveToNext()) {
long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
@@ -1284,7 +1319,7 @@
}
}
- private void postscan(String[] directories) throws RemoteException {
+ private void postscan(final String[] directories) throws RemoteException {
// handle playlists last, after we know what media files are on the storage.
if (mProcessPlaylists) {
diff --git a/packages/DocumentsUI/res/drawable/ic_root_home.xml b/packages/DocumentsUI/res/drawable/ic_root_home.xml
index 0a258ac..696ee05 100644
--- a/packages/DocumentsUI/res/drawable/ic_root_home.xml
+++ b/packages/DocumentsUI/res/drawable/ic_root_home.xml
@@ -1,15 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+<!--
+Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<path
- android:fillColor="#000000"
- android:pathData="M20 6h-8l-2-2H4c-1.1 0-1.99 .9 -1.99 2L2 18c0 1.1 .9 2 2 2h16c1.1 0 2-.9
-2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4
-8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z" />
- <path
- android:pathData="M0 0h24v24H0z" />
+ android:fillColor="#FF000000"
+ android:pathData="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index f6caaa9..2706e25 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -190,18 +190,27 @@
* Send the intent to trigger the {@link android.settings.ShowAdminSupportDetailsDialog}.
*/
public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
- Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
+ final Intent intent = getShowAdminSupportDetailsIntent(context, admin);
int adminUserId = UserHandle.myUserId();
+ if (admin.userId != UserHandle.USER_NULL) {
+ adminUserId = admin.userId;
+ }
+ context.startActivityAsUser(intent, new UserHandle(adminUserId));
+ }
+
+ public static Intent getShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
+ final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
if (admin != null) {
if (admin.component != null) {
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component);
}
+ int adminUserId = UserHandle.myUserId();
if (admin.userId != UserHandle.USER_NULL) {
adminUserId = admin.userId;
}
intent.putExtra(Intent.EXTRA_USER_ID, adminUserId);
}
- context.startActivityAsUser(intent, new UserHandle(adminUserId));
+ return intent;
}
public static void setTextViewPadlock(Context context,
@@ -224,6 +233,34 @@
this.userId = userId;
}
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) return true;
+ if (!(object instanceof EnforcedAdmin)) return false;
+ EnforcedAdmin other = (EnforcedAdmin) object;
+ if (userId != other.userId) {
+ return false;
+ }
+ if ((component == null && other == null) ||
+ (component != null && component.equals(other))) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "EnforcedAdmin{component=" + component + ",userId=" + userId + "}";
+ }
+
+ public void copyTo(EnforcedAdmin other) {
+ if (other == null) {
+ other = new EnforcedAdmin();
+ }
+ other.component = component;
+ other.userId = userId;
+ }
+
public EnforcedAdmin() {}
}
}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 569017a..13a46d0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -79,6 +79,15 @@
mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
}
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled && isDisabledByAdmin()) {
+ mHelper.setDisabledByAdmin(null);
+ return;
+ }
+ super.setEnabled(enabled);
+ }
+
public void setDisabledByAdmin(EnforcedAdmin admin) {
if (mHelper.setDisabledByAdmin(admin)) {
notifyChanged();
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index f041504..06aba96 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -45,7 +45,7 @@
private EnforcedAdmin mEnforcedAdmin;
private String mAttrUserRestriction = null;
- RestrictedPreferenceHelper(Context context, Preference preference,
+ public RestrictedPreferenceHelper(Context context, Preference preference,
AttributeSet attrs) {
mContext = context;
mPreference = preference;
@@ -54,21 +54,21 @@
mRestrictedPadlockPadding = mContext.getResources().getDimensionPixelSize(
R.dimen.restricted_lock_icon_padding);
- mAttrUserRestriction = attrs.getAttributeValue(
- R.styleable.RestrictedPreference_userRestriction);
- final TypedArray attributes = context.obtainStyledAttributes(attrs,
- R.styleable.RestrictedPreference);
- final TypedValue userRestriction =
- attributes.peekValue(R.styleable.RestrictedPreference_userRestriction);
- CharSequence data = null;
- if (userRestriction != null && userRestriction.type == TypedValue.TYPE_STRING) {
- if (userRestriction.resourceId != 0) {
- data = context.getText(userRestriction.resourceId);
- } else {
- data = userRestriction.string;
+ if (attrs != null) {
+ final TypedArray attributes = context.obtainStyledAttributes(attrs,
+ R.styleable.RestrictedPreference);
+ final TypedValue userRestriction =
+ attributes.peekValue(R.styleable.RestrictedPreference_userRestriction);
+ CharSequence data = null;
+ if (userRestriction != null && userRestriction.type == TypedValue.TYPE_STRING) {
+ if (userRestriction.resourceId != 0) {
+ data = context.getText(userRestriction.resourceId);
+ } else {
+ data = userRestriction.string;
+ }
}
+ mAttrUserRestriction = data == null ? null : data.toString();
}
- mAttrUserRestriction = data == null ? null : data.toString();
}
/**
@@ -100,7 +100,7 @@
/**
* Disable / enable if we have been passed the restriction in the xml.
*/
- protected void onAttachedToHierarchy() {
+ public void onAttachedToHierarchy() {
if (mAttrUserRestriction != null) {
checkRestrictionAndSetDisabled(mAttrUserRestriction, UserHandle.myUserId());
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 308477b0..84e2bff 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -79,6 +79,15 @@
mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
}
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled && isDisabledByAdmin()) {
+ mHelper.setDisabledByAdmin(null);
+ return;
+ }
+ super.setEnabled(enabled);
+ }
+
public void setDisabledByAdmin(EnforcedAdmin admin) {
if (mHelper.setDisabledByAdmin(admin)) {
notifyChanged();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index f7b291b..2ee4b12 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -234,10 +234,13 @@
}
public boolean matches(WifiConfiguration config) {
- if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint())
+ if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
return config.FQDN.equals(mConfig.providerFriendlyName);
- else
- return ssid.equals(removeDoubleQuotes(config.SSID)) && security == getSecurity(config);
+ } else {
+ return ssid.equals(removeDoubleQuotes(config.SSID))
+ && security == getSecurity(config)
+ && (mConfig == null || mConfig.shared == config.shared);
+ }
}
public WifiConfiguration getConfig() {
diff --git a/packages/SystemUI/res/color/qs_tile_text.xml b/packages/SystemUI/res/color/qs_tile_text.xml
new file mode 100644
index 0000000..90e0bce
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_tile_text.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2015 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
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
+ <item android:color="#B3FFFFFF" /> <!-- 70% white -->
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_user_detail_name.xml b/packages/SystemUI/res/color/qs_user_detail_name.xml
index 57f7e65..35c7a4f 100644
--- a/packages/SystemUI/res/color/qs_user_detail_name.xml
+++ b/packages/SystemUI/res/color/qs_user_detail_name.xml
@@ -18,5 +18,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:color="@color/current_user_border_color" />
+ <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
<item android:color="#66ffffff" /> <!-- 40% white -->
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
new file mode 100644
index 0000000..603ebbf
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView android:id="@+id/tile_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/qs_tile_text"
+ android:gravity="center_horizontal"
+ android:minLines="2"
+ android:padding="0dp"
+ android:fontFamily="sans-serif-condensed"
+ android:textStyle="normal"
+ android:textSize="@dimen/qs_tile_text_size"
+ android:clickable="false" />
+ <ImageView android:id="@+id/restricted_padlock"
+ android:layout_width="@dimen/qs_tile_text_size"
+ android:layout_height="@dimen/qs_tile_text_size"
+ android:src="@drawable/ic_settings_lock_outline"
+ android:layout_marginLeft="@dimen/restricted_padlock_pading"
+ android:baselineAlignBottom="true"
+ android:scaleType="centerInside"
+ android:visibility="gone" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index af22f03..a22c360 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -40,12 +40,25 @@
systemui:framePadding="6dp"
systemui:activeFrameColor="@color/current_user_border_color"/>
- <TextView
- android:id="@+id/user_name"
+ <LinearLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="@dimen/qs_detail_item_secondary_text_size"
- android:textColor="@color/qs_user_detail_name"
- android:gravity="center_horizontal" />
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/qs_detail_item_secondary_text_size"
+ android:textColor="@color/qs_user_detail_name"
+ android:gravity="center_horizontal" />
+ <ImageView
+ android:id="@+id/restricted_padlock"
+ android:layout_width="@dimen/qs_detail_item_secondary_text_size"
+ android:layout_height="@dimen/qs_detail_item_secondary_text_size"
+ android:src="@drawable/ic_settings_lock_outline"
+ android:layout_marginLeft="@dimen/restricted_padlock_pading"
+ android:baselineAlignBottom="true"
+ android:scaleType="centerInside"
+ android:visibility="gone" />
+ </LinearLayout>
</com.android.systemui.qs.tiles.UserDetailItemView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 61c71dd..a80a5de 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -36,7 +36,6 @@
<color name="system_warning_color">#fff4511e</color><!-- deep orange 600 -->
<color name="qs_text">#FFFFFFFF</color>
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
- <color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
<color name="qs_subhead">#99FFFFFF</color><!-- 60% white -->
<color name="qs_detail_empty">#24B0BEC5</color><!-- 14% blue grey 200 -->
<color name="qs_detail_button">#FFB0BEC5</color><!-- 100% blue grey 200 -->
@@ -48,6 +47,7 @@
<color name="data_usage_graph_warning">#FFFFFFFF</color>
<color name="status_bar_clock_color">#FFFFFFFF</color>
<color name="qs_user_detail_icon_muted">#FFFFFFFF</color> <!-- not so muted after all -->
+ <color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
<!-- Tint color for the content on the notification overflow card. -->
<color name="keyguard_overflow_content_color">#ff686868</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 097c352..1a9b874 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -167,6 +167,8 @@
<dimen name="segmented_button_spacing">0dp</dimen>
<dimen name="borderless_button_radius">2dp</dimen>
+ <dimen name="restricted_padlock_pading">4dp</dimen>
+
<!-- How far the expanded QS panel peeks from the header in collapsed state. -->
<dimen name="qs_peek_height">0dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
index 01eb5f9..7651ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
@@ -81,7 +81,11 @@
}
}
}
-
+ if (state.disabledByPolicy) {
+ iv.setColorFilter(getContext().getColor(R.color.qs_tile_disabled_color));
+ } else {
+ iv.clearColorFilter();
+ }
}
protected int getIconMeasureMode() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 2d9d105..de7c02d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -23,10 +23,13 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
+
+import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.qs.QSTile.State;
import com.android.systemui.qs.external.TileServices;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -47,6 +50,8 @@
import java.util.Collection;
import java.util.Objects;
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
/**
* Base quick-settings tile, extend this to create a new tile.
*
@@ -256,6 +261,18 @@
mCallbacks.clear();
}
+ protected void checkIfRestrictionEnforced(State state, String userRestriction) {
+ EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+ userRestriction, UserHandle.myUserId());
+ if (admin != null) {
+ state.disabledByPolicy = true;
+ state.enforcedAdmin = admin;
+ } else {
+ state.disabledByPolicy = false;
+ state.enforcedAdmin = null;
+ }
+ }
+
protected final class H extends Handler {
private static final int ADD_CALLBACK = 1;
private static final int CLICK = 2;
@@ -282,8 +299,14 @@
handleAddCallback((QSTile.Callback)msg.obj);
} else if (msg.what == CLICK) {
name = "handleClick";
- mAnnounceNextStateChange = true;
- handleClick();
+ if (mState.disabledByPolicy) {
+ Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
+ mContext, mState.enforcedAdmin);
+ mHost.startActivityDismissingKeyguard(intent);
+ } else {
+ mAnnounceNextStateChange = true;
+ handleClick();
+ }
} else if (msg.what == SECONDARY_CLICK) {
name = "handleSecondaryClick";
handleSecondaryClick();
@@ -437,6 +460,8 @@
public CharSequence contentDescription;
public CharSequence dualLabelContentDescription;
public boolean autoMirrorDrawable = true;
+ public boolean disabledByPolicy;
+ public EnforcedAdmin enforcedAdmin;
public boolean copyTo(State other) {
if (other == null) throw new IllegalArgumentException();
@@ -446,12 +471,16 @@
|| !Objects.equals(other.contentDescription, contentDescription)
|| !Objects.equals(other.autoMirrorDrawable, autoMirrorDrawable)
|| !Objects.equals(other.dualLabelContentDescription,
- dualLabelContentDescription);
+ dualLabelContentDescription)
+ || !Objects.equals(other.disabledByPolicy, disabledByPolicy)
+ || !Objects.equals(other.enforcedAdmin, enforcedAdmin);
other.icon = icon;
other.label = label;
other.contentDescription = contentDescription;
other.dualLabelContentDescription = dualLabelContentDescription;
other.autoMirrorDrawable = autoMirrorDrawable;
+ other.disabledByPolicy = disabledByPolicy;
+ enforcedAdmin.copyTo(other.enforcedAdmin);
return changed;
}
@@ -467,6 +496,8 @@
sb.append(",contentDescription=").append(contentDescription);
sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
sb.append(",autoMirrorDrawable=").append(autoMirrorDrawable);
+ sb.append(",disabledByPolicy=").append(disabledByPolicy);
+ sb.append(",enforcedAdmin=").append(enforcedAdmin);
return sb.append(']');
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 41ac4d9..664ca39 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -19,32 +19,33 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Typeface;
import android.util.MathUtils;
-import android.util.TypedValue;
import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.View;
+import android.widget.ImageView;
import android.widget.TextView;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
/** View that represents a standard quick settings tile. **/
public class QSTileView extends QSTileBaseView {
- private static final Typeface CONDENSED = Typeface.create("sans-serif-condensed",
- Typeface.NORMAL);
-
protected final Context mContext;
+ private QSIconView mIconView;
private final int mTileSpacingPx;
private int mTilePaddingTopPx;
private TextView mLabel;
+ private ImageView mPadLock;
public QSTileView(Context context, QSIconView icon) {
super(context, icon);
mContext = context;
+ mIconView = icon;
final Resources res = context.getResources();
mTileSpacingPx = res.getDimensionPixelSize(R.dimen.qs_tile_spacing);
+
setClipChildren(false);
setClickable(true);
@@ -76,16 +77,10 @@
private void createLabel() {
final Resources res = mContext.getResources();
- mLabel = new TextView(mContext);
- mLabel.setTextColor(mContext.getColor(R.color.qs_tile_text));
- mLabel.setGravity(Gravity.CENTER_HORIZONTAL);
- mLabel.setMinLines(2);
- mLabel.setPadding(0, 0, 0, 0);
- mLabel.setTypeface(CONDENSED);
- mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
- mLabel.setClickable(false);
- addView(mLabel);
+ View view = LayoutInflater.from(mContext).inflate(R.layout.qs_tile_label, null);
+ mLabel = (TextView) view.findViewById(R.id.tile_label);
+ mPadLock = (ImageView) view.findViewById(R.id.restricted_padlock);
+ addView(view);
}
public void init(OnClickListener clickPrimary, OnLongClickListener longClick) {
@@ -96,5 +91,7 @@
protected void handleStateChanged(QSTile.State state) {
super.handleStateChanged(state);
mLabel.setText(state.label);
+ mLabel.setEnabled(!state.disabledByPolicy);
+ mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 4d9b266..39eda6b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -22,6 +22,7 @@
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.view.LayoutInflater;
@@ -99,14 +100,6 @@
@Override
public void handleClick() {
- if (mController.isVolumeRestricted()) {
- // Collapse the panels, so the user can see the toast.
- mHost.collapsePanels();
- SysUIToast.makeText(mContext, mContext.getString(
- com.android.internal.R.string.error_message_change_not_allowed),
- Toast.LENGTH_LONG).show();
- return;
- }
MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
if (mState.value) {
mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
@@ -123,6 +116,8 @@
final boolean newValue = zen != Global.ZEN_MODE_OFF;
final boolean valueChanged = state.value != newValue;
state.value = newValue;
+ state.disabledByPolicy = mController.isVolumeRestricted();
+ checkIfRestrictionEnforced(state, UserManager.DISALLOW_ADJUST_VOLUME);
switch (zen) {
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 21c5c96..167c611 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -43,6 +43,7 @@
private TextView mName;
private Typeface mRegularTypeface;
private Typeface mActivatedTypeface;
+ private View mRestrictedPadlock;
public UserDetailItemView(Context context) {
this(context, null);
@@ -59,6 +60,7 @@
public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.UserDetailItemView, defStyleAttr, defStyleRes);
final int N = a.getIndexCount();
@@ -95,6 +97,12 @@
mAvatar.setDrawable(picture);
}
+ public void setDisabledByAdmin(boolean disabled) {
+ mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE);
+ mName.setEnabled(!disabled);
+ mAvatar.setDisabled(disabled);
+ }
+
@Override
protected void onFinishInflate() {
mAvatar = (UserAvatarView) findViewById(R.id.user_picture);
@@ -106,6 +114,7 @@
mActivatedTypeface = mName.getTypeface();
}
updateTypeface();
+ mRestrictedPadlock = findViewById(R.id.restricted_padlock);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index d4f54b6..b44ef0b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -16,17 +16,18 @@
package com.android.systemui.qs.tiles;
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
-import com.android.systemui.qs.PseudoGridView;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-
import android.content.Context;
+import android.content.Intent;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.R;
+import com.android.systemui.qs.PseudoGridView;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
/**
* Quick settings detail view for user switching.
*/
@@ -55,11 +56,13 @@
public static class Adapter extends UserSwitcherController.BaseUserAdapter
implements OnClickListener {
- private Context mContext;
+ private final Context mContext;
+ private final UserSwitcherController mController;
public Adapter(Context context, UserSwitcherController controller) {
super(controller);
mContext = context;
+ mController = controller;
}
@Override
@@ -77,6 +80,7 @@
v.bind(name, item.picture);
}
v.setActivated(item.isCurrent);
+ v.setDisabledByAdmin(item.isDisabledByAdmin);
v.setTag(item);
return v;
}
@@ -85,8 +89,14 @@
public void onClick(View view) {
UserSwitcherController.UserRecord tag =
(UserSwitcherController.UserRecord) view.getTag();
- MetricsLogger.action(mContext, MetricsLogger.QS_SWITCH_USER);
- switchTo(tag);
+ if (tag.isDisabledByAdmin) {
+ final Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
+ mContext, tag.enforcedAdmin);
+ mController.startActivity(intent);
+ } else {
+ MetricsLogger.action(mContext, MetricsLogger.QS_SWITCH_USER);
+ switchTo(tag);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 50e88d3..5be52ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -866,7 +866,7 @@
mKeyguardMonitor = new KeyguardMonitor(mContext);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
- mHandler);
+ mHandler, this);
if (mUserSwitcherController.useFullscreenUserSwitcher()) {
mFullscreenUserSwitcher = new FullscreenUserSwitcher(this, mUserSwitcherController,
(ViewStub) mStatusBarWindow.findViewById(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
index 101a5f3..4f33d82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -23,6 +23,9 @@
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
@@ -41,6 +44,7 @@
private float mFramePadding;
private Bitmap mBitmap;
private Drawable mDrawable;
+ private boolean mIsDisabled;
private final Paint mFramePaint = new Paint();
private final Paint mBitmapPaint = new Paint();
@@ -239,4 +243,28 @@
mDrawable.setState(getDrawableState());
}
}
+
+ public void setDisabled(boolean disabled) {
+ if (mIsDisabled == disabled) {
+ return;
+ }
+ mIsDisabled = disabled;
+ int disabledColor = getContext().getColor(R.color.qs_tile_disabled_color);
+ PorterDuffColorFilter filter = new PorterDuffColorFilter(disabledColor,
+ PorterDuff.Mode.SRC_ATOP);
+ if (mBitmap != null) {
+ if (disabled) {
+ mBitmapPaint.setColorFilter(filter);
+ } else {
+ mBitmapPaint.setColorFilter(null);
+ }
+ } else if (mDrawable != null) {
+ if (disabled) {
+ mDrawable.setColorFilter(filter);
+ } else {
+ mDrawable.setColorFilter(null);
+ }
+ }
+ invalidate();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index f3a3554..ffec615 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -22,7 +22,9 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -33,6 +35,7 @@
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -48,11 +51,13 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.UserIcons;
+import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.BitmapHelper;
import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.statusbar.phone.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.io.FileDescriptor;
@@ -61,6 +66,8 @@
import java.util.ArrayList;
import java.util.List;
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
/**
* Keeps a list of all users on the device for user switching.
*/
@@ -88,6 +95,7 @@
= new GuestResumeSessionReceiver();
private final KeyguardMonitor mKeyguardMonitor;
private final Handler mHandler;
+ private final ActivityStarter mActivityStarter;
private ArrayList<UserRecord> mUsers = new ArrayList<>();
private Dialog mExitGuestDialog;
@@ -99,11 +107,12 @@
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
- Handler handler) {
+ Handler handler, ActivityStarter activityStarter) {
mContext = context;
mGuestResumeSessionReceiver.register(context);
mKeyguardMonitor = keyguardMonitor;
mHandler = handler;
+ mActivityStarter = activityStarter;
mUserManager = UserManager.get(context);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
@@ -206,25 +215,23 @@
}
}
- boolean systemCanCreateUsers = !mUserManager.hasUserRestriction(
- UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
boolean currentUserCanCreateUsers = currentUserInfo != null
&& (currentUserInfo.isAdmin()
- || currentUserInfo.id == UserHandle.USER_SYSTEM)
- && systemCanCreateUsers;
- boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked;
- boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
+ || currentUserInfo.id == UserHandle.USER_SYSTEM);
+ boolean canCreateGuest = (currentUserCanCreateUsers || addUsersWhenLocked)
&& guestRecord == null;
- boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
+ boolean canCreateUser = (currentUserCanCreateUsers || addUsersWhenLocked)
&& mUserManager.canAddMoreUsers();
boolean createIsRestricted = !addUsersWhenLocked;
if (!mSimpleUserSwitcher) {
if (guestRecord == null) {
if (canCreateGuest) {
- records.add(new UserRecord(null /* info */, null /* picture */,
+ guestRecord = new UserRecord(null /* info */, null /* picture */,
true /* isGuest */, false /* isCurrent */,
- false /* isAddUser */, createIsRestricted));
+ false /* isAddUser */, createIsRestricted);
+ checkIfAddUserDisallowed(guestRecord);
+ records.add(guestRecord);
}
} else {
int index = guestRecord.isCurrent ? 0 : records.size();
@@ -233,9 +240,11 @@
}
if (!mSimpleUserSwitcher && canCreateUser) {
- records.add(new UserRecord(null /* info */, null /* picture */,
+ UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
- createIsRestricted));
+ createIsRestricted);
+ checkIfAddUserDisallowed(addUserRecord);
+ records.add(addUserRecord);
}
return records;
@@ -594,6 +603,22 @@
}
}
+ private void checkIfAddUserDisallowed(UserRecord record) {
+ EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+ UserManager.DISALLOW_ADD_USER, UserHandle.myUserId());
+ if (admin != null) {
+ record.isDisabledByAdmin = true;
+ record.enforcedAdmin = admin;
+ } else {
+ record.isDisabledByAdmin = false;
+ record.enforcedAdmin = null;
+ }
+ }
+
+ public void startActivity(Intent intent) {
+ mActivityStarter.startActivity(intent, true);
+ }
+
public static final class UserRecord {
public final UserInfo info;
public final Bitmap picture;
@@ -602,6 +627,8 @@
public final boolean isAddUser;
/** If true, the record is only visible to the owner and only when unlocked. */
public final boolean isRestricted;
+ public boolean isDisabledByAdmin;
+ public EnforcedAdmin enforcedAdmin;
public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent,
boolean isAddUser, boolean isRestricted) {
@@ -634,6 +661,10 @@
if (isCurrent) sb.append(" <isCurrent>");
if (picture != null) sb.append(" <hasPicture>");
if (isRestricted) sb.append(" <isRestricted>");
+ if (isDisabledByAdmin) {
+ sb.append(" <isDisabledByAdmin>");
+ sb.append(" enforcedAdmin=" + enforcedAdmin);
+ }
sb.append(')');
return sb.toString();
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 39d5952..28aeef7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -715,7 +715,7 @@
}
@Override
- public IBinder getWindowToken(int windowId) {
+ public IBinder getWindowToken(int windowId, int userId) {
mSecurityPolicy.enforceCallingPermission(
Manifest.permission.RETRIEVE_WINDOW_TOKEN,
GET_WINDOW_TOKEN);
@@ -724,8 +724,7 @@
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
- .resolveCallingUserIdEnforcingPermissionsLocked(
- UserHandle.getCallingUserId());
+ .resolveCallingUserIdEnforcingPermissionsLocked(userId);
if (resolvedUserId != mCurrentUserId) {
return null;
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 2264c69..aa15373 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -239,6 +239,11 @@
// How long between attempts to perform a full-data backup of any given app
static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day
+ // If an app is busy when we want to do a full-data backup, how long to defer the retry.
+ // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
+ static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
+ static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
+
Context mContext;
private PackageManager mPackageManager;
IPackageManager mPackageManagerBinder;
@@ -4496,35 +4501,72 @@
return false;
}
- // At this point we know that we have work to do, just not right now. Any
- // exit without actually running backups will also require that we
+ // At this point we know that we have work to do, but possibly not right now.
+ // Any exit without actually running backups will also require that we
// reschedule the job.
boolean runBackup = true;
+ boolean headBusy;
- if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "Preconditions not met; not running full backup");
- }
- runBackup = false;
- // Typically this means we haven't run a key/value backup yet. Back off
- // full-backup operations by the key/value job's run interval so that
- // next time we run, we are likely to be able to make progress.
- latency = KeyValueBackupJob.BATCH_INTERVAL;
- }
+ do {
+ headBusy = false;
- if (runBackup) {
- entry = mFullBackupQueue.get(0);
- long timeSinceRun = now - entry.lastBackup;
- runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
- if (!runBackup) {
- // It's too early to back up the next thing in the queue, so bow out
+ if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
if (MORE_DEBUG) {
- Slog.i(TAG, "Device ready but too early to back up next app");
+ Slog.i(TAG, "Preconditions not met; not running full backup");
}
- // Wait until the next app in the queue falls due for a full data backup
- latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+ runBackup = false;
+ // Typically this means we haven't run a key/value backup yet. Back off
+ // full-backup operations by the key/value job's run interval so that
+ // next time we run, we are likely to be able to make progress.
+ latency = KeyValueBackupJob.BATCH_INTERVAL;
}
- }
+
+ if (runBackup) {
+ entry = mFullBackupQueue.get(0);
+ long timeSinceRun = now - entry.lastBackup;
+ runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
+ if (!runBackup) {
+ // It's too early to back up the next thing in the queue, so bow out
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Device ready but too early to back up next app");
+ }
+ // Wait until the next app in the queue falls due for a full data backup
+ latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+ break; // we know we aren't doing work yet, so bail.
+ }
+
+ try {
+ PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+ headBusy = mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
+
+ if (headBusy) {
+ final long nextEligible = System.currentTimeMillis()
+ + BUSY_BACKOFF_MIN_MILLIS
+ + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
+ if (DEBUG_SCHEDULING) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ Slog.i(TAG, "Full backup time but " + entry.packageName
+ + " is busy; deferring to "
+ + sdf.format(new Date(nextEligible)));
+ }
+ // This relocates the app's entry from the head of the queue to
+ // its order-appropriate position further down, so upon looping
+ // a new candidate will be considered at the head.
+ enqueueFullBackup(entry.packageName,
+ nextEligible - MIN_FULL_BACKUP_INTERVAL);
+ }
+
+ } catch (NameNotFoundException nnf) {
+ // So, we think we want to back this up, but it turns out the package
+ // in question is no longer installed. We want to drop it from the
+ // queue entirely and move on, but if there's nothing else in the queue
+ // we should bail entirely. headBusy cannot have been set to true yet.
+ runBackup = (mFullBackupQueue.size() > 1);
+ } catch (RemoteException e) {
+ // Cannot happen; the Activity Manager is in the same process
+ }
+ }
+ } while (headBusy);
if (!runBackup) {
if (DEBUG_SCHEDULING) {
@@ -4539,7 +4581,7 @@
return false;
}
- // Okay, the top thing is runnable now. Pop it off and get going.
+ // Okay, the top thing is ready for backup now. Do it.
mFullBackupQueue.remove(0);
CountDownLatch latch = new CountDownLatch(1);
String[] pkg = new String[] {entry.packageName};
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 093a33d..ca38b71 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18,7 +18,6 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
-
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.AssistUtils;
@@ -240,6 +239,7 @@
import java.util.concurrent.atomic.AtomicLong;
import dalvik.system.VMRuntime;
+
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -7256,6 +7256,17 @@
}
@Override
+ public boolean isAppForeground(int uid) throws RemoteException {
+ synchronized (this) {
+ UidRecord uidRec = mActiveUids.get(uid);
+ if (uidRec == null || uidRec.idle) {
+ return false;
+ }
+ return uidRec.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ }
+ }
+
+ @Override
public boolean inMultiWindowMode(IBinder token) {
final long origId = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 78618ce..2eb9095 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2682,6 +2682,31 @@
}
continue;
}
+ String packageName = getPackageName(op.target);
+ ApplicationInfo ai = null;
+ if (packageName != null) {
+ try {
+ ai = mContext.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS);
+ } catch (NameNotFoundException e) {
+ operationIterator.remove();
+ mSyncStorageEngine.deleteFromPending(op.pendingOperation);
+ continue;
+ }
+ }
+ // If app is considered idle, then skip for now and backoff
+ if (ai != null
+ && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) {
+ increaseBackoffSetting(op);
+ op.appIdle = true;
+ if (isLoggable) {
+ Log.v(TAG, "Sync backing off idle app " + packageName);
+ }
+ continue;
+ } else {
+ op.appIdle = false;
+ }
if (!isOperationValidLocked(op)) {
operationIterator.remove();
mSyncStorageEngine.deleteFromPending(op.pendingOperation);
@@ -2700,28 +2725,6 @@
}
continue;
}
- String packageName = getPackageName(op.target);
- ApplicationInfo ai = null;
- if (packageName != null) {
- try {
- ai = mContext.getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_UNINSTALLED_PACKAGES
- | PackageManager.GET_DISABLED_COMPONENTS);
- } catch (NameNotFoundException e) {
- }
- }
- // If app is considered idle, then skip for now and backoff
- if (ai != null
- && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) {
- increaseBackoffSetting(op);
- op.appIdle = true;
- if (isLoggable) {
- Log.v(TAG, "Sync backing off idle app " + packageName);
- }
- continue;
- } else {
- op.appIdle = false;
- }
// Add this sync to be run.
operations.add(op);
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 972dcb2..0c06ae8 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -24,6 +24,7 @@
import android.net.StaticIpConfiguration;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.text.TextUtils;
import java.util.Arrays;
@@ -327,6 +328,13 @@
/**
* @hide
+ * This network configuration is visible to and usable by other users on the
+ * same device.
+ */
+ public boolean shared;
+
+ /**
+ * @hide
*/
private IpConfiguration mIpConfiguration;
@@ -1103,6 +1111,7 @@
mIpConfiguration = new IpConfiguration();
lastUpdateUid = -1;
creatorUid = -1;
+ shared = true;
}
/**
@@ -1488,6 +1497,9 @@
key = mCachedConfigKey;
} else if (providerFriendlyName != null) {
key = FQDN + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+ if (!shared) {
+ key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
+ }
} else {
if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
@@ -1499,6 +1511,9 @@
} else {
key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
}
+ if (!shared) {
+ key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
+ }
mCachedConfigKey = key;
}
return key;
@@ -1511,27 +1526,6 @@
return configKey(false);
}
- /** @hide
- * return the config key string based on a scan result
- */
- static public String configKey(ScanResult result) {
- String key = "\"" + result.SSID + "\"";
-
- if (result.capabilities.contains("WEP")) {
- key = key + "-WEP";
- }
-
- if (result.capabilities.contains("PSK")) {
- key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_PSK];
- }
-
- if (result.capabilities.contains("EAP")) {
- key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_EAP];
- }
-
- return key;
- }
-
/** @hide */
public IpConfiguration getIpConfiguration() {
return mIpConfiguration;
@@ -1593,6 +1587,11 @@
return 0;
}
+ /** @hide */
+ public boolean isVisibleToUser(int userId) {
+ return shared || (UserHandle.getUserId(creatorUid) == userId);
+ }
+
/** copy constructor {@hide} */
public WifiConfiguration(WifiConfiguration source) {
if (source != null) {
@@ -1676,6 +1675,7 @@
noInternetAccessExpected = source.noInternetAccessExpected;
creationTime = source.creationTime;
updateTime = source.updateTime;
+ shared = source.shared;
}
}
@@ -1747,6 +1747,7 @@
dest.writeInt(userApproved);
dest.writeInt(numNoInternetAccessReports);
dest.writeInt(noInternetAccessExpected ? 1 : 0);
+ dest.writeInt(shared ? 1 : 0);
}
/** Implement the Parcelable interface {@hide} */
@@ -1814,6 +1815,7 @@
config.userApproved = in.readInt();
config.numNoInternetAccessReports = in.readInt();
config.noInternetAccessExpected = in.readInt() != 0;
+ config.shared = in.readInt() != 0;
return config;
}